Output Formats

Home > 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:

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.

tsujikiri -i project.input.yml --target luabridge3 src/lua_bindings.cpp

Generated Function

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<QualifiedName>("BindingName"). Classes with a C++ base class (first public base) use .deriveClass<Derived, Base>("BindingName").

Tsujikiri applies a topological sort so base classes are always registered before derived classes.

// Base registered first:
.beginClass<mylib::Shape>("Shape")
    .addFunction("get_name", &mylib::Shape::getName)
.endClass()

// Derived registered after:
.deriveClass<mylib::Circle, mylib::Shape>("Circle")
    .addFunction("get_radius", &mylib::Circle::getRadius)
.endClass()

Constructor Registration

.addConstructor<void (*)(double)>()         // single-arg constructor
.addConstructor<void (*)(float, float)>()   // overloaded constructor

Multiple constructors each get their own .addConstructor<>() call.

Method Registration

// 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:

// Different parameter types → luabridge::overload<>
.addFunction("resize",
    luabridge::overload<double>(&mylib::Circle::resize),
    luabridge::overload<double, double>(&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

.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:

// 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:

.beginNamespace("Color")
    .addProperty("Red",   +[] { return static_cast<std::underlying_type_t<mylib::Color>>(mylib::Color::kRed); })
    .addProperty("Green", +[] { return static_cast<std::underlying_type_t<mylib::Color>>(mylib::Color::kGreen); })
    .addProperty("Blue",  +[] { return static_cast<std::underlying_type_t<mylib::Color>>(mylib::Color::kBlue); })
.endNamespace()

When an enum value is renamed (e.g. kRedRed), the property name uses the binding name and the C++ reference uses the original C++ name:

.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.

// 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<ns::Vec3>("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):

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)

// DO NOT EDIT - Auto-generated LuaBridge3 bindings for combined by tsujikiri
extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
} // extern "C"

#include <LuaBridge/LuaBridge.h>
#include <utility>
#include "combined.hpp"

void register_combined(lua_State* L)
{
  luabridge::getGlobalNamespace(L)
    .beginNamespace("combined")
      .beginNamespace("Color")
        .addProperty("Red",   +[] { return static_cast<std::underlying_type_t<mylib::Color>>(mylib::Color::Red); })
        .addProperty("Green", +[] { return static_cast<std::underlying_type_t<mylib::Color>>(mylib::Color::Green); })
        .addProperty("Blue",  +[] { return static_cast<std::underlying_type_t<mylib::Color>>(mylib::Color::Blue); })
      .endNamespace()
      .addFunction("compute_area",
          luabridge::overload<double>(&mylib::computeArea),
          luabridge::overload<double, double>(&mylib::computeArea)
      )
      .beginClass<mylib::Shape>("Shape")
        .addConstructor<void (*)(const char *)>()
        .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<mylib::Circle, mylib::Shape>("Circle")
        .addConstructor<void (*)(double)>()
        .addFunction("perimeter", &mylib::Circle::perimeter)
        .addFunction("get_radius", &mylib::Circle::getRadius)
        .addFunction("set_radius", &mylib::Circle::setRadius)
        .addFunction("resize",
            luabridge::overload<double>(&mylib::Circle::resize),
            luabridge::overload<double, double>(&mylib::Circle::resize)
        )
        .addProperty("radius", &mylib::Circle::radius_)
      .endClass()
      .beginClass<mylib::Calculator>("Calculator")
        .addConstructor<void (*)(void)>()
        .addFunction("add",
            luabridge::overload<int, int>(&mylib::Calculator::add),
            luabridge::overload<double, double>(&mylib::Calculator::add)
        )
        .addStaticFunction("max",
            luabridge::overload<int, int>(&mylib::Calculator::max),
            luabridge::overload<double, double>(&mylib::Calculator::max)
        )
        .addFunction("get_value", &mylib::Calculator::getValue)
        .addFunction("set_value", &mylib::Calculator::setValue)
      .endClass()
    .endNamespace();
}

Built-in: luals

Generates Lua Language Server annotation stubs. Use alongside the luabridge3 output to provide IDE auto-completion and type checking.

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

---@class Shape
---@field scale number
local Shape = {}

---@param name string
---@return Shape
function Shape.new(name) end

Instance Methods

---@return number
function Shape:area() end

---@param name string
---@return nil
function Shape:set_name(name) end

Static Methods

---@param a integer
---@param b integer
---@return integer
function Calculator.max(a, b) end

Overloads

-- 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:

---@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

---@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.

tsujikiri -i project.input.yml --target pybind11 src/py_bindings.cpp

Generated Module

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "myproject.hpp"

namespace py = pybind11;

PYBIND11_MODULE(myproject, m)
{
  // ... all bindings ...
}

Class Registration

Plain classes use py::class_<QualifiedName>(m, "BindingName"). Classes with public base classes use py::class_<Derived, Base>(m, "BindingName"). Multiple base classes are listed in order.

py::class_<mylib::Shape>(m, "Shape", "A 2D shape")
    .def(py::init<const char *>())
    .def("area", &mylib::Shape::area)
    .def_readwrite("scale", &mylib::Shape::scale_);

py::class_<mylib::Circle, mylib::Shape>(m, "Circle")
    .def(py::init<double>())
    .def("get_radius", &mylib::Circle::getRadius);

Method Registration

// 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

.def("resize",
    py::overload_cast<double>(&mylib::Circle::resize),
    py::arg("factor"))
.def("resize",
    py::overload_cast<double, double>(&mylib::Circle::resize),
    py::arg("width"), py::arg("height"))

Field Registration

.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:

// 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

py::enum_<mylib::Color>(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. kRedRed), the .value(...) string uses the binding name and the C++ enumerator reference uses the original C++ name:

.value("Red", mylib::Color::kRed)
//     ^^^^^ binding name  ^^^^ original C++ enumerator name

API Version Embedding

When --embed-version is passed:

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.

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

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

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:

class Color(int):
    """Colour enumeration."""
    Red: Color
    Green: Color
    Blue: Color

API Version

When --embed-version is used, the stub includes:

__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:

{{ ctor.params | param_pairs('name', ': ', 'type', ', ') }}
{# Produces: "x: number, y: number, z: number" #}

Creating a Custom Format

Step 1 — Create the format file

# 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

tsujikiri -i project.input.yml --target myformat src/bindings.cpp -f ./my_formats/

Step 3 — List available formats (includes custom)

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.

# 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:

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