如何移除C中的extern函数并实现Go调用C++库的C封装回调
问题描述
我有一个C库的C封装,想要在Go语言中调用,预期的回调流程是 **Go->C->C**。目前C->C++部分已经测试通过,但在Go->C环节遇到以下问题:
- 按照Go官方Wiki示例实现后,Linux下可正常运行,但Windows环境下分开编译C库与Go代码时,会因
extern关键字报错。 - 曾尝试将C库拆分为完全不依赖Go的部分,把带
extern的回调放在Go目录中,但所需的回调函数依赖未对外暴露的from_c函数(用于返回C++对象),代码如下:
ClipperPaths64 * clipper_clipperoffset_execute_gocallback(void *mem, ClipperClipperOffset *c, uintptr_t h) { auto result = new (mem) Paths64(); from_c(c)->Execute(from_c(h), *result); return to_c(result); }
这个方案因from_c无法在外部使用而不可行,需要解决Windows下的编译问题。
解决方案
1. 统一编译单元(避免分开编译)
在Windows环境下,不要将C库和Go代码分开编译,而是把需要回调的C代码直接嵌入Go的cgo代码块中:
- 将回调函数
clipper_clipperoffset_execute_gocallback、from_c/to_c的定义与实现,全部放在Go项目的cgo头文件或.go文件的/* ... */ import "C"块内。 - 编译时Go会将这些C/C++代码与Go代码合并编译,既保证内部函数的可见性,又避免跨编译单元的
extern符号问题。
2. 调整C库的Windows符号导出规则
Windows动态库的符号导出需要显式声明,修改C库代码适配:
- 在C库的头文件中添加导出宏:
#ifdef _WIN32 #define CLIPPER_EXPORT __declspec(dllexport) #else #define CLIPPER_EXPORT #endif // 导出回调函数 CLIPPER_EXPORT ClipperPaths64 *clipper_clipperoffset_execute_gocallback(void *mem, ClipperClipperOffset *c, uintptr_t h);
- 确保
from_c/to_c的实现包含在C库中(作为内部函数,无需导出),这样回调函数在C库内部可以正常调用它们。 - Go代码中链接动态库时,通过
#cgo LDFLAGS指定库路径和名称,确保能找到导出的符号。
3. 改用静态库编译
将C封装库编译为Windows静态库(.lib),再通过cgo链接:
- 编译C++/C代码为静态库,所有符号(包括
from_c和回调函数)都会被打包到静态库中,不存在动态导出的问题。 - 在Go代码中配置
cgo链接静态库:
/* #cgo LDFLAGS: -L./lib -lclipper_c_static #include "clipper_c.h" */ import "C"
静态编译能彻底规避动态库的符号可见性问题,同时保证内部函数的调用权限。
内容的提问来源于stack exchange,提问作者lvjonok
相关产品推荐
相关产品推荐

