C ABI consumer
This example is the docs/examples/c_consumer reference from the C+ source
tree. It backs the systems-language claim that C+ emits normal libraries that C
programs can call.
The recipe has two parts:
docs/examples/c_consumer/
├── mathlib/
│ ├── Cplus.toml
│ └── src/lib.cplus
└── c_user/
├── c_user.c
└── Makefile
mathlib is a C+ library. c_user is a C program that includes the generated
mathlib.h, links the built library, calls every export, and exits nonzero if
any ABI shape fails.
ABI coverage
| Export | ABI class | C-side shape |
|---|---|---|
add(i32, i32) -> i32 |
scalar arguments and return | int32_t add(int32_t, int32_t) |
square(Point) -> i32 |
8-byte #[repr(C)] aggregate argument |
int32_t square(Point) |
make_point(...) -> Point |
8-byte aggregate return | Point make_point(...) |
sum_pair(Pair) -> i64 |
16-byte aggregate argument | int64_t sum_pair(Pair) |
make_pair(...) -> Pair |
16-byte aggregate return | Pair make_pair(...) |
sum_triple(Triple) -> i64 |
larger-than-16-byte aggregate argument | int64_t sum_triple(Triple) |
make_triple(...) -> Triple |
larger-than-16-byte aggregate return | C sret result shape |
color_index(Color) -> i32 |
plain enum | C enum constants |
fill_with(*i32, i32) |
raw pointer out parameter | int32_t * |
apply(fn(i32)->i32, i32) |
C function pointer argument | int32_t (*)(int32_t) |
C+ exports
The aggregate types that cross the C boundary are explicitly #[repr(C)]:
#[repr(C)]
pub struct Point {
pub x: i32,
pub y: i32,
}
pub extern fn square(p: Point) -> i32 {
return p.x * p.x + p.y * p.y;
}
pub extern fn apply(f: fn(i32) -> i32, x: i32) -> i32 {
return f(x);
}
The C consumer passes a normal C callback through the generated header:
static int32_t double_it(int32_t x) { return x * 2; }
int32_t y = apply(double_it, 21);
printf("apply(double_it, 21) = %d (expected 42)\n", y);
if (y != 42) failures++;
Reproduce
From docs/examples/c_consumer/c_user in the C+ source tree:
make check
Expected result: the Makefile builds mathlib, builds the C program with
clang, runs it, prints each expected value, and finishes with 0 failure(s).
That is the useful proof boundary: a separate C compiler consumes the C+ library through generated C-facing declarations, not through a C+ test harness.
‹ Back to all examples