英文:
Good or recommended way to translate C struct to Go struct
问题
你好!根据你的描述,你正在使用cgo从Go语言开发库绑定。你想知道如何更好地实现TranslateCCons2GoCons函数。
根据你提供的代码,我注意到你已经定义了一些C函数来获取C结构体的字段值。然后,你在TranslateCCons2GoCons函数中使用这些C函数来创建Go结构体。
这种方法是可行的,但也可以考虑使用Go语言的unsafe包来直接访问C结构体的字段。这样可以避免在C和Go之间频繁地进行内存拷贝。
下面是一个使用unsafe包的示例实现:
import "unsafe"
func TranslateCCons2GoCons(c *C.cons_t) *Cons {
typePtr := (*C.size_t)(unsafe.Pointer(&c._type))
carPtr := (*C.cons_t)(unsafe.Pointer(&c.car))
cdrPtr := (*C.cons_t)(unsafe.Pointer(&c.cdr))
return &Cons{
_type: int(*typePtr),
car: TranslateCCons2GoCons(carPtr),
cdr: TranslateCCons2GoCons(cdrPtr),
}
}
这种方法使用了unsafe.Pointer来将C结构体的字段转换为Go指针,并通过指针访问字段的值。请注意,这种方法需要谨慎使用,因为它涉及到指针操作和类型转换,可能会导致不安全的行为。
希望这个回答对你有帮助!如果你有任何其他问题,请随时问我。
英文:
i'm using cgo for developing library binding from Go.
Let me consider the C struct and Go Struct as below.
struct cons_t {
size_t type;
cons_t *car;
cons_t *cdr;
};
cons_t* parse(const char *str);
and this is go's struct
type Cons struct {
type int;
car *Cons;
cdr *Cons;
}
For implementing Go function as below, what is better way to implement TranslateCCons2GoCons?
func Parse (str string) *Cons {
str_ptr := C.CString(string);
cons_ptr := C.parse(str_ptr);
retCons := TranslateCCons2GoCons(cons_ptr);
return retCons;
}
My first answer is as below.
/*#cgo
int getType(cons_t *cons) {
return cons->type;
}
cons_t *getCar(cons_t *cons) {
return cons->car;
}
cons_t *getCdr(cons_t *cons) {
return cons->cdr;
}
*/
func TranslateCCons2GoCons (c *C.cons_t) Cons {
type := C.getType(c);
car := C.getCar(c);
cdr := C.getCdr(c);
// drop null termination for simplicity
return Cons{type, TranslateCCons2GoCons(car), TranslateCCons2GoCons(cdr)};
}
are there any better way?
答案1
得分: 3
你可以在Go语言中使用C结构体(尽管如果struct
包含union
会变得更加复杂)。最简单的方法是:
type Cons struct {
c C.cons_t
}
在Go中,任何C函数都只是一个透传:
func Parse(s string) Cons {
str := C.CString(s)
// 警告:如果这个字符串在C代码中存储了,请不要释放它
defer C.free(unsafe.Pointer(str))
return Cons{c: C.parse(str)}
}
这样做会增加一些开销,因为你需要在访问元素时进行类型转换。所以之前的var c Cons{}; c.Type
现在变成了:
func (c Cons) Type() int {
return int(c.c.type)
}
可以使用中间的折中方案,在C类型旁边存储字段以便于访问:
type Cons struct {
type int
c C.cons_t
}
func (c *Cons) SetType(t int) {
c.type = t
c.c.type = C.size_t(t)
}
func (c Cons) Type() int {
return c.type
}
唯一的问题是,如果你经常调用C函数,这样做会在设置Go端字段时增加维护开销:
func (c *Cons) SomeFuncThatAltersType() {
C.someFuncThatAltersType(&c.c)
c.Type = int(c.c.type) // 现在我们必须记得这样做
}
英文:
You can use C structs in Go (though if the struct
holds a union
it gets a bit more complex). The simplest way would just be
type Cons struct {
c C.cons_t
}
Any function in C is now just a passthrough in Go
func Parse(s string) Cons {
str := C.CString(s)
// Warning: don't free this if this is stored in the C code
defer C.free(unsafe.Pointer(str))
return Cons{c: C.parse(str)}
}
This has its own overhead, since you have to do a type conversion on element access. So what was before var c Cons{}; c.Type
is now
func (c Cons) Type() int {
return int(c.c.type)
}
An intermediate compromise can be used where you store fields alongside the C type for easy access
type Cons struct {
type int
c C.cons_t
}
func (c *Cons) SetType(t int) {
c.type = t
c.c.type = C.size_t(t)
}
func (c Cons) Type() int {
return c.type
}
The only real problem with this is that if you're calling C functions a lot, this can introduce maintenance overhead in setting the Go-side fields:
func (c *Cons) SomeFuncThatAltersType() {
C.someFuncThatAltersType(&c.c)
c.Type = int(c.c.type) // now we have to remember to do this
}
答案2
得分: 1
我建议不要使用访问器函数。你应该直接访问C结构体的字段,这样可以避免Go调用C函数的开销(这是非常重要的)。所以你可以使用类似以下的代码:
func TranslateCCons2GoCons(c *C.cons_t) *Cons {
if c == nil {
return nil
}
return &Cons{
type: int(c._type),
car: TranslateCCons2GoCons(c.car),
cdr: TranslateCCons2GoCons(c.cdr),
}
}
另外,如果你使用C.CString
分配了一个C字符串,你需要释放它。所以你的Parse
函数应该类似下面这样:
func Parse(str string) *Cons {
str_ptr := C.CString(str)
defer C.free(unsafe.Pointer(str_ptr))
cons_ptr := C.parse(str_ptr)
retCons := TranslateCCons2GoCons(cons_ptr)
// FIXME: 在这里做一些释放cons_ptr的操作。Go运行时不会为你做这个
return retCons
}
注意:以上代码只是示例,你可能需要根据实际情况进行适当的修改。
英文:
I would recommend against the accessor functions. You should be able to access the fields of the C struct directly, which will avoid the Go -> C function call overhead (which is non-trivial). So you might use something like:
func TranslateCCons2GoCons (c *C.cons_t) *Cons {
if c == nil {
return nil
}
return &Cons{
type: int(c.type),
car: TranslateCCons2GoCons(c.car),
cdr: TranslateCCons2GoCons(c.cdr),
}
}
Also, if you allocate a C string with C.CString
, you need to free it. So your Parse
function should look something like:
func Parse (str string) *Cons {
str_ptr := C.CString(str)
defer C.free(unsafe.Pointer(str_ptr)
cons_ptr := C.parse(str_ptr)
retCons := TranslateCCons2GoCons(cons_ptr)
// FIXME: Do something to free cons_ptr here. The Go runtime won't do it for you
return retCons
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论