Skip to content

C ABI Design

libfdl_core exposes a pure C ABI through native/core/include/fdl/fdl_core.h. This document describes the interface contract for anyone calling the library directly or writing a new language binding.

For function-level documentation, see the Doxygen C/C++ API Reference.

Opaque Handles

All domain objects are represented as opaque pointers to forward-declared structs:

typedef struct fdl_doc fdl_doc_t;
typedef struct fdl_context fdl_context_t;
typedef struct fdl_canvas fdl_canvas_t;
typedef struct fdl_framing_decision fdl_framing_decision_t;
typedef struct fdl_framing_intent fdl_framing_intent_t;
typedef struct fdl_canvas_template fdl_canvas_template_t;
typedef struct fdl_clip_id fdl_clip_id_t;
typedef struct fdl_file_sequence fdl_file_sequence_t;

Only fdl_doc_t* is caller-owned. Create it with fdl_doc_create() or fdl_doc_parse_json(), free it with fdl_doc_free().

All other handles are borrowed pointers into the document's internal data structure. They are obtained via collection traversal (fdl_doc_context_at, fdl_context_canvas_at, etc.) and remain valid until the owning document is freed. Handles use index-based resolution and are stable across collection mutations (e.g., push_back).

Value Types

Small data structures are passed by value (not as opaque handles):

Type Fields Usage
fdl_dimensions_i64_t width, height (int64) Canvas pixel dimensions
fdl_dimensions_f64_t width, height (double) Computed/intermediate dimensions
fdl_point_f64_t x, y (double) Anchor points, offsets
fdl_rect_t x, y, width, height (double) Bounding rectangles
fdl_round_strategy_t even, mode (uint32) Rounding configuration
fdl_geometry_t 4 dims + 3 anchors Template pipeline geometry container

These are stack-allocated -- no heap management required.

String Ownership

The ABI uses two string return conventions:

Borrowed strings (const char*)

Returned by field accessors like fdl_canvas_get_label(). The pointer is thread-local and valid until the next call for the same field on the same thread. Do not free these.

Caller-owned strings (char*)

Returned by serialization functions (fdl_doc_to_json, fdl_context_to_json, etc.) and error messages. The caller must free these with fdl_free().

The IDL annotates this with ownership: caller_frees on the relevant functions in fdl_api.yaml.

Error Handling

The ABI uses three error-reporting patterns:

Null return -- factory functions return NULL on failure:

fdl_doc_t* doc = fdl_doc_create();  // NULL on allocation failure

Result structs -- operations that can fail return a struct with both a success value and an error string:

fdl_parse_result_t r = fdl_doc_parse_json(json, len);
if (r.doc)   { /* success -- caller owns r.doc */ }
if (r.error) { /* failure -- caller must fdl_free(r.error) */ }

Validation result -- fdl_doc_validate() returns an opaque result handle with error_count + error_at(index):

fdl_validation_result_t* v = fdl_doc_validate(doc);
for (uint32_t i = 0; i < fdl_validation_result_error_count(v); i++)
    printf("%s\n", fdl_validation_result_error_at(v, i));
fdl_validation_result_free(v);

Enums

Enums are uint32_t typedefs with #define constants using the FDL_ prefix:

typedef uint32_t fdl_rounding_mode_t;
#define FDL_ROUNDING_MODE_UP    0
#define FDL_ROUNDING_MODE_DOWN  1
#define FDL_ROUNDING_MODE_ROUND 2

Enum types: fdl_rounding_mode_t, fdl_rounding_even_t, fdl_geometry_path_t, fdl_fit_method_t, fdl_halign_t, fdl_valign_t, fdl_custom_attr_type_t.

Collection Traversal

Collections follow a consistent count / at / find_by_id pattern:

uint32_t n = fdl_doc_contexts_count(doc);
for (uint32_t i = 0; i < n; i++) {
    fdl_context_t* ctx = fdl_doc_context_at(doc, i);
    // use ctx...
}
// Or look up by identifier:
fdl_context_t* ctx = fdl_doc_context_find_by_label(doc, "Camera A");

Custom Attributes

Custom attributes use a macro-generated API providing 19 functions per handle type across all 8 handle types (152 functions total). Attribute names are passed without the underscore prefix -- the library prepends _ internally.

Type-safe: setting an attribute with a different type than its current value returns -1. Remove first, then set with the new type.

ABI Versioning

The library reports its ABI version at runtime:

fdl_abi_version_t v = fdl_abi_version();
// v.major -- breaking changes
// v.minor -- backwards-compatible additions
// v.patch -- bug fixes

CI enforces ABI parity between fdl_api.yaml and the compiled library via python scripts/check_abi_symbols.py.

Thread Safety

Per-document mutex locking. See the Architecture doc for details.

Memory Management

All heap-allocated values returned by the library must be freed by the caller:

Allocated by Free with
fdl_doc_create, fdl_doc_parse_json fdl_doc_free()
fdl_apply_canvas_template fdl_template_result_free()
fdl_doc_validate fdl_validation_result_free()
fdl_doc_to_json, error strings, etc. fdl_free()

fdl_free() is safe to call with NULL.