Reputation: 53
I am trying to initialize a struct of C array in go side. I am new to cgo. Still trying to understand the use case.
test.h
typedef struct reply {
char *name;
reply_cb callback_fn;
} reply_t;
typedef struct common {
char *name;
int count;
reply_t reply[];
} common_t;
int
init_s (common_t *service);
test.go
name := C.CString("ABCD")
defer C.free(unsafe.Pointer(name))
num := C.int(3)
r := [3]C.reply_t{{C.CString("AB"), (C.s_cb)(unsafe.Pointer(C.g_cb))},
{C.CString("BC"), (C.s_cb)(unsafe.Pointer(C.g_cb))},
{C.CString("CD"), (C.s_cb)(unsafe.Pointer(C.g_cb))}}
g := C.common_t{
name: name,
count: num,
reply : r,
}
rc := C.init_s(&g)
I am getting error on "reply: r" unknown field 'r' in struct literal of type
Any help will be appreciated. The goal is initialize and then use it values in C init_s for processing.
Upvotes: 1
Views: 475
Reputation: 55453
You cannot use a flexible array field from Go: https://go-review.googlesource.com/c/go/+/12864/.
I think the reasonong is simple: this wart of C normally requires you to perform a trick of allocating a properly-aligned memory buffer long enough to accomodate for the sizeof(struct_type)
itself at the beginning of that buffer plus sizeof(array_member[0]) * array_element_count
bytes. This does not map to Go's type system because in it, structs have fixed size known at compile time. If Go would not hide reply
from the definition, it would refer to a zero-length field you cannot do anything useful with anyway—see #20275.
Don't be deceived by code examples where a flexible array member field is initialized with a literal: as torek pointed out, it's a GCC extension, but what is more important, it requires work on part of the compiler—that is, it analyzes the literal, understands the context it appeared in and generates a code which allocates large enough memory block to accomodate both the struct and all the members of the flexible array.
The initialization of the array in your Go code may look superficially similar but it has an important difference: it allocates a separate array which has nothing to do with the memory block of the struct it's supposed to be "put into".
What's more Go's array are different beasts than C's: in C, arrays are pointers in disguise, in Go, arrays are first-class citizens and when you assign an array or pass it to a function call, the whole array is copied by value—as opposed to "decaying into a pointer"—in C's terms.
So even if the Go compiler would not hide the reply
field, assignment to it would fail.
I think you cannot directly use values of this type from Go without additional helper code written in C. For instance, to initialize values of common_t
, you would write a C helper which would first allocate a memory buffer long enough and then expose to the Go code a pair of pointers: to the beginning of the buffer (of type *C.common_t
), and to the first element of the array—as *C.reply_t
.
If this C code is the code you own, I'd recommend to just get rid of the flexible array and maintain a pointer to a "normal" array in the reply
field.
Yes, this would mean extra pointer chasing for the CPU but it will be simpler to interoperate with Go.
Upvotes: 3