# Output Formats [Home](index.md) > Output Formats An output format defines how the filtered and transformed IR is rendered into target code. Each format is described by a `.output.yml` file containing metadata, type mappings, and a Jinja2 template. --- ## Format File Structure A `.output.yml` file has the following top-level keys: ```yaml format_name: myformat format_version: "1.0" description: "Human-readable description" language: cpp # informational only # Substring-matched types that cause methods/fields to be excluded unsupported_types: - CFStringRef - OSType # Format-level type mapping: applied only in template rendering (the map_type filter) type_mappings: "std::string": "String" "int32_t": "int" # Single Jinja2 template (entire format defined here) template: | // Generated by tsujikiri for {{ module_name }} ... # Or alternatively a reference to an external template Jinja2 file template_file: "myformat.output.tpl" ``` ### `unsupported_types` Any method whose return type or any parameter type **contains** a listed string is excluded from the context entirely — the template never sees it. Same for fields whose type contains a listed string. This is a substring check (not exact, not regex). `CFStringRef` will also match `const CFStringRef *`. ### `type_mappings` vs `add_type_mapping` transform | | `type_mappings` in `.output.yml` | `add_type_mapping` transform | |-|----------------------------------|------------------------------| | Where applied | Jinja2 template rendering only (via `map_type` filter) | IR itself, before generation | | Affects manifest | No | Yes | | Affects overload kind detection | No | Yes | | Use for | Cosmetic renaming in output (C++ `int` → Lua `integer`) | Structural type remapping (library type → standard type) | --- ## Built-in: `luabridge3` Generates a C++ registration function that wires up LuaBridge3 bindings for a `lua_State`. ```bash tsujikiri -i project.input.yml --target luabridge3 src/lua_bindings.cpp ``` ### Generated Function ```cpp void register_myproject(lua_State* L) { luabridge::getGlobalNamespace(L) .beginNamespace("myproject") // ... all bindings ... .endNamespace(); } ``` ### Namespace Wrapping All bindings live inside `.beginNamespace("module_name") ... .endNamespace()`. The module name comes from the input file name (`myproject.input.yml` → `"myproject"`). ### Class Registration Plain classes use `.beginClass("BindingName")`. Classes with a C++ base class (first public base) use `.deriveClass("BindingName")`. Tsujikiri applies a **topological sort** so base classes are always registered before derived classes. ```cpp // Base registered first: .beginClass("Shape") .addFunction("get_name", &mylib::Shape::getName) .endClass() // Derived registered after: .deriveClass("Circle") .addFunction("get_radius", &mylib::Circle::getRadius) .endClass() ``` ### Constructor Registration ```cpp .addConstructor() // single-arg constructor .addConstructor() // overloaded constructor ``` Multiple constructors each get their own `.addConstructor<>()` call. ### Method Registration ```cpp // Instance method: .addFunction("get_value", &mylib::Calculator::getValue) // Static method: .addStaticFunction("max", &mylib::Calculator::max) // With wrapper_code (from modify_method): .addFunction("get_name", +[](mylib::Shape& self) { return std::string(self.getName()); }) ``` Method names are converted from `camelCase` to `snake_case` via the `camel_to_snake` Jinja2 filter. ### Overloaded Methods When multiple methods share the same binding name: ```cpp // Different parameter types → luabridge::overload<> .addFunction("resize", luabridge::overload(&mylib::Circle::resize), luabridge::overload(&mylib::Circle::resize) ) // Same params, const vs non-const → constOverload / nonConstOverload .addFunction("get_value", luabridge::constOverload<>(&mylib::Calculator::getValue), luabridge::nonConstOverload<>(&mylib::Calculator::getValue) ) ``` ### Field Registration ```cpp .addProperty("scale", &mylib::Shape::scale_) // read-write .addProperty("radius", &mylib::Circle::radius_, nullptr) // read-only ``` Fields are read-only when `const` in C++ or when `read_only: true` is set via `modify_field`. When a field is renamed, the binding string uses the binding name and the C++ member pointer uses the original C++ name: ```cpp // C++ field `myField_` renamed to `myField` via rename_field or [[tsujikiri::rename(...)]] .addProperty("my_field", &mylib::Shape::myField_) // ^^^^^^^^^ binding name (snake_case applied) // ^^^^^^^^^ original C++ name ``` ### Enum Registration Top-level enums and class-member enums are exposed as nested namespaces with lambda accessors: ```cpp .beginNamespace("Color") .addProperty("Red", +[] { return static_cast>(mylib::Color::kRed); }) .addProperty("Green", +[] { return static_cast>(mylib::Color::kGreen); }) .addProperty("Blue", +[] { return static_cast>(mylib::Color::kBlue); }) .endNamespace() ``` When an enum value is renamed (e.g. `kRed` → `Red`), the property name uses the binding name and the C++ reference uses the original C++ name: ```cpp .addProperty("Red", +[] { return static_cast<...>(mylib::Color::kRed); }) // ^^^^^ binding name original C++ enumerator ^^^^^ ``` ### Operator Metamethods C++ operator overloads are automatically mapped to Lua metamethods using the built-in `operator_mappings` table. The following operators are supported: | C++ operator | Lua metamethod | Description | |-------------|----------------|-------------| | `operator+` | `__add` | Addition | | `operator-` (binary) | `__sub` | Subtraction | | `operator-` (unary) | `__unm` | Negation | | `operator*` | `__mul` | Multiplication | | `operator/` | `__div` | Division | | `operator%` | `__mod` | Modulo | | `operator==` | `__eq` | Equality | | `operator<` | `__lt` | Less than | | `operator<=` | `__le` | Less or equal | | `operator()` | `__call` | Call | | `operator&` | `__band` | Bitwise AND | | `operator\|` | `__bor` | Bitwise OR | | `operator^` | `__bxor` | Bitwise XOR | | `operator~` | `__bnot` | Bitwise NOT | | `operator<<` | `__shl` | Bitwise left shift | | `operator>>` | `__shr` | Bitwise right shift | Note: `__index` and `__newindex` are used internally by LuaBridge3 and are never emitted. Operators without a mapping (e.g. `operator++`) are silently skipped. ```cpp // C++ class with operator overloads: struct Vec3 { Vec3 operator+(const Vec3& rhs) const; Vec3 operator-() const; bool operator==(const Vec3& rhs) const; }; // Generated binding: .beginClass("Vec3") .addFunction("__add", &ns::Vec3::operator+) .addFunction("__unm", &ns::Vec3::operator-) .addFunction("__eq", &ns::Vec3::operator==) .endClass() ``` ### API Version Embedding When `--embed-version` is passed (or `embed_version: true` in `generation`): ```cpp static constexpr const char* k_myproject_api_version = "3a8f...e291"; const char* get_myproject_api_version() { return k_myproject_api_version; } // Inside register_myproject: .addFunction("get_api_version", +[] () -> const char* { return k_myproject_api_version; }) ``` ### Template Block Names The luabridge3 template uses named blocks for every major section. Override any block using `template_extends` in `format_overrides`: | Block | Contents | |-------|----------| | `prologue` | Includes, extern "C", headers, api_version constant, function open | | `api_version` | The `constexpr` and `get_*_api_version()` definition | | `api_version_registration` | The `.addFunction("get_api_version", ...)` call | | `enum` | Per-enum `.beginNamespace() ... .endNamespace()` block | | `enum_value` | Single `.addProperty(...)` for one enum value | | `function_group` | Non-overloaded or overloaded free function `.addFunction(...)` | | `function_overloaded` | Single overloaded free function entry | | `class` | Full class block from `.beginClass()` to `.endClass()` | | `class_constructors` | All `.addConstructor<>()` calls | | `class_constructor_group` | Single `.addConstructor<>()` call | | `class_methods` | All method `.addFunction()` calls | | `class_static_method_group` | Static method group | | `class_method_group` | Instance method group | | `class_overloaded_static_method` | Single overloaded static method cast | | `class_overloaded_method` | Single overloaded instance method cast | | `class_fields` | All `.addProperty()` calls | | `class_field` | Single `.addProperty()` call | | `class_enums` | Nested enum namespace blocks | | `class_enum` | Single enum namespace block | | `class_enum_value` | Single enum value `.addProperty()` | | `epilogue` | `.endNamespace();` and closing brace | ### Complete Example Output (from `combined.hpp`) ```cpp // DO NOT EDIT - Auto-generated LuaBridge3 bindings for combined by tsujikiri extern "C" { #include #include #include } // extern "C" #include #include #include "combined.hpp" void register_combined(lua_State* L) { luabridge::getGlobalNamespace(L) .beginNamespace("combined") .beginNamespace("Color") .addProperty("Red", +[] { return static_cast>(mylib::Color::Red); }) .addProperty("Green", +[] { return static_cast>(mylib::Color::Green); }) .addProperty("Blue", +[] { return static_cast>(mylib::Color::Blue); }) .endNamespace() .addFunction("compute_area", luabridge::overload(&mylib::computeArea), luabridge::overload(&mylib::computeArea) ) .beginClass("Shape") .addConstructor() .addFunction("area", &mylib::Shape::area) .addFunction("perimeter", &mylib::Shape::perimeter) .addFunction("get_name", &mylib::Shape::getName) .addFunction("set_name", &mylib::Shape::setName) .addFunction("get_scale", &mylib::Shape::getScale) .addFunction("set_scale", &mylib::Shape::setScale) .addProperty("scale", &mylib::Shape::scale_) .endClass() .deriveClass("Circle") .addConstructor() .addFunction("perimeter", &mylib::Circle::perimeter) .addFunction("get_radius", &mylib::Circle::getRadius) .addFunction("set_radius", &mylib::Circle::setRadius) .addFunction("resize", luabridge::overload(&mylib::Circle::resize), luabridge::overload(&mylib::Circle::resize) ) .addProperty("radius", &mylib::Circle::radius_) .endClass() .beginClass("Calculator") .addConstructor() .addFunction("add", luabridge::overload(&mylib::Calculator::add), luabridge::overload(&mylib::Calculator::add) ) .addStaticFunction("max", luabridge::overload(&mylib::Calculator::max), luabridge::overload(&mylib::Calculator::max) ) .addFunction("get_value", &mylib::Calculator::getValue) .addFunction("set_value", &mylib::Calculator::setValue) .endClass() .endNamespace(); } ``` --- ## Built-in: `luals` Generates [Lua Language Server](https://luals.github.io/) annotation stubs. Use alongside the luabridge3 output to provide IDE auto-completion and type checking. ```bash tsujikiri -i project.input.yml --target luals types/myproject.lua ``` ### Type Mappings The luals format ships with a comprehensive C++ → Lua type mapping table: | C++ type | Lua type | |----------|----------| | `int`, `unsigned int`, `long`, `short`, `int8_t`, … `uint64_t`, `size_t` | `integer` | | `float`, `double` | `number` | | `bool` | `boolean` | | `std::string`, `const char *`, `char *`, `std::string_view` | `string` | | `void` | `nil` | Types not in this table pass through unchanged (e.g. `Vec3` stays `Vec3`). ### Class Annotations ```lua ---@class Shape ---@field scale number local Shape = {} ---@param name string ---@return Shape function Shape.new(name) end ``` ### Instance Methods ```lua ---@return number function Shape:area() end ---@param name string ---@return nil function Shape:set_name(name) end ``` ### Static Methods ```lua ---@param a integer ---@param b integer ---@return integer function Calculator.max(a, b) end ``` ### Overloads ```lua -- First overload uses @param/@return: ---@param a integer ---@param b integer ---@return integer -- Additional overloads use @overload: ---@overload fun(a: number, b: number): number function Calculator:add(a, b) end ``` ### Operator Metamethods Lua metamethods are emitted as instance methods with the metamethod name. The same operator mappings as luabridge3 are used, so luals annotations stay in sync with the bindings: ```lua ---@param other Vec3 ---@return Vec3 function Vec3:__add(other) end ---@return Vec3 function Vec3:__unm() end ---@param other Vec3 ---@return boolean function Vec3:__eq(other) end ---@return string function Vec3:__tostring() end ``` The `__tostring` metamethod always emits `---@return string` regardless of the C++ return type. Operators without a mapping are silently skipped. ### Enums ```lua ---@enum Color local Color = { Red = 0, Green = 1, Blue = 2, } ``` --- ## Built-in: `pybind11` Generates a C++ `PYBIND11_MODULE` block that registers classes, methods, properties, and enums using [pybind11](https://pybind11.readthedocs.io/). ```bash tsujikiri -i project.input.yml --target pybind11 src/py_bindings.cpp ``` ### Generated Module ```cpp #include #include #include "myproject.hpp" namespace py = pybind11; PYBIND11_MODULE(myproject, m) { // ... all bindings ... } ``` ### Class Registration Plain classes use `py::class_(m, "BindingName")`. Classes with public base classes use `py::class_(m, "BindingName")`. Multiple base classes are listed in order. ```cpp py::class_(m, "Shape", "A 2D shape") .def(py::init()) .def("area", &mylib::Shape::area) .def_readwrite("scale", &mylib::Shape::scale_); py::class_(m, "Circle") .def(py::init()) .def("get_radius", &mylib::Circle::getRadius); ``` ### Method Registration ```cpp // Instance method with py::arg: .def("get_value", &mylib::Calculator::getValue, py::arg("key")) // Static method: .def_static("max", &mylib::Calculator::max) // With wrapper_code (from modify_method/modify_function): .def("get_name", [](mylib::Shape& self) { return std::string(self.getName()); }) ``` Method names are converted from `camelCase` to `snake_case` via the `camel_to_snake` filter. ### Overloaded Methods ```cpp .def("resize", py::overload_cast(&mylib::Circle::resize), py::arg("factor")) .def("resize", py::overload_cast(&mylib::Circle::resize), py::arg("width"), py::arg("height")) ``` ### Field Registration ```cpp .def_readwrite("scale", &mylib::Shape::scale_) // read-write .def_readonly("radius", &mylib::Circle::radius_) // read-only ``` When a field is renamed, the binding string uses the binding name and the C++ member pointer uses the original C++ name: ```cpp // C++ field `myField_` renamed to `myField` via rename_field or [[tsujikiri::rename(...)]] .def_readwrite("my_field", &mylib::Shape::myField_) // ^^^^^^^^^ binding name (snake_case applied) // ^^^^^^^^^ original C++ name ``` ### Enum Registration ```cpp py::enum_(m, "Color", "Colour enumeration") .value("Red", mylib::Color::kRed, "The red channel") .value("Green", mylib::Color::kGreen) .value("Blue", mylib::Color::kBlue) .export_values(); ``` When an enum value is renamed (e.g. `kRed` → `Red`), the `.value(...)` string uses the binding name and the C++ enumerator reference uses the original C++ name: ```cpp .value("Red", mylib::Color::kRed) // ^^^^^ binding name ^^^^ original C++ enumerator name ``` ### API Version Embedding When `--embed-version` is passed: ```cpp static constexpr const char* k_myproject_api_version = "3a8f...e291"; PYBIND11_MODULE(myproject, m) { m.attr("__api_version__") = k_myproject_api_version; // ... } ``` ### Template Block Names | Block | Contents | |-------|----------| | `prologue` | Includes, `namespace py = pybind11`, api_version constant, `PYBIND11_MODULE` open | | `api_version` | The `constexpr` api_version definition | | `api_version_registration` | The `m.attr("__api_version__")` call | | `enum` | Per-enum `py::enum_<>` block | | `enum_value` | Single `.value(...)` for one enum value | | `function_group` | Free function `m.def(...)` call | | `function_overloaded` | Single overloaded free function entry | | `class` | Full class block from `py::class_<>` to `;` | | `class_constructors` | All `.def(py::init<>())` calls | | `class_constructor_group` | Single `.def(py::init<>())` call | | `class_methods` | All method `.def(...)` calls | | `class_static_method_group` | Static method group | | `class_method_group` | Instance method group | | `class_overloaded_static_method` | Single overloaded static method | | `class_overloaded_method` | Single overloaded instance method | | `class_fields` | All `.def_readwrite()`/`.def_readonly()` calls | | `class_field` | Single field binding | | `epilogue` | Closing `}` | --- ## Built-in: `pyi` Generates Python type stub files (`.pyi`) for use alongside `pybind11` bindings. Use these to give IDEs and type checkers accurate signatures for your extension module. ```bash tsujikiri -i project.input.yml --target pyi mymodule.pyi ``` ### Type Mappings The pyi format ships with a comprehensive C++ → Python type mapping table: | C++ type | Python type | |----------|-------------| | `int`, `unsigned int`, `long`, `short`, `int8_t`, … `uint64_t`, `size_t` | `int` | | `float`, `double` | `float` | | `bool` | `bool` | | `std::string`, `const char *`, `char *`, `std::string_view` | `str` | | `void` | `None` | Types not in this table pass through unchanged (e.g. `Vec3` stays `Vec3`). ### Class Stubs ```python class Shape: """A 2D shape.""" scale: float def __init__(self, name: str) -> None: ... def area(self) -> float: ... def set_name(self, name: str) -> None: ... class Circle(Shape): def __init__(self, radius: float) -> None: ... def get_radius(self) -> float: ... ``` ### Static Methods and Overloads ```python class Calculator: @staticmethod def max(a: int, b: int) -> int: ... @overload def add(self, a: int, b: int) -> int: ... @overload def add(self, a: float, b: float) -> float: ... ``` ### Enum Stubs Enums are represented as `class Foo(int)` with typed class attributes: ```python class Color(int): """Colour enumeration.""" Red: Color Green: Color Blue: Color ``` ### API Version When `--embed-version` is used, the stub includes: ```python __api_version__: str ``` --- ## Jinja2 Template Context Reference The generator builds a plain-data context dict passed to the Jinja2 template. Templates iterate over this data directly. ### Top-Level Variables | Variable | Type | Description | |----------|------|-------------| | `module_name` | `string` | Binding module name (derived from input file name) | | `includes` | `list[string]` | All collected `#include` strings | | `enums` | `list[enum]` | Top-level module enums (emit=True) | | `function_groups` | `list[function_group]` | Free function groups (overloads grouped) | | `classes` | `list[class]` | All emittable classes in topological order (base before derived), including inner classes | | `api_version` | `string` | SHA-256 uid if `--embed-version`, else `""` | | `code_injections` | `list[injection]` | Module-level code injections | | `custom_data` | `dict` | Arbitrary user-defined data from `custom_data:` in the input YAML | ### Class Dict | Field | Type | Description | |-------|------|-------------| | `name` | `string` | Binding name (rename or original) | | `qualified_name` | `string` | Fully-qualified C++ name (e.g. `mylib::Circle`) | | `parts` | `list[string]` | Every component of the C++ path, e.g. `["mylib", "Circle"]` | | `namespaces` | `list[string]` | Namespace components only, e.g. `["mylib"]` | | `parent_class` | `string\|null` | Immediate enclosing class name when the class is an inner class, else `null` | | `attributes` | `list[string]` | Raw C++ attribute contents | | `doc` | `string` | Documentation string (from `[[tsujikiri::doc(...)]]`, else `""`) | | `bases` | `list[{qualified_name, access}]` | All base classes | | `public_bases` | `list[{qualified_name, short_name, access}]` | Public base classes only (emit=True) | | `base_name` | `string` | First public base's qualified name (empty if none) | | `base_short_name` | `string` | Last component of `base_name` (e.g. `Shape` from `mylib::Shape`) | | `variable_name` | `string` | camelCase variable name (used in some templates) | | `has_virtual_methods` | `bool` | Any virtual method present | | `is_abstract` | `bool` | Any pure virtual method present | | `copyable` | `bool\|null` | `null` = infer from C++ | | `movable` | `bool\|null` | `null` = infer from C++ | | `force_abstract` | `bool` | Suppress constructor binding | | `constructor_group` | `constructor_group` | All emittable constructors | | `method_groups` | `list[method_group]` | All emittable method groups | | `fields` | `list[field]` | All emittable fields | | `enums` | `list[enum]` | Nested class enums | | `code_injections` | `list[injection]` | Class-level code injections | **`public_bases` entry:** ``` { qualified_name: string, # e.g. "mylib::Shape" short_name: string, # last component, e.g. "Shape" access: string # "public" } ``` Use `public_bases` when generating pybind11 or pyi stubs where the class template argument list needs all public bases. Use `base_name` / `base_short_name` when only a single base is needed (e.g. LuaBridge3's `deriveClass`). ### Constructor Group ``` constructor_group = { is_overloaded: bool, constructors: [ { params: [{ name, original_name, type, raw_type, ownership, default }], overload_index: int, is_noexcept: bool, is_explicit: bool, code_injections: [injection] } ] } ``` ### Method Groups Each `method_group` represents one binding name (possibly overloaded): ``` method_group = { name: string, # binding name (after rename) is_overloaded: bool, is_static: bool, methods: [method] } method = { name: string, # binding name spelling: string, # original C++ method name (for &Class::spelling) parts: [string], # every component of the C++ path, e.g. ["ns","Cls","method"] namespaces: [string], # namespace components only, e.g. ["ns"] parent_class: string|null, # enclosing class name (always set for methods) params: [param], return_type: string, # mapped type (via map_type filter) raw_return_type: string, # unmapped C++ type return_ownership: string, # "none" | "cpp" | "script" allow_thread: bool, wrapper_code: string, # non-empty → use instead of &Class::method overload_kind: string, # "const" | "nonconst" | "overload" overload_separator: string, # "," or "" (for multi-line overload lists) is_const: bool, is_virtual: bool, is_pure_virtual: bool, is_noexcept: bool, overload_index: int, doc: string, # documentation string (from [[tsujikiri::doc(...)]], else "") attributes: [string], code_injections: [injection] } ``` ### Parameter Dict ``` param = { name: string, # binding name (after rename) original_name: string, # original C++ parameter name type: string, # mapped type raw_type: string, # unmapped C++ type spelling ownership: string, # "none" | "cpp" | "script" default: string|null # default expression if set, else null } ``` ### Field Dict ``` field = { name: string, # binding name (after rename) original_name: string, # original C++ member name (before any rename) parts: [string], # every component of the C++ path, e.g. ["ns","Cls","field"] namespaces: [string], # namespace components only, e.g. ["ns"] parent_class: string|null, # enclosing class name (always set for fields) type: string, # mapped type raw_type: string, # unmapped C++ type spelling is_const: bool, # true if the C++ field is const-qualified is_static: bool, # true if the C++ field is static read_only: bool, # true if const or forced read-only (via modify_field) doc: string, # documentation string (from [[tsujikiri::doc(...)]], else "") } ``` `original_name` is the raw C++ member name as clang parsed it. When a `[[tsujikiri::rename(...)]]` attribute or `rename_field` transform is applied, `name` changes to the binding-visible name but `original_name` stays unchanged. Templates that reference the C++ member (e.g. `&Class::memberName`) must use `original_name`, not `name`. ### Enum Dict ``` enum = { name: string, qualified_name: string, # e.g. "mylib::Color" parts: [string], # every component of the C++ path, e.g. ["mylib","Color"] namespaces: [string], # namespace components only, e.g. ["mylib"] parent_class: string|null, # enclosing class name when nested in a class, else null doc: string, # documentation string (from [[tsujikiri::doc(...)]], else "") attributes: [string], values: [ { name: string, # binding name (after rename) original_name: string, # original C++ enum member name (for C++ symbol reference) parts: [string], # every component, e.g. ["mylib","Color","kRed"] namespaces: [string], # namespace components only, e.g. ["mylib"] parent_class: string|null, # the enum name, e.g. "Color" number: string, # integer value as string doc: string, # documentation string (from [[tsujikiri::doc(...)]], else "") attributes: [string] } ] } ``` The `original_name` on enum values is the original C++ enumerator name. When a rename transform or `[[tsujikiri::rename(...)]]` is applied, `name` changes but `original_name` stays unchanged. Templates that reference the C++ enum (e.g. `EnumType::EnumeratorName`) must use `original_name`. ### Symbol Decomposition Fields Every symbol context dict (class, method, field, enum, enum value, free function) carries three fields that describe its position in the C++ namespace/class hierarchy: | Field | Type | Description | |-------|------|-------------| | `parts` | `list[string]` | All path components from root to this symbol, e.g. `["a","b","c","d","e"]` | | `namespaces` | `list[string]` | Only the C++ namespace components (no class names), e.g. `["a","b","c"]` | | `parent_class` | `string\|null` | Immediate enclosing class or enum name (`"d"`), or `null` when none | For `namespace a::b::c { struct d { int e; }; }`: | Symbol | `parts` | `namespaces` | `parent_class` | |--------|---------|--------------|----------------| | class `d` | `["a","b","c","d"]` | `["a","b","c"]` | `null` | | field `e` | `["a","b","c","d","e"]` | `["a","b","c"]` | `"d"` | | method `m` | `["a","b","c","d","m"]` | `["a","b","c"]` | `"d"` | | top-level enum `Color` | `["a","b","c","Color"]` | `["a","b","c"]` | `null` | | class-nested enum `d::Color` | `["a","b","c","d","Color"]` | `["a","b","c"]` | `"d"` | | enum value `Color::Red` | `["a","b","c","Color","Red"]` | `["a","b","c"]` | `"Color"` | | free function `f` | `["a","b","c","f"]` | `["a","b","c"]` | `null` | These fields always use original C++ names — renames applied via transforms or attributes do not affect them. ### Function Group Dict Same shape as `method_group` but at module level: ``` function_group = { name: string, is_overloaded: bool, functions: [ { name: string, spelling: string, # qualified_name (e.g. "mylib::computeArea") parts: [string], # every component, e.g. ["mylib","computeArea"] namespaces: [string], # namespace components only, e.g. ["mylib"] parent_class: string|null, # always null for free functions params: [param], return_type: string, raw_return_type: string, overload_kind: string, overload_separator: string, overload_index: int, is_noexcept: bool, doc: string, # documentation string (from [[tsujikiri::doc(...)]], else "") wrapper_code: string, attributes: [string] } ] } ``` ### Code Injection Dict ``` injection = { position: "beginning"|"end", code: string } ``` ### Built-in Jinja2 Filters | Filter | Signature | Description | |--------|-----------|-------------| | `map_type` | `type_str \| map_type` | Apply format's `type_mappings` to a type string | | `camel_to_snake` | `name \| camel_to_snake` | Convert `camelCase` or `CamelCase` to `snake_case` | | `snake_to_camel` | `name \| snake_to_camel(uppercase_first=True)` | Convert `snake_case` to `CamelCase` or `camelCase` | | `param_name` | `name \| param_name(name_key, index)` | Output the parameter name or an indexed placeholder as `name` or `p3` | | `param_pairs` | `params \| param_pairs(name_key, sep, type_key, joiner)` | Format parameter list as `name0: type0, name1: type1, ...` | | `code_at` | `injections \| code_at("beginning"\|"end")` | Extract and concatenate code injections at a position | **`param_pairs` example:** ```jinja {{ ctor.params | param_pairs('name', ': ', 'type', ', ') }} {# Produces: "x: number, y: number, z: number" #} ``` --- ## Creating a Custom Format ### Step 1 — Create the format file ```yaml # my_formats/myformat.output.yml format_name: myformat format_version: "1.0" description: "My custom binding format" language: cpp unsupported_types: - CFStringRef type_mappings: "std::string": "String" "int32_t": "int" template: | {%- block prologue %} // DO NOT EDIT - Auto-generated myformat bindings for {{ module_name }} {%- for inc in includes %} #include {{ inc }} {%- endfor %} void register_{{ module_name }}() { {%- endblock %} {%- for cls in classes %} {%- block class scoped %} bind_class<{{ cls.qualified_name }}>("{{ cls.name }}") {%- for ctor in cls.constructor_group.constructors %} .constructor<{{ ctor.params | map(attribute='raw_type') | join(', ') }}>() {%- endfor %} {%- for group in cls.method_groups %} {%- set method = group.methods[0] %} .method("{{ group.name | camel_to_snake }}", &{{ cls.qualified_name }}::{{ method.spelling }}) {%- endfor %} ; {%- endblock %} {%- endfor %} {%- block epilogue %} } {%- endblock %} ``` ### Step 2 — Run with the custom format ```bash tsujikiri -i project.input.yml --target myformat src/bindings.cpp -f ./my_formats/ ``` ### Step 3 — List available formats (includes custom) ```bash tsujikiri --list-formats -f ./my_formats/ # luabridge3 # luals # myformat # pybind11 # pyi ``` --- ## Extending a Built-in Format Use `template_extends` in `format_overrides` to create a Jinja2 child template that overrides specific blocks of a built-in format without duplicating the entire template. ```yaml # project.input.yml format_overrides: luabridge3: template_extends: | {% extends "luabridge3.tpl" %} {% block prologue %} // Copyright 2024 My Company. All rights reserved. {{ super() }} {% endblock %} {% block epilogue %} {{ super() }} // End of myproject bindings {% endblock %} ``` `{{ super() }}` renders the parent block's content at that position. **Example — override the class block to add a custom method to every class:** ```yaml format_overrides: luabridge3: template_extends: | {% extends "luabridge3.tpl" %} {% block class scoped %} {{ super() }} {# Note: super() already includes .endClass() — inject before it by overriding more specific blocks #} {% endblock %} {% block class_fields scoped %} {{ super() }} .addFunction("__type", +[]{ return "{{ cls.name }}"; }) {% endblock %} ``` Available parent template names (for `{% extends %}`): - `"luabridge3.tpl"` — the built-in LuaBridge3 template - `"luals.tpl"` — the built-in LuaLS template - `"pybind11.tpl"` — the built-in pybind11 template - `"pyi.tpl"` — the built-in Python type stub template --- ## See Also - [Transforms](transforms.md) — `add_type_mapping` and `inject_code` affect what the template sees - [Manifest and Versioning](manifest-and-versioning.md) — `embed_version` and the API version hash - [Input File Reference](input-file-reference.md) — `format_overrides`, `generation.includes`