互操作
原生库
导入宿主 C ABI 符号,同时明确保持 managed 边界。维护者C2Go contributors在当前实现中,只有声明、没有定义的普通函数默认就是 unmanaged host import。因此普通原生函数只需要常规 C 声明,不要求写 unmanaged extern。符号会通过宿主 C ABI 从真实 .so、.dylib 或 .dll 解析。
显式 unmanaged extern 是可选但更强的契约:它表示该函数之后不会再得到 C2Go 定义。当参数或返回值本身是函数指针时,它还会选择 host-callback 函数指针规则。回调章节只因这个特殊目的使用显式形式。
声明导入
#include <c2go.h>
extern double vendor_scale(double value);
c2go_extern double scaled_value(double value) {
return vendor_scale(value);
}
未使用的普通声明不会进入生成的 import metadata。显式 unmanaged extern 即使未被 C 代码引用,也可以继续暴露给生成的 Go;这也是只在确实需要该契约时才使用显式形式的另一个原因。
c2go_extern 不是 import 标记:它把一个 C 定义导出给 Go。这两个概念有意保持分离。
告诉 c2go-bind 加载哪个库
绑定时传入动态库名称和可选搜索目录:
c2go-bind \
--out=native \
--sidecar=native.json \
-l vendor \
-L /opt/example/lib \
native.s
manifest 通常会决定需要 Unix 还是 Windows dispatch glue。--extern-os=unix|windows 只用于受控覆盖,不应用它掩盖 target mismatch。
无 cgo 构建
C2Go 生成的是普通 Go 文件和 Plan 9 汇编,不会生成 import "C"。c2go-libc 在 Unix 上使用固定版本的 PureGo bridge 完成原生调用;Windows 使用对应的 syscall bridge。因此生成后的 package 可以在关闭 cgo 的环境构建:
CGO_ENABLED=0 go build ./...
最终构建不需要宿主 C 编译器。若程序调用外部动态库,该 .so、.dylib 或 .dll 在运行时仍然必须存在;无 cgo 不等于把第三方动态库静态打进程序。
指针仍然需要所有权规则
wrapper 负责 calling convention 和 runtime transition,但不会凭空创造所有权策略。传递指针前必须明确:
- 内存由哪一侧分配;
- callee 是否在返回后继续保存;
- buffer 是否可能包含 managed 指针;
- 最终由哪一侧释放。
如果外部库会保存 buffer,应遵守该库规定的 allocator/释放函数,或改用稳定 handle;不能假定 c2go-libc 的 malloc 就是宿主系统 allocator。把 managed 指针传过边界可能产生有损转换 warning,并需要明确设计生命周期。
受支持的调用形状
当前 Unix 与 Windows target 支持标量、指针、float 和 double 导入。在目标 ABI 限制内支持小 record 按值传递;更大或特殊形状会使用 wrapper 或 fail closed。变参导入和平台特有 record 应增加专门的跨目标测试。
直接调用与函数指针
调用 vendor_scale(value) 会自动经过生成的宿主桥。如果需要 C2Go 可调用的函数指针,使用 c2go_callout(vendor_scale);如果需要把原生地址交回另一个 native API,使用 &vendor_scale,但不要从 C2Go 直接调用这个裸地址。
下一章通过回调详细说明两个方向。