c ABI

 

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 内部