在Go代码中,是否可以编写修改已定义类型的结构体的C函数?

huangapple go评论92阅读模式
英文:

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

问题

这是对这个问题的后续。我在那里做了一个可能不正确的假设,这就是为什么我明确询问这个问题。因为我忘记问这是否可能,所以我已经提交了问题 #8114


使用cgo,可以让Go代码操作C类型,就像这样:

package foo

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

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

是否可以反过来呢?也就是编写操作Go类型的C函数?这个具体问题在上面链接的问题中有详细说明;我想要将无法从Go代码访问的C结构体进行编组,无论是因为它们使用了联合体或位域,还是因为它们的对齐方式使它们与Go代码不兼容。

英文:

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.

答案1

得分: 1

据我所知,不,你不能这样做。

但是你可以使用一些不太美观的方法,比如使用https://github.com/OneOfOne/go-nfqueue/blob/master/nfqueue.go#L130,其中你可以导出一个接受许多指针的Go函数,并在Go中构建你的结构体。

英文:

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.

答案2

得分: 1

我写了以下的"脏"解决方案来解决从C语言无法访问Go语言结构体的问题。虽然这个解决方案不能保证适用于所有情况,但对于那些Go和C语言在结构体布局上达成一致的情况,它是有效的,而这恰好是我感兴趣的情况。

对于每个我想要访问的Go语言结构体:

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

我创建一个相应的C语言结构体,它具有相同的宽度和相同的布局:

typedef ptrdiff_t GoInt;

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

然后,我编写使用这些C语言结构体的C函数:

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;
}

并且像参数具有相应的Go类型一样调用它们:

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

    C.marshallJewelTarget(nt, jt)

    return uintptr(unsafe.Pointer(nt))
}

所有的示例都来自于我的nfc绑定库

英文:

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.

huangapple
  • 本文由 发表于 2014年8月29日 16:30:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/25564490.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定