8

我正在使用 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)};
}

有没有更好的方法?

4

2 回答 2

2

您可以在 Go 中使用 C 结构(尽管如果struct持有 aunion它会变得更复杂一些)。最简单的方法就是

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-side 字段时引入维护开销:

func (c *Cons) SomeFuncThatAltersType() {
    C.someFuncThatAltersType(&c.c)
    c.Type = int(c.c.type) // now we have to remember to do this
}
于 2014-04-11T06:00:48.240 回答
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 字符串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
}
于 2014-04-11T06:02:25.813 回答