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
|
|
|
|---|---|---|
Where applied |
Jinja2 template rendering only (via |
IR itself, before generation |
Affects manifest |
No |
Yes |
Affects overload kind detection |
No |
Yes |
Use for |
Cosmetic renaming in output (C++ |
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. kRed → Red), 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 |
|---|---|---|
|
|
Addition |
|
|
Subtraction |
|
|
Negation |
|
|
Multiplication |
|
|
Division |
|
|
Modulo |
|
|
Equality |
|
|
Less than |
|
|
Less or equal |
|
|
Call |
|
|
Bitwise AND |
|
|
Bitwise OR |
|
|
Bitwise XOR |
|
|
Bitwise NOT |
|
|
Bitwise left shift |
|
|
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 |
|---|---|
|
Includes, extern “C”, headers, api_version constant, function open |
|
The |
|
The |
|
Per-enum |
|
Single |
|
Non-overloaded or overloaded free function |
|
Single overloaded free function entry |
|
Full class block from |
|
All |
|
Single |
|
All method |
|
Static method group |
|
Instance method group |
|
Single overloaded static method cast |
|
Single overloaded instance method cast |
|
All |
|
Single |
|
Nested enum namespace blocks |
|
Single enum namespace block |
|
Single enum value |
|
|
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 |
|---|---|
|
|
|
|
|
|
|
|
|
|
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. kRed → Red), 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 |
|---|---|
|
Includes, |
|
The |
|
The |
|
Per-enum |
|
Single |
|
Free function |
|
Single overloaded free function entry |
|
Full class block from |
|
All |
|
Single |
|
All method |
|
Static method group |
|
Instance method group |
|
Single overloaded static method |
|
Single overloaded instance method |
|
All |
|
Single field binding |
|
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 |
|---|---|
|
|
|
|
|
|
|
|
|
|
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 |
|---|---|---|
|
|
Binding module name (derived from input file name) |
|
|
All collected |
|
|
Top-level module enums (emit=True) |
|
|
Free function groups (overloads grouped) |
|
|
All emittable classes in topological order (base before derived), including inner classes |
|
|
SHA-256 uid if |
|
|
Module-level code injections |
|
|
Arbitrary user-defined data from |
Class Dict
Field |
Type |
Description |
|---|---|---|
|
|
Binding name (rename or original) |
|
|
Fully-qualified C++ name (e.g. |
|
|
Every component of the C++ path, e.g. |
|
|
Namespace components only, e.g. |
|
|
Immediate enclosing class name when the class is an inner class, else |
|
|
Raw C++ attribute contents |
|
|
Documentation string (from |
|
|
All base classes |
|
|
Public base classes only (emit=True) |
|
|
First public base’s qualified name (empty if none) |
|
|
Last component of |
|
|
camelCase variable name (used in some templates) |
|
|
Any virtual method present |
|
|
Any pure virtual method present |
|
|
|
|
|
|
|
|
Suppress constructor binding |
|
|
All emittable constructors |
|
|
All emittable method groups |
|
|
All emittable fields |
|
|
Nested class enums |
|
|
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 |
|---|---|---|
|
|
All path components from root to this symbol, e.g. |
|
|
Only the C++ namespace components (no class names), e.g. |
|
|
Immediate enclosing class or enum name ( |
For namespace a::b::c { struct d { int e; }; }:
Symbol |
|
|
|
|---|---|---|---|
class |
|
|
|
field |
|
|
|
method |
|
|
|
top-level enum |
|
|
|
class-nested enum |
|
|
|
enum value |
|
|
|
free function |
|
|
|
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 |
|---|---|---|
|
|
Apply format’s |
|
|
Convert |
|
|
Convert |
|
|
Output the parameter name or an indexed placeholder as |
|
|
Format parameter list as |
|
|
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
Transforms —
add_type_mappingandinject_codeaffect what the template seesManifest and Versioning —
embed_versionand the API version hashInput File Reference —
format_overrides,generation.includes