使用 C2Go
将 C 导出给 Go
把已定义的 C 函数和 managed record 暴露为生成的 Go API。维护者C2Go contributorsc2go_extern 会通过生成的 package 导出一个已经定义的 C 函数。Managed record 元数据则让 c2go-bind 暴露与之匹配的 Go 可见布局。
导出函数
#include <c2go.h>
#include <stddef.h>
#include <stdint.h>
c2go_extern int64_t sum(const int64_t *values, size_t count) {
int64_t total = 0;
for (size_t i = 0; i < count; ++i) {
total += values[i];
}
return total;
}
公开 API 建议使用固定宽度整数。long 在 Linux/macOS 64 位 target 上是 64 位,在 Windows 64 位 target 上仍是 32 位;除非这个差异就是契约的一部分,否则不要用它表达跨平台 Go API。
默认 Go 导出名会把每个下划线分段转换为 CamelCase,并移除下划线:sum 变为 Sum,is_upper 变为 IsUpper,sqlite3_open 变为 Sqlite3Open。最终 Go import path 只通过 c2go-clang -fc2go-package 指定,并随 manifest 传给 binder。
只有现有契约确实要求保持拼写时,才使用 c2go_extern_as(C2GO_KEEPCASE):
c2go_extern_as(C2GO_KEEPCASE)
int ParseID(const char *text) { /* ... */ }
声明与定义必须一致。应在第一次声明上写 c2go_extern;只在后续 redeclaration 中补上会被拒绝。
导出 managed record
struct managed Point {
double x;
double y;
};
c2go_extern struct Point translate(
struct Point point,
double dx,
double dy
) {
point.x += dx;
point.y += dy;
return point;
}
manifest 会记录 size、字段 offset 和 pointer layout。c2go-bind 根据这些事实生成 Go 兼容类型,而不是从宿主编译器默认值猜测。
Packed 或自定义 alignment 的 managed record 会产生 C2Go warning。当前 manifest/binder 能描述显式 padding,但最终公开布局与 target 相关,必须检查生成的 Go type。包含 GC 扫描 data pointer 的 unmanaged record 更严格:packing 或自定义 alignment 会被拒绝,因为 conservative scanner 无法找到发生偏移的 pointer word。
Go 保留名称
导出的 init 或 main 函数不能保留小写名。这些名称在 Go 中具有语言级语义,因此 C2Go 会拒绝可能产生冲突的导出。
保持生成 package 完整
除了表面可见的声明,生成文件还包括 ABI/版本 guard,并可能包含 runtime import、GC metadata 或 native-call glue。不要只复制看起来像公共 API 的 .go 文件;应完整保留整个生成目录及其授权记录。
下一章从生命周期出发解释内存与 Go GC,以及如何让导出指针布局保持对 GC 可见。