英文:
Correct Go type to pass to C function?
问题
我正在将我用C语言编写的一些服务器代码移植到Go语言,并且它使用了一个我真的不想重写的加密库。相反,我正在尝试使用Cgo编写一个包装器,以便我的其他代码可以更轻松地调用它。这是库的一部分头文件:
// encryption/encryption.h
#define CRYPT_BBCFG 1
typedef struct {
// ...一堆字段...
uint32_t bb_posn;
} CRYPT_SETUP;
int CRYPT_CreateKeys(CRYPT_SETUP* cs, void* key, unsigned char type);
这是我正在尝试使其工作的概念验证代码片段:
package goserv
//#include "encryption/encryption.h"
import "C"
func main() {
cdata := new(C.struct_CRYPT_SETUP)
key := make([]byte, 48)
C.CRYPT_CreateKeys(cdata, &key[0], C.CRYPT_BLUEBURST)
}
我在头文件中定义了一个测试函数(int test() { return 1; }
),从我的代码中调用它没有问题(通过C.test()
),也可以引用任何已定义的常量(C.CRYPT_BBCFG
),但是当我尝试运行go install goserv时,会出现以下错误:
Undefined symbols for architecture x86_64:
"_CRYPT_CreateKeys", referenced from:
__cgo_e89359206bf1_Cfunc_CRYPT_CreateKeys in goserv.cgo2.o
ld: symbol(s) not found for architecture x86_64
在这一点上,我认为我只是没有使用正确的参数调用该函数。我认为cdata的类型是*C.struct_CRYPT_SETUP
,key应该是*byte
(尽管没有&
也不起作用),而C.CRYPT_BLUEBURST的类型是...某种类型。尝试使用C.uchar(CRYPT_BLURBURST)
也没有改变任何东西。
有关如何编译此代码的建议吗?
编辑:忘记了我的平台,我正在运行Mac OS X 10.10
编辑2(已解决):Jsor提到使用unsafe.Pointer
与key的第一个元素的地址有所帮助,但我还必须将我的C源文件移动到与Go文件相同的目录中。还有另一个类型错误,由于使用了C.struct_CRYPT_DATA而不是C.CRYPT_DATA,所以如果其他人遇到类似这样的错误:
./goserv.go:18: cannot use cdata (type *C.struct_CRYPT_SETUP) as type *C.struct___0 in argument to _Cfunc_CRYPT_CreateKeys
那么请删除struct_前缀(尽管cgo文档说这是直接引用C结构类型的方法)。
英文:
I'm porting some server code I wrote in C over to Go and it uses an encryption library I really don't want to rewrite. Instead I'm trying to use Cgo to write a wrapper so that the rest of my code can call it more easily. Here's part of the header for the lib:
// encryption/encryption.h
#define CRYPT_BBCFG 1
typedef struct {
// ...bunch of fields...
uint32_t bb_posn;
} CRYPT_SETUP;
int CRYPT_CreateKeys(CRYPT_SETUP* cs, void* key, unsigned char type);
And here's the proof-of-concept snippet I'm trying to get to work:
package goserv
//#include "encryption/encryption.h"
import "C"
func main() {
cdata := new(C.struct_CRYPT_SETUP)
key := make([]byte, 48)
C.CRYPT_CreateKeys(cdata, &key, C.CRYPT_BLUEBURST)
}
I defined a test function (int test() { return 1; }
) in the header and have no problem calling that from my code (via C.test()
) nor referencing any of the #defined'd constants (C.CRYPT_BBCFG
) but get the following error when I attempt to run go install goserv:
Undefined symbols for architecture x86_64:
"_CRYPT_CreateKeys", referenced from:
__cgo_e89359206bf1_Cfunc_CRYPT_CreateKeys in goserv.cgo2.o
(maybe you meant: __cgo_e89359206bf1_Cfunc_CRYPT_CreateKeys)
ld: symbol(s) not found for architecture x86_64
At this point I'm assuming I'm just not calling the function with the correct arguments. I was under the impression that cdata is of type *C.struct_CRYPT_SETUP
, key should be *byte
(though it doesn't work without the & either) and C.CRYPT_BLUEBURST of type...something. Trying C.uchar(CRYPT_BLURBURST)
also doesn't change anything.
Any suggestions on getting this code to compile?
Edit: Forgot my platform, I'm running Mac OS X 10.10
Edit2 (SOLVED): Jsor's point about using unsafe.Pointer with the address of the first element of key helped but I also had to move my C source files into the same directory as my Go file. There was another type error resulting from using C.struct_CRYPT_DATA instead of C.CRYPT_DATA, so if anyone else runs into errors like this:
./goserv.go:18: cannot use cdata (type *C.struct_CRYPT_SETUP) as type *C.struct___0 in argument to _Cfunc_CRYPT_CreateKeys
Then remove the struct_ prefix (though the cgo docs say that's how to directly reference C struct types)
答案1
得分: 2
你可能想要使用&key[0]
,否则你指向的是Slice Header,它是一个由3个值组成的结构体。你最终会得到一个指向你的缓冲区的指针的指针,这可能不是你想要的。
一般来说,切片的底层缓冲区的指针是&slice[0]
,而不是&slice
。
作为标准免责声明:要小心将Go分配的缓冲区传递给C。几乎每个人都这样做(包括我),但这可能不是一个好主意,并且在某些时候可能停止支持。只要在C中保持对数据的引用,并且不将指针存储以供以后在C端使用,这可能是可以的,但由于Go 1.3的堆栈移动GC,可能会出现奇怪的问题。
更新:我刚想起来的一件事是,C中的void*
类似于Go的unsafe.Pointer
。所以你的调用应该是:
C.CRYPT_CreateKeys(cdata, unsafe.Pointer(&key[0]), C.CRYPT_BLUEBURST)
另一个问题可能是,如果你通常使用-l
链接外部库。对此,使用以下代码:
// #cgo LDFLAGS: -llibname
在你做包含的地方,就在import "C"
的上面。
编辑2:另外,仔细看一下,似乎CRYPT_CreateKeys
函数在头文件中没有定义,只有函数原型。确保如果它在一个单独的.c文件中定义,将其声明为extern
。不这样做会导致Go出错。
英文:
You probably want &key[0]
, otherwise you're pointing to the Slice Header, which is a struct of 3 values. You end up getting a pointer to a pointer to your buffer, which is probably not what you want.
In general, the pointer to the backing buffer of a slice is &slice[0]
, not &slice
.
As a standard disclaimer: be warned about passing Go-allocated buffers into C. Almost everybody does it, (including me) but it may not be a good idea, and may stop being supported at some point. It's probably fine as long as you keep a reference to the data for the entire duration it's in C, and don't store the pointer for later use on the C side, but it's plausible you'll end up with weirdness due to the Go 1.3 stack moving GC.
Update: Something I just remembered -- void*
in C is analogous to Go's unsafe.Pointer
. So your call should be:
C.CRYPT_CreateKeys(cdata, unsafe.Pointer(&key[0]), C.CRYPT_BLUEBURST)
Another issue may be if you usually link an external library with -l
. For this, use
// #cgo LDFLAGS: -llibname
In the area right above import "C"
where you do your includes.
Edit2: Also, looking at it more, it looks like the function CRYPT_CreateKeys
is not defined in the header file -- only the function prototype. Make sure if it's defined in a separate .c file to declare it extern
. Not doing so will trip up Go.
答案2
得分: 0
如果你在Go和C之间传递缓冲区,还有一个要注意的地方。最好的选择是以一种方式编写你的C代码,即将由Go传递的缓冲区在本地进行复制。这样做的好处是,当你将缓冲区强制转换为int*或其他指针类型时,你将不再依赖于Cgo提供的缓冲区。
英文:
One more point if you are passing buffers in between go
And C. It’s good option to write your C code in a way that,
It copies buffer locally passed by GO.
This will help when you are typecasting your buffer to
Int* or some other pointer type.
This way you will be independent of the buffer supplied
By Cgo.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论