将C结构体翻译为Go结构体的好方法或推荐方法是什么?

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

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
}

huangapple
  • 本文由 发表于 2014年4月11日 13:49:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/23004474.html
匿名

发表评论

匿名网友

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

确定