cgo - cast C struct to Go struct

15,084 views
Skip to first unread message

Sonia Hamilton

unread,
Dec 14, 2012, 2:14:32 PM12/14/12
to golan...@googlegroups.com
Using cgo, I have a function that returns a C struct, and I can cast the fields to print them out from Go:

urip := C.gnet_snmp_parse_uri(curi, &gerror)
fmt.Printf("urip: %v\n", urip)
fmt.Printf("scheme: %s\n", C.GoString((*C.char)(urip.scheme)))

urip: &{0x7f1e54000960 0x7f1e54000980 0x7f1e540009a0 161 [0 0 0 0] 0x7f1e540009c0 <nil> <nil>}
scheme: snmp


Rather than manually casting every field, is there an easier way to cast all the fields at once, or convert the C struct to a Go struct?

urip is this C struct:

typedef struct _GURI GURI;

struct _GURI
{
  gchar* scheme;
  gchar* userinfo;
  gchar* hostname;
  gint   port;
  gchar* path;
  gchar* query;
  gchar* fragment;                                                                                                   
};

(Out of interest, the C code is from gsnmp and gnet-2.0/uri.h)

--
Sonia

minux

unread,
Dec 14, 2012, 2:24:36 PM12/14/12
to Sonia Hamilton, golan...@googlegroups.com
working with a lot of typedefs in cgo is a real pain (Go's typing rules are simply too strict
for a C programmer).
I'd suggest you create a wrapper function in C (or Go) to create the structure for you. 

for printing, you can define the String method on real type of structure (it won't be portable,
as it depends the real type name of the C struct, but it's certainly doable, and will save you
a lot of work if you're debugging a C-Type-rich application)
For example,

package main

/*
struct CType {
int a;
char b;
float c;
};
*/
import "C"

import "fmt"

func (c _Ctype_struct_CType) String() string { return "hello" }

func main() {
fmt.Printf("%v\n", C.struct_CType{})
}

bryanturley

unread,
Dec 14, 2012, 2:37:44 PM12/14/12
to golan...@googlegroups.com

http://golang.org/misc/cgo/gmp/gmp.go  <-- cgo example that uses c structs

Sonia Hamilton

unread,
Dec 15, 2012, 12:28:23 AM12/15/12
to golan...@googlegroups.com
Thanks Minux and Bryan, both your comments are helpful. I'll keep reading through gmp & other cgo projects, see how they've handled different C issues.

--
Sonia.

Kevin Gillette

unread,
Dec 15, 2012, 2:06:46 AM12/15/12
to golan...@googlegroups.com
You can cast the entire struct via intermediate pointers. The conversion should work in either direction. There are probably other ways too, like encoding/binary. An example of the pointer approach follows:

package main

import (
"fmt"
"unsafe"
)

// struct x {
// int y, z;
// };
//
// int sum(struct x a) {
// return a.y + a.z;
// }
//
import "C"

type X struct{ Y, Z int32 }

func main() {
a := &X{5, 7}
fmt.Println(a, "->", C.sum(*((*C.struct_x)(unsafe.Pointer(a)))))

Sonia Hamilton

unread,
Dec 16, 2012, 7:16:27 AM12/16/12
to golan...@googlegroups.com
Thanks Kevin. "Much learning to do you have" says Master Yoda...

minux

unread,
Dec 16, 2012, 9:43:56 AM12/16/12
to Kevin Gillette, golan...@googlegroups.com
On Sat, Dec 15, 2012 at 3:06 PM, Kevin Gillette <extempor...@gmail.com> wrote:
You can cast the entire struct via intermediate pointers. The conversion should work in either direction. There are probably other ways too, like encoding/binary. An example of the pointer approach follows:

package main

import (
"fmt"
"unsafe"
)

// struct x {
// int y, z;
// };
//
// int sum(struct x a) {
// return a.y + a.z;
// }
//
import "C"

type X struct{ Y, Z int32 }

func main() {
a := &X{5, 7}
fmt.Println(a, "->", C.sum(*((*C.struct_x)(unsafe.Pointer(a)))))
}
This is not safe and Go doesn't guarantee compatible struct layout rules with gcc.

for example, 8g currently aligns uint64 only to 4-byte boundary, but gcc aligns it to 8-byte boundary.
If you want compatible structure layout, you can use "cgo -godefs".

for example, given file.go:
package main

/*
#include <stdio.h>
*/
import "C"

type File C.FILE

const Sizeof_File = C.sizeof_FILE


go tool cgo -godefs file.go will generate a Go definition of type File that matches that of C's FILE.

edmc...@hotmail.com

unread,
Jul 22, 2013, 6:09:05 PM7/22/13
to golan...@googlegroups.com


On Sunday, December 16, 2012 9:43:56 AM UTC-5, minux wrote:

On Sat, Dec 15, 2012 at 3:06 PM, Kevin Gillette <extempor...@gmail.com> wrote:
// struct x {
// int y, z;
// };
//
// int sum(struct x a) {
// return a.y + a.z;
// }
//
import "C"

type X struct{ Y, Z int32 }

func main() {
a := &X{5, 7}
fmt.Println(a, "->", C.sum(*((*C.struct_x)(unsafe.Pointer(a)))))
}
This is not safe and Go doesn't guarantee compatible struct layout rules with gcc.

for example, 8g currently aligns uint64 only to 4-byte boundary, but gcc aligns it to 8-byte boundary.
If you want compatible structure layout, you can use "cgo -godefs".

Does "cgo -godefs" generate a structure with a compatible layout so that the above cast would be safe? (Sorry for resurrecting such an old thread)

--Ed
 

Stephen Gutekanst

unread,
Jul 23, 2013, 3:22:12 AM7/23/13
to golan...@googlegroups.com
... "If you want compatible structure layout, you can use "cgo -godefs"." ...

So, yes, it does make the above cast 'safe'.

Stephen

edmc...@hotmail.com

unread,
Jul 23, 2013, 12:22:54 PM7/23/13
to golan...@googlegroups.com


On Tuesday, July 23, 2013 3:22:12 AM UTC-4, Stephen Gutekanst wrote:
... "If you want compatible structure layout, you can use "cgo -godefs"." ...

So, yes, it does make the above cast 'safe'.

Stephen

Thanks. I know my question might have looked silly, since it was basically already answered by that line you quoted, but it's nice to have clarification.

--Ed

Stephen Gutekanst

unread,
Jul 23, 2013, 2:26:48 PM7/23/13
to golan...@googlegroups.com
No worries, we've all been there and done that more than once.

AFAIK cgo -godefs is designed to explicitly convert C structs into their equivalent go definitions.

Glad to help,
Stephen
Reply all
Reply to author
Forward
0 new messages