问题描述
我有一些使用反射来处理结构体字段的通用代码。这些字段可能包含也可能不包含通过 Cgo 引用的 C 类型。
我遇到的一个问题是,前面提到的那些 C 类型可能是“不完整的结构”,例如:typedef struct unkNown u;
。这可能会导致代码稍后出现恐慌。
理想情况下,我需要能够检查和跳过这样的实例,而不会意外跳过有效字段。
我想检查 Type.Size()
返回的值是否为 0,但空的 Go 结构体将返回相同的值。
有没有办法做到这一点?
解决方法
TL;DR 总结:Cgo 没有不完整的类型,所以这里没有什么可测试的;但是您不应该以导致运行时恐慌的方式使用不完整的类型。 (显示minimal reproducible example,有人可能会告诉您哪里出错了。)
在C中,你不能有一个不完整类型的对象,但你可以有一个指向类型的值(一些不完整的类型),你可以将它存储在一个类型的对象中指向不完整类型的指针。无法跟随(取消引用)指针。
奇怪的是,Cgo 似乎允许跟随指针。大概是因为Go中没有不完整类型这样的东西,所以Cgo只是把C对象变成了一个不包含字段且大小为零的结构体:
package main
// #include <stdlib.h>
//
// struct incomp;
// struct incomp *foo() {
// return malloc(10);
// }
import "C"
import (
"fmt"
"reflect"
)
func newFoo() *C.struct_incomp {
return C.foo()
}
func main() {
p := newFoo()
t := reflect.TypeOf(*p)
if t.Kind() != reflect.Struct {
panic("not a struct")
}
fmt.Println("Size:",t.Size())
fmt.Println("NumFields:",t.NumField())
}
我有一些使用反射来处理结构体字段的通用代码。这些字段可能包含也可能不包含通过 Cgo 引用的 C 类型。 ... 这可能会导致代码稍后出现恐慌。
鉴于上述情况(不完整的“对象”本身的大小为零),永远不要将实际的不完整类型的 Cgo 类型嵌入到 Go 类型中。始终使用 (C) 指针。否则,Go 类型中的任何后续字段将覆盖 C 代码中的实际字段。