Skip to main content

Crate prebindgen_c

Crate prebindgen_c 

Source
Expand description

CbindgenBuilder — the C / cbindgen language adapter.

§Experimental API

This module is a proof of concept. Its Rust builder API may change in a minor release; do not rely on it as part of the stable 0.5 API.

A Prebindgen back-end that turns a “flat” #[prebindgen] library into a Rust file suitable for cbindgen to parse into a C header plus a static / dynamic library.

Items are opt-in: nothing is converted unless it is explicitly declared with CbindgenBuilder::function / CbindgenBuilder::opaque_ptr / CbindgenBuilder::data_struct / CbindgenBuilder::enum_type / CbindgenBuilder::tagged_union. The C name of a declared type’s generated destructor can be pinned by chaining CbindgenBuilder::base_name.

§C ABI conventions

  • Pointer struct (declared with CbindgenBuilder::opaque_ptr): a Box-owned Rust value whose lifecycle is owned by the C side. The C type T is opaque/incomplete and the handle is a bare T * = Box::into_raw. A typed <name>_drop(T *) destructor (running the Rust Drop) is generated per handle.

  • Data struct (declared with CbindgenBuilder::data_struct): a by-value #[repr(C)] struct whose fields are mapped to C-ABI wire types (String*mut c_char). No per-struct destructor — each char* field is released individually via the CbindgenBuilder::free_memory_function.

  • Enum type (declared with CbindgenBuilder::enum_type): a fieldless enum, mirrored as a #[repr(C)] enum that cbindgen renders as the C enum. Rust → C hands over the mirror directly (Rust only ever builds declared variants). C → Rust must not do the reverse: a C enum is an int at the ABI, so materialising a caller-supplied discriminant as a Rust enum is undefined behaviour when it matches no variant — before any match could check it. An enum parameter is therefore taken as MaybeUninit<mirror> — the same ABI and the same C spelling (cbindgen renders MaybeUninit<T> as T), but legal to hold any bit pattern — and its raw c_int is validated against the mirror’s variants before the Rust value is built. An unmatched value is a fallible-input error (see below), so a function taking an enum by value needs either a Result return or CbindgenBuilder::panic. This relies on cbindgen’s C rendering; the C++ language mode is not supported.

  • Tagged union (declared with CbindgenBuilder::tagged_union): a data-carrying enum crossing by value as a #[repr(C)] enum with payload variants, which cbindgen renders as a tag enum plus a union of the variant bodies. When any variant’s payload wire owns memory, a typed <name>_drop frees the active arm. Inbound it obeys the same rule as a plain enum, one level up: the mirror arrives as MaybeUninit<mirror>, its leading c_int tag is range-checked against the variants, and only then is the value assume_inited and matched. Every payload wire is bit-pattern-agnostic (a declared enum_type payload rides as MaybeUninit too, and is validated by its own converter), so the tag is the sole obligation. The typed drop checks it as well and treats an out-of-range one as nothing to release.

  • Direct String output: a bare char * — a malloc’d, null-terminated raw block (no wrapper struct), freed via the free_memory_function.

  • CbindgenBuilder::free_memory_function: the single, type-agnostic raw memory freer (C free) for every char* the layer hands out (string returns and data-struct String fields). It runs no destructor and needs no length. Required whenever such string memory is produced.

  • Result<T, E> return lowers by the success wire kind:

    • pointer wire (opaque handle, char*) → T f(<inputs>, E *e), where a NULL return signals error (details written to *e);
    • unitbool f(<inputs>, E *e);
    • value wire (data struct, scalar, enum) → bool f(T *out, <inputs>, E *e) filling a caller-allocated *out.

    e may be NULL, in which case the error value is dropped. Infallible producers return the value/pointer directly (no out-param).

§Error handling (multiple error types)

Any type used as the E of a Result<T, E> return must be declared as an error type via CbindgenBuilder::data_struct + CbindgenBuilder::error — otherwise the build fails. Error types are ordinary data structs (marshalled by value) and must additionally implement From<String>.

Built-in input converters that can fail (a String arg, an opaque handle passed by value, a declared enum whose discriminant the caller chose) are error-type-agnostic: they return Result<_, String> where the Err is just a message. The generated wrapper for a Result<T, E> function converts such a message into that function’s E via <E as From<String>>::from(msg); the function’s own Err(E) is marshalled directly through E’s output converter.

If a function can produce such an internal message but does not return Result, that is a build error — suppress it by chaining CbindgenBuilder::panic after the function declaration, which makes the wrapper panic! on the internal error instead.

References to the original Rust types in generated bodies are written fully-qualified against CbindgenBuilder::source_module so the generated file can define its own identically-named #[repr(C)] wrapper structs without colliding with the source crate’s types.

Structs§

Cbindgen
C / cbindgen language adapter. Build it with CbindgenBuilder::new, declare the items to convert with the fluent methods, then drive it through CbindgenBuilder::buildCbindgen::write_rust.
CbindgenBuilder

Functions§

snake_case
PascalCase → snake_case (ZKeyExprz_key_expr). Convert a PascalCase / camelCase identifier to snake_case (a convention-free helper, re-exported for consumers composing their own CbindgenBuilder::mangle_rust_type rules). Thin alias for the core spelling, which sum-variant leaf naming shares.