c ABI
ABI定义
场景
🚨 以下操作 = ABI 破坏
❌ 改函数参数顺序 ❌ 改参数类型(int → long) ❌ 改返回类型 ❌ 删除导出函数 ❌ 改 struct 布局 ❌ 改 enum 值含义
✅ 以下操作 = ABI 安全
✔ 新增函数 ✔ 新增 enum 值(不改旧值) ✔ 新增 struct 字段(在末尾 + size 字段) ✔ 修复实现 bug
安全策略
opaque handle
// foo_c_api.h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct FooHandle FooHandle;
FooHandle* foo_create(int x);
void foo_destroy(FooHandle* h);
int foo_add(FooHandle* h, int v);
#ifdef __cplusplus
}
#endif
// foo.cpp
#include"foo_c_api.h"
#include <iostream>
class Foo {
public:
explicit Foo(int x) : x_(x) {}
int add(int v) {
return x_ + v;
}
private:
int x_;
};
struct FooHandle {
Foo* impl;
};
extern "C" {
FooHandle* foo_create(int x) {
return new FooHandle{ new Foo(x) };
}
void foo_destroy(FooHandle* h) {
delete h->impl;
delete h;
}
int foo_add(FooHandle* h, int v) {
return h->impl->add(v);
}
}
struct Versioning (结构体版本化)
天真写法(不可扩展)
typedef struct {
int a;
int b;
} FooConfig;
✅ 工业级写法
typedef struct {
uint32_t size; // 必须
uint32_t version; // 可选
int a;
int b;
} FooConfig;
使用方式
FooConfig cfg = {
.size = sizeof(FooConfig),
.version = 1,
.a = 1,
.b = 2
};
c++ 内部判断
if (cfg.size >= offsetof(FooConfig, b) + sizeof(int)) {
// 可以安全访问 b
}
❓ 为什么不用 c++ class 直接导出? 👉 ABI 不稳定
❓ extern “C” 解决了什么? 👉 名字改编, 不解决 ABI 复杂性
❓ 为什么用不透明指针? 👉 隐藏实现 + ABI 稳定
❓ C 能不能 new/delete? 👉 ❌ 必须封装
❓ 异常怎么处理? 👉 C ABI 边界全部 catch
❓ STL 能不能出现在头文件? 👉 ❌ 只能在 cpp 内部