题
我正在使用 cgo 从 Go 开发库绑定。让我考虑如下的 C 结构和 Go 结构。
struct cons_t {
size_t type;
cons_t *car;
cons_t *cdr;
};
cons_t* parse(const char *str);
这是 go 的结构
type Cons struct {
type int;
car *Cons;
cdr *Cons;
}
为了实现如下Go功能,实现TranslateCCons2GoCons的更好方法是什么?
func Parse (str string) *Cons {
str_ptr := C.CString(string);
cons_ptr := C.parse(str_ptr);
retCons := TranslateCCons2GoCons(cons_ptr);
return retCons;
}
我的第一个答案如下。
/*#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)};
}
还有更好的办法吗?
解决方案
我建议不要使用访问器函数。您应该能够直接访问 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 字符串 C.CString
, ,你需要释放它。所以你的 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: Do something to free cons_ptr here. The Go runtime won't do it for you
return retCons
}
其他提示
你可以在 Go 中使用 C 结构(尽管如果 struct
持有一个 union
它变得有点复杂)。最简单的方法就是
type Cons struct {
c C.cons_t
}
C 中的任何函数现在都只是 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)}
}
这有其自身的开销,因为您必须对元素访问进行类型转换。那么之前的情况是怎样的 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) // now we have to remember to do this
}
不隶属于 StackOverflow