Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to write C functions that modify structs of types defined in Go code?

Tags:

c

struct

go

cgo

This is a follow-up to this question. I made an assumption there that might not be true, which is why I am explicitly asking about it. Because I forgot to ask if this is actually possible, I have already filed issue #8114 about this.


With cgo, it is possible to have Go code operate on C types, like this:

package foo

//#include <sys/stat.h>
import "C"

func fileSizeFromStat(stat *C.struct_stat) int64 {
    return int64(stat.st_size)
}

Is the reverse possible? I.e. writing C functions that operate on go types? The concrete point of this is outlined in the question linked above; I want to marshall C structures that cannot be accessed from Go code, either because they use unions or bitfields or because their alignment makes them incompatible with Go code.

like image 997
fuz Avatar asked Aug 29 '14 08:08

fuz


2 Answers

As far as I know, no, you can't.

But you could use something ugly like https://github.com/OneOfOne/go-nfqueue/blob/master/nfqueue.go#L130 where you export a Go function that takes a lot of pointers and construct your Go struct in go.

like image 120
OneOfOne Avatar answered Nov 11 '22 10:11

OneOfOne


I wrote the following dirty hack to work around the apparent inability to access Go structures from C. While this hack is not guaranteed to work, it works for those cases were Go and C agree on how to lay out structures, which happens to be the case for all those cases I am interested in.

For each Go structure I want to access

type JewelTarget struct {
    SensRes [2]byte
    Id      [4]byte
    Baud    int
}

I create a corresponding C structure which has fields of the same width and hopefully the same layout:

typedef ptrdiff_t GoInt;

struct JewelTarget {
    uint8_t SensRes[2];
    uint8_t Id[4];
    GoInt   Baud;
};

Then I write C functions that use these C structs:

extern void
marshallJewelTarget(nfc_target *nt, const struct JewelTarget *jt)
{
    nfc_jewel_info *ji = &nt->nti.nji;

    memcpy(ji->btSensRes, jt->SensRes, sizeof(jt->SensRes));
    memcpy(ji->btId, jt->Id, sizeof(jt->Id));

    nt->nm.nbr = jt->Baud;
    nt->nm.nmt = NMT_JEWEL;
}

and call them as if the arguments had the corresponding Go types:

func (d *JewelTarget) Marshall() uintptr {
    nt := mallocTarget()
    jt := (*C.struct_JewelTarget)(unsafe.Pointer(d))

    C.marshallJewelTarget(nt, jt)

    return uintptr(unsafe.Pointer(nt))
}

All examples taken from my nfc bindings.

like image 1
fuz Avatar answered Nov 11 '22 11:11

fuz