Moyen bon ou recommandé de traduire la structure C en structure Go
Question
j'utilise cgo pour développer la liaison de bibliothèque à partir de Go.Permettez-moi de considérer la structure C et Go Struct comme ci-dessous.
struct cons_t {
size_t type;
cons_t *car;
cons_t *cdr;
};
cons_t* parse(const char *str);
et c'est la structure de go
type Cons struct {
type int;
car *Cons;
cdr *Cons;
}
Pour implémenter la fonction Go comme ci-dessous, quelle est la meilleure façon d'implémenter TranslateCCons2GoCons ?
func Parse (str string) *Cons {
str_ptr := C.CString(string);
cons_ptr := C.parse(str_ptr);
retCons := TranslateCCons2GoCons(cons_ptr);
return retCons;
}
Ma première réponse est la suivante.
/*#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)};
}
existe-t-il un meilleur moyen ?
La solution
Je déconseille les fonctions d'accesseur.Vous devriez pouvoir accéder directement aux champs de la structure C, ce qui évitera la surcharge d'appel de fonction Go -> C (qui n'est pas triviale).Vous pourriez donc utiliser quelque chose comme :
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),
}
}
De plus, si vous allouez une chaîne C avec C.CString
, vous devez le libérer.Alors ton Parse
la fonction devrait ressembler à quelque chose comme :
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
}
Autres conseils
Vous pouvez utiliser des structures C dans Go (mais si le struct
détient un union
ça devient un peu plus complexe).Le plus simple serait simplement
type Cons struct {
c C.cons_t
}
Toute fonction en C n'est plus qu'un relais dans 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)}
}
Cela a sa propre surcharge, puisque vous devez effectuer une conversion de type lors de l'accès aux éléments.Alors qu'était-ce qu'il y avait avant var c Cons{}; c.Type
est maintenant
func (c Cons) Type() int {
return int(c.c.type)
}
Un compromis intermédiaire peut être utilisé lorsque vous stockez des champs à côté du type C pour un accès facile
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
}
Le seul vrai problème est que si vous appelez beaucoup des fonctions C, cela peut introduire une surcharge de maintenance dans la définition des champs Go-side :
func (c *Cons) SomeFuncThatAltersType() {
C.someFuncThatAltersType(&c.c)
c.Type = int(c.c.type) // now we have to remember to do this
}