C API and bindings architecture

CNA snapshot 009d40f5  ·  Development › Architecture Maps  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Read from modules/c-api (abi.h, the detail header, CMakeLists.txt), the ABI documents and the module probes; nothing was built. The external repositories are evidence for their own pinned revisions, and the C library was not built or tested for this page.

The CNA repository contains exactly one binding foundation: an explicitly experimental C API, modules/c-api, whose ABI version at this snapshot is 0.29.0. The C#, Java, TypeScript, Python, Rust, Swift, Go and Ruby projects are separate repositories that consume that ABI; they are not generated from CNA, they pin their own ABI generation (0.21.x), and they must not be described as automatically compatible with this snapshot. This page states the boundary rules a C-ABI change has to respect and what a maintainer owes the bindings when it moves; the routes, handle registry and per-layer policies are in C API internals.

Native C API

modules/c-api supplies 61 public C headers under CNA/C/, a shared library cna_c_api (alias CNA::CApi; a static library under Emscripten, which is linked into the cna_c_api_wasm module target), an optional relocatable static archive and a WebAssembly adapter path. It is an opt-in target: CNA_BUILD_C_API defaults to OFF and requires CNA_ENABLE_NET=ON because the library links the GamerServices and Net modules (see the module graph). On ELF a linker version script exports cna_* and nothing else, and the export list for the WebAssembly module is generated from the headers rather than maintained by hand.

  • Opaque, generation-checked handles keep C++ types out of the ABI. A CNA_Handle is a 64-bit value holding a slot index and a generation; a zero, stale, wrong-kind or foreign handle answers CNA_RESULT_INVALID_HANDLE. The registry records the object kind and the creating thread.
  • No exception crosses the boundary. Entry points run inside CallWithExceptionBarrier (CnaCApiDetail.hpp), which maps C++ exceptions to one of the fixed CNA_Result codes (0 success through 14 buffer too small) and records a per-thread last-error message that query routes never overwrite. Callbacks run synchronously on the game's thread with borrowed handles, and a callback that fails stops the game with CNA_RESULT_CALLBACK.
  • The ABI version is exact, not stable. CNA_ABI_VERSION (0.29.0, in abi.h) is unrelated to the product version, and while the ABI is 0.x an incompatible change takes a minor increment, release notes and a regenerated baseline. Two such steps (0.28.0 and 0.29.0) came after the last commit of every external binding, so consumers must validate the exact exported version instead of assuming compatibility. The history is in ABI_VERSIONING.md and summarized on the C API guide.
  • Build status is source-level. No CNA CI workflow builds the C library at this snapshot: the C-API workflows are header, JSON and documentation gates, and CNA's own release gate reports the surface as not ready. Whether the library builds and passes at exactly this commit was not established for this page.

Boundary checklist

  1. State who creates and destroys every handle, and in what order. Owned children are released before their parent, on the creating thread; cna_game_destroy answers CNA_RESULT_INVALID_STATE while owned graphics resources, content managers, audio resources or components exist, and refuses from inside a lifecycle callback. A wrong-thread call answers CNA_RESULT_THREAD, so a foreign garbage collector on another thread cannot be relied on for teardown order.
  2. Define whether every string and buffer is borrowed, copied or transferred, and for how long. The conventions are UTF-8 CNA_StringView in, count-then-copy out (a too-small buffer is refused with no partial write), CNA_Bool restricted to 0 and 1, and versioned input structs that start with struct_size and struct_version.
  3. Convert every failure to the C error contract before returning; an exception that escapes the barrier is a defect.
  4. Keep the public headers valid C: they are compiled alone and through the umbrella at a C99 floor (with C11 and C17, and C++11 to C++17 as required cells), so do not leak C++ syntax, namespaces, templates or C++-only types into them.
  5. Test double-destroy, null and wrong-type handles, callback re-entry, wrong-thread calls and shutdown order.

The tests live under modules/c-api/tests: 85 pure-C programs, five C++ tests (including HandleRegistryTest.cpp and BoundaryDetailTest.cpp, registered as CApi_HandleRegistry and CApi_BoundaryDetail) and two libFuzzer sources. They are built and registered only when -DCNA_BUILD_C_API=ON, and none was executed for this page. The lifetime rules in more detail are CNA's own OWNERSHIP.md, HANDLES.md and CALLBACKS_AND_THREADING.md, and the ownership map places handles beside the C++ lifetimes.

External language repositories

BindingRepositoryABI generation targetedWhat this page supports
C# / .NETlibcna/cna-cs0.21.xSeparate versioning; its README and compatibility data are authoritative
Javalibcna/cna-java0.21.xSeparate generated and handwritten boundary; verify the native ABI revision
TypeScriptlibcna/cna-ts0.21.xSeparate project with its own native-bridge and WebAssembly decisions
Pythonlibcna/cna-python0.21.xSeparate release cadence; do not infer parity from repository existence
Rustlibcna/cna-rust0.21.xSeparate crate and repository contract; verify the ownership mapping
Swiftlibcna/cna-swift0.21.xSeparate package; its loader policy is its own
Golibcna/cna-go0.21.xSeparate module; its loader policy is its own
Rubylibcna/cna-ruby0.21.x (also admits 0.7.0)Separate gem; its loader admits only its own version set
⚠

Compatibility is not established by this source revision. Every public binding targets ABI 0.21.x and this snapshot exports 0.29.0; five of them refuse a 0.29.0 library by an exact-version rule, and none has been qualified against it. Consumer projects are evidence for their own pinned revision, not for this one. Before changing the native API, read each repository's current compatibility matrix and CI; never claim that the latest binding works with the latest CNA. The per-binding pinned commits and loader behavior are tabulated on the C API guide.

Propagating a native API change

  1. Change and test the owning C++ module first.
  2. Decide whether the operation belongs in the experimental C ABI at all. The generated coverage inventory (generate_coverage_inventory.py, output in the C API coverage document) maps every public C++ symbol to implemented, partial, planned or not applicable; the C API guide gives the counts.
  3. Update the C header, the implementation, the symbol and export rules and the C API tests together. The header half of the ABI baseline gate (CApiAbiHeaderBaseline, from generate_abi_baseline.py --check against abi_baseline.json) runs in an ordinary build and names a moved field, a changed constant or a vanished export as a break; CApiAbiBaseline and CApiDeclaredExports additionally need the built library on ELF, and CApiReleaseGate reports the release verdict. Additive changes are re-recorded; an incompatible one requires the version increment above.
  4. For each maintained external binding, pin a compatible native artifact, update its generated declarations and its handwritten ownership and marshaling code, and let its own compatibility policy decide what it accepts. Removing a route (as 0.29.0 did for one) breaks a binding at compile or link time, not at run time.
  5. Run binding tests under sanitizers where the foreign runtime permits it, and treat the C library's own smoke and sanitizer configurations as the C-side evidence.

The step-by-step recipe is update the C API. The C# binding, Java and Python pages describe how those layers apply the ownership rules.

The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.

Tests and validation
Test architecture