Cut through C++ bindings
tsujikiri parses C++ headers via libclang, filters by namespace and pattern, transforms symbols — rename, remap types, inject code — then renders ready-to-compile bindings through a Jinja2 template. Precise control, zero boilerplate.
Built-in support for LuaBridge3 (Lua bindings), LuaLS (Lua Language Server annotations), pybind11 (Python bindings), and Python type stubs (.pyi). Custom formats are first-class.
C++ Header (.hpp)
│
▼ libclang
Intermediate Representation (IR)
│
├─▶ FilterEngine suppress classes / methods / fields by pattern
│
├─▶ Transform Pipeline rename, inject, remap types
│
▼ Jinja2 templates
Target Binding Code
Each phase is independently configurable per input file and per output format.
Full documentation is available at tsujikiri.readthedocs.io.
Using pip:
pip install tsujikiriUsing uv:
uv pip install tsujikiriRequirements: Python ≥ 3.12
By default tsujikiri installs libclang-ng>=19, which resolves to the latest available version (see https://github.com/kunitoki/libclang-ng for more information). To pin a specific Clang release use an extra:
# pip
pip install "tsujikiri[clang19]"
pip install "tsujikiri[clang20]"
pip install "tsujikiri[clang21]"
pip install "tsujikiri[clang22]"
# uv
uv pip install "tsujikiri[clang21]"Only one clangXX extra may be active at a time — they are mutually exclusive.
# myproject.input.yml
source:
path: myproject.hpp
parse_args: ["-std=c++17"]
include_paths: ["/usr/local/include"]
filters:
namespaces: ["myproject"]
classes:
whitelist: ["Vec3", "Matrix4", "Camera"]
constructors:
include: true
generation:
includes: ["<myproject.hpp>"]# Print to stdout
tsujikiri -i myproject.input.yml --target luabridge3 -
# Write to file
tsujikiri -i myproject.input.yml --target luabridge3 bindings.cpp
# Generate multiple outputs in one pass
tsujikiri -i myproject.input.yml \
--target luabridge3 bindings.cpp \
--target pybind11 py_bindings.cpp \
--target pyi mymodule.pyi
# Dry-run: parse and filter, print summary
tsujikiri -i myproject.input.yml --target luabridge3 - --dry-run
# List available formats
tsujikiri --list-formatsGiven a header with a Vec3 class, tsujikiri emits:
#include <myproject.hpp>
#include <LuaBridge/LuaBridge.h>
void register_myproject(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<myproject::Vec3>("Vec3")
.addConstructor<void(*)(float, float, float)>()
.addFunction("length", &myproject::Vec3::length)
.addFunction("dot", &myproject::Vec3::dot)
.addProperty("x", &myproject::Vec3::x)
.addProperty("y", &myproject::Vec3::y)
.addProperty("z", &myproject::Vec3::z)
.endClass();
}Generates C++ registration code for LuaBridge3.
tsujikiri -i project.input.yml --target luabridge3 bindings/lua_bindings.cppHandles: classes, constructors, instance/static methods, overloaded methods, properties, enums, free functions, inheritance.
Generates Lua Language Server annotation stubs.
tsujikiri -i project.input.yml --target luals types/myproject.luaEmits ---@class, ---@field, ---@param, ---@return annotations with C++→Lua type mappings.
Generates C++ registration code for pybind11.
tsujikiri -i project.input.yml --target pybind11 src/py_bindings.cppHandles: classes with multiple inheritance, constructors, instance/static methods, overloaded methods (via py::overload_cast), read-write/read-only properties, enums (via py::enum_), free functions, doc strings.
Generates Python type stub files (.pyi) for use alongside pybind11 bindings.
tsujikiri -i project.input.yml --target pyi mymodule.pyiEmits Python-typed stubs with @overload, @staticmethod, class inheritance, enum stubs as class Foo(int), and C++→Python type mappings.
Create a myformat.output.yml alongside your templates:
format_name: "myformat"
format_version: "1.0"
description: "My custom binding format"
type_mappings:
"std::string": "String"
"int32_t": "int"
unsupported_types:
- "CFStringRef"
template:
{%- block prologue -%}
# DO NOT EDIT - Auto-generated Python stubs for {{ module_name }} by tsujikiri
from __future__ import annotations
from typing import overload
{{ code_injections | code_at("beginning") }}
{%- endblock %}
# ... finish the templatePoint tsujikiri at your format directory:
tsujikiri -i project.input.yml --target myformat out/bindings.cpp -f ./my_formats/usage: tsujikiri [-h] [--input FILE] [--target FORMAT FILE] [--formats-dir DIR] [--list-formats] [--dry-run] [--manifest-file FILE]
[--check-compat] [--embed-version] [--trace-transforms] [--dump-ir [FILE]] [--validate-config] [--verbose] [--api-version VERSION]
辻斬り — Generic C++ Binding Generator
options:
-h, --help show this help message and exit
--input FILE, -i FILE
Input config YAML (e.g. myproject.input.yml)
--target FORMAT FILE, -t FORMAT FILE
Output target: FORMAT is a built-in name (luabridge3) or path to .output.yml; FILE is the output path ('-' for stdout). Repeatable.
--formats-dir DIR, -f DIR
Additional directory to search for .output.yml format files (repeatable)
--list-formats Print available built-in output formats and exit
--dry-run Parse and filter but do not generate output; print a summary instead
--manifest-file FILE, -m FILE
Write API manifest JSON to FILE; if FILE already exists, compare with new manifest
--check-compat Exit 1 if --manifest-file exists and breaking API changes are detected
--embed-version Embed the API version hash in the generated code (template must support it)
--trace-transforms Print which transform stages ran and on what entities to stderr
--dump-ir [FILE] Dump the post-transform IR as JSON to FILE (default: stdout when flag is given without FILE)
--validate-config Validate the input config YAML (regex patterns, transform stage names) and exit
--verbose, -v Enable verbose output during parsing (currently only applies to Clang diagnostics)
--api-version VERSION
Target API version (semver). Entities with api_since > VERSION or api_until <= VERSION are excluded.
# Install task runner and sync dependencies
pip install just uv
just sync
# Run tests
just test
# Run tests with coverage
just coverage
# Build wheel
just buildThe test suite covers parsing, filtering, transforms, generation, CLI integration, and end-to-end compilation with LuaBridge3.
MIT — see LICENSE.
