Initialize flexible array C struct member from Go

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

Initialize flexible array C struct member from Go

问题

我正在尝试在Go语言中初始化一个C数组的结构体。
我对cgo还不熟悉,仍在努力理解其用法。

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)

我在"reply: r"处遇到了错误,错误信息为:在类型为struct literal的结构体中,未知字段'r'。

任何帮助将不胜感激。目标是在C的init_s函数中初始化并使用这些值进行处理。

英文:

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.

答案1

得分: 3

你不能在Go中使用灵活数组字段:<https://go-review.googlesource.com/c/go/+/12864/>。

我认为原因很简单:这种C语言的问题通常要求你执行一个技巧,即分配一个足够长的、对齐的内存缓冲区,以容纳sizeof(struct_type)本身在该缓冲区的开头,以及sizeof(array_member[0]) * array_element_count字节。这在Go的类型系统中无法映射,因为在Go中,结构体的大小在编译时是固定的。如果Go不隐藏reply字段的定义,它将引用一个你无法做任何有用操作的零长度字段—参见#20275

不要被用字面值初始化灵活数组成员字段的代码示例所欺骗:正如torek指出的那样,这是GCC的扩展,但更重要的是,它需要编译器的工作—也就是说,它分析字面值,理解它出现的上下文,并生成一个代码,该代码分配足够大的内存块,以容纳结构体和灵活数组的所有成员。
你Go代码中数组的初始化可能表面上看起来相似,但有一个重要的区别:它分配了一个_独立的_数组,与它所谓的“放入”的结构体的内存块无关。
而且Go的数组与C的数组是不同的:在C中,数组是伪装成指针的指针,而在Go中,数组是一等公民,当你赋值给一个数组或将其传递给函数调用时,整个数组都是按值复制的—与C的术语“衰变为指针”相反。
因此,即使Go编译器不隐藏reply字段,对它的赋值也会失败。

我认为你不能直接在Go中使用这种类型的值,除非编写额外的C辅助代码。例如,要初始化common_t的值,你将编写一个C辅助函数,该函数首先分配一个足够长的内存缓冲区,然后向Go代码公开一对指针:指向缓冲区开头的指针(类型为*C.common_t),以及指向数组第一个元素的指针(类型为*C.reply_t)。

如果这段C代码是你自己的代码,我建议你摆脱灵活数组,并在reply字段中维护一个指向“普通”数组的指针。
是的,这意味着CPU需要额外的指针追踪,但与Go的互操作性更简单。

英文:

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.

huangapple
  • 本文由 发表于 2022年9月9日 06:30:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/73655587.html
匿名

发表评论

匿名网友

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

确定