Replicating C unions with unsafe, arrays, and some constant arithmetic

190 views
Skip to first unread message

aind...@gmail.com

unread,
Aug 24, 2023, 10:58:51 PM8/24/23
to golang-nuts
I found this trick recently to replicate a C union in Go with unsafe.Sizeof, arrays, and some constant arithmetic. This lets you stack-allocate data that needs to be the max size of multiple other data types. So given a type A and B that you want to union, you can say

const ASize = int64(unsafe.Sizeof(*new(A)))
const BSize = int64(unsafe.Sizeof(*new(B)))
const diff = ASize - BSize
const max = ASize - ((diff>>(bits.UintSize-1))&1)*diff

var storage [max]byte
*(*A)(unsafe.Pointer(&storage))
*(*B)(unsafe.Pointer(&storage))


This process can be repeated for any number of types. For example, if you wanted to represent a tagged union defined like

type A =
    | B
    | C int
    | D (a: int, b: int)


you could lower it to something like

const size = int64(unsafe.Sizeof(*new(B)))
const size0 = int64(unsafe.Sizeof(*new(C)))
const diff = size - size0
const max = size - diff*((diff>>(bits.UintSize-1))&1)
const size1 = int64(unsafe.Sizeof(*new(D)))
const diff0 = size1 - max
const max0 = size1 - diff0*((diff0>>(bits.UintSize-1))&1)

type A struct {
        tag     uint8
        storage [max0]byte
}
type B struct{}
type C int
type D struct {
        a int
        b int
}

Unfortunately, the resulting code is pretty inefficient compared to interfaces or struct embedding. It might be that the use of unsafe is hindering compiler optimizations, or maybe it's an alignment issue.

Ian Lance Taylor

unread,
Aug 25, 2023, 12:43:09 AM8/25/23
to aind...@gmail.com, golang-nuts
Note that is not permitted by the rules of the unsafe package, and that in particular it will break the garbage collector if any of the types stored in the union contain pointer fields.

Ian

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/5d150c6a-3ecb-4cb8-91b3-c048940294aen%40googlegroups.com.

Kurtis Rader

unread,
Aug 25, 2023, 1:11:50 AM8/25/23
to aind...@gmail.com, golang-nuts
I googled "emulate c unions in go" to find examples of people wanting to do the same thing. It appears that most (all?) of them are better solved using Go interfaces. While I only spent a few minutes looking at the results of my search I didn't see any examples where something like your trick was necessary to deal with a FFI (foreign function interface). I've been programming for more than four decades, and on UNIX for almost as long. I am familiar with UNIX APIs that use C unions. If you are trying to deal with a FFI it would help if you showed that as a concrete example. If, on the other hand, you are simply trying to emulate a C union in Go then it would help if you explained why you feel that is necessary, or preferable, rather than using the idiomatic Go interface mechanism.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/5d150c6a-3ecb-4cb8-91b3-c048940294aen%40googlegroups.com.


--
Kurtis Rader
Caretaker of the exceptional canines Junior and Hank
Reply all
Reply to author
Forward
0 new messages