传递给C函数的正确Go类型是什么?

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

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.

huangapple
  • 本文由 发表于 2014年11月6日 13:05:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/26772100.html
匿名

发表评论

匿名网友

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

确定