tsujikiri Documentation
tsujikiri (辻斬り — “cut through C++ bindings”) parses C++ headers via libclang and generates binding code through a template-driven pipeline. Define what to expose, plug in a target format, and get ready-to-compile bindings.
What tsujikiri Does
tsujikiri reads a YAML configuration file (.input.yml) that describes which C++ headers to parse, which classes and methods to expose, and how to transform the declarations for the target binding system.
It parses each header using libclang, building an Intermediate Representation (IR) — a pure Python data model of all classes, methods, fields, constructors, enums, and free functions. The IR is then processed through three configurable phases — filtering, attribute processing, and transforms — before being rendered via a Jinja2 template into the final output.
Built-in support for LuaBridge3 (Lua bindings), Lua Language Server (LuaLS type stubs), pybind11 (Python bindings), and Python type stubs (.pyi). Custom formats are first-class citizens — define a .output.yml file with your Jinja2 template and point tsujikiri at it.
Pipeline
C++ Header (.hpp)
│
▼ libclang parse
Intermediate Representation (IR)
│
├──▶ FilterEngine namespaces / classes / methods / fields
│ ↳ filtering.md
│
├──▶ AttributeProcessor [[tsujikiri::skip]], [[tsujikiri::keep]], [[tsujikiri::rename(...)]], [[tsujikiri::doc(...)]], etc.
│ ↳ attributes.md
│
├──▶ TransformPipeline rename, suppress, inject, remap, modify
│ ↳ transforms.md
│
▼ Jinja2 template render
Target Binding Code (.cpp / .lua)
│
└──▶ Manifest (optional) API snapshot for versioning and compat checks
↳ manifest-and-versioning.md
Each phase is independently configurable per input file and per output format. All configuration lives in a single .input.yml file.
Quick Example
Header (vec3.hpp):
namespace myproject {
class Vec3 {
public:
Vec3() = default;
Vec3(float x, float y, float z) : x_(x), y_(y), z_(z) {}
float length() const;
float dot(const Vec3& other) const;
public:
float x_ = 0.0f, y_ = 0.0f, z_ = 0.0f;
};
}
Config (myproject.input.yml):
source:
path: vec3.hpp
parse_args: ["-std=c++17"]
filters:
namespaces: ["myproject"]
classes:
whitelist: ["Vec3"]
constructors:
include: true
transforms:
- stage: modify_field
class: Vec3
field: "(.+)_$"
field_is_regex: true
rename: "\\1"
generation:
includes: ['"vec3.hpp"']
Command:
tsujikiri -i myproject.input.yml --target luabridge3 src/bindings.cpp
Output (excerpt):
void register_myproject(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginNamespace("myproject")
.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()
.endNamespace();
}
See Getting Started for the full walkthrough.
Installation
Using pip:
pip install tsujikiri
Using uv:
uv pip install tsujikiri
Requirements: Python ≥ 3.12.
License
MIT — see LICENSE.