Tutorial 129: Inspect the Experimental C API Boundary

CNA Tutorials  ·  CNA 0.1.0-alpha.1

Do not attempt to ship the alpha.1 C library. Product release 0.1.0-alpha.1 contains a broad C ABI 0.7.0 source surface, but its final C API implementation cannot compile: the C renderer table has 49 entries while CNA has 50 identities. NanoVG has no C identity.

Confirm the tag-level blocker

Inspect modules/c-api/src/CnaCApiCoreExt.cpp. Its RendererIdentities array ends at PIXIJS, has 49 rows, and is checked against the generated canonical C++ renderer count:

static_assert(RendererIdentities.size() == CanonicalRendererCount(),
              "A renderer was added to CNA::GraphicsRendererType without a C identity.");

The tag's C++ enum includes NanoVG as identity 50, so the assertion fails. An isolated Headless/Null-audio build with default networking reproduces comparison reduces to (49 == 50). This is a useful fail-closed guard: the C contract cannot silently return the wrong identity. It also means the later configure, install and consumer steps in the repository describe an intended design, not a usable alpha.1 package.

Do not try to avoid the issue with -DCNA_ENABLE_NET=OFF. Alpha.1 configures that combination but then fails earlier because the C API unconditionally includes a GamerServices header whose module is disabled.

Understand the intended consumer project

After a later release corrects the renderer map, the declared package shape is a C99-compatible public header surface backed by C17 implementation targets:

cmake_minimum_required(VERSION 3.20)
project(hello_cna_c LANGUAGES C)

find_package(CNA 0.7 CONFIG REQUIRED)

add_executable(hello_cna main.c)
set_target_properties(hello_cna PROPERTIES
  C_STANDARD 99
  C_STANDARD_REQUIRED ON
  C_EXTENSIONS OFF)
target_link_libraries(hello_cna PRIVATE CNA::CApi)

Read the source-owned example

The following excerpt shows the intended API style. It is documentation only at alpha.1; you cannot link it against a successfully built tag artifact.

#include <CNA/C/cna.h>

#include <stdint.h>
#include <stdio.h>
#include <string.h>

static CNA_Result update(
    CNA_Handle game,
    const CNA_GameTime* time,
    void* context,
    CNA_CallbackError* error)
{
    (void)time;
    (void)context;
    (void)error;
    return cna_game_request_exit(game);
}

int main(void)
{
    const uint32_t abi = cna_get_abi_version();
    if ((abi >> 16U) != CNA_ABI_VERSION_MAJOR) {
        fprintf(stderr, "incompatible CNA C ABI\n");
        return 1;
    }

    CNA_GameCallbacks callbacks;
    CNA_GameCreateInfo create_info;
    CNA_Handle game = CNA_INVALID_HANDLE;
    static const char title[] = "CNA C API";

    memset(&callbacks, 0, sizeof(callbacks));
    callbacks.struct_size = (uint32_t)sizeof(callbacks);
    callbacks.struct_version = UINT32_C(1);
    callbacks.update = update;

    memset(&create_info, 0, sizeof(create_info));
    create_info.struct_size = (uint32_t)sizeof(create_info);
    create_info.struct_version = UINT32_C(1);
    create_info.is_fixed_time_step = CNA_TRUE;
    create_info.target_elapsed_time_ticks = INT64_C(166667);
    create_info.window_title.data = title;
    create_info.window_title.byte_length = sizeof(title) - 1U;
    create_info.callbacks = &callbacks;

    CNA_Result result = cna_game_create(&create_info, &game);
    if (result == CNA_RESULT_SUCCESS) {
        result = cna_game_run(game);
    }

    if (game != CNA_INVALID_HANDLE) {
        CNA_Result destroy_result = cna_game_destroy(game);
        if (result == CNA_RESULT_SUCCESS) result = destroy_result;
    }

    return result == CNA_RESULT_SUCCESS ? 0 : 1;
}

Every fallible operation returns CNA_Result. Versioned structures must be zero-initialized and must set struct_size plus struct_version. Handles are opaque, generation-checked and thread-affine; owned child objects must be destroyed before their owning game.

What to require from a later release

  • The native library compiles with all 50 renderer identities mapped, or the public identity set is intentionally versioned.
  • An installed consumer builds against the staged CNACApi package in both claimed shared/static modes.
  • cna_get_abi_version() matches the public CNA_ABI_VERSION policy.
  • Runtime vertical slices execute for every configuration the release claims, with structured failures and ownership rules checked.

The complete source-owned example at modules/c-api/examples/c/hello_cna.c remains useful for reviewing capability queries, count-then-copy strings, resource ownership and diagnostics, but it is not alpha.1 runtime evidence.

What is still measurable: the tag contains 59 public C headers and 2,861 declared route names; its checked-in inventory records 6,317 implemented, 15 partial and 380 not-applicable rows out of 6,712. These are source and generated-report counts, not proof of a linkable or runnable C library.

Next steps