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.