Skip to main content

辻斬り Generic C++ Bindings Generator

Project description

Backdrop

Tests Type Check Coverage Documentation PyPI version Downloads Python License: MIT

tsujikiri — 辻斬り

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.


How It Works

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.


Documentation

Full documentation is available at tsujikiri.readthedocs.io.


Installation

Using pip:

pip install tsujikiri

Using uv:

uv pip install tsujikiri

Requirements: Python ≥ 3.12

Clang Version

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.


Quick Start

1. Write an input config

# 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>"]

2. Generate bindings

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

3. Example output (LuaBridge3)

Given 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();
}

Built-in Formats

luabridge3

Generates C++ registration code for LuaBridge3.

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

Handles: classes, constructors, instance/static methods, overloaded methods, properties, enums, free functions, inheritance.

luals

Generates Lua Language Server annotation stubs.

tsujikiri -i project.input.yml --target luals types/myproject.lua

Emits ---@class, ---@field, ---@param, ---@return annotations with C++→Lua type mappings.

pybind11

Generates C++ registration code for pybind11.

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

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

pyi

Generates Python type stub files (.pyi) for use alongside pybind11 bindings.

tsujikiri -i project.input.yml --target pyi mymodule.pyi

Emits Python-typed stubs with @overload, @staticmethod, class inheritance, enum stubs as class Foo(int), and C++→Python type mappings.


Custom Formats

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 template

Point tsujikiri at your format directory:

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

CLI Reference

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.

Development

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

The test suite covers parsing, filtering, transforms, generation, CLI integration, and end-to-end compilation with LuaBridge3.


Coverage

Coverage tree


License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tsujikiri-0.18.0.tar.gz (70.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tsujikiri-0.18.0-py3-none-any.whl (81.7 kB view details)

Uploaded Python 3

File details

Details for the file tsujikiri-0.18.0.tar.gz.

File metadata

  • Download URL: tsujikiri-0.18.0.tar.gz
  • Upload date:
  • Size: 70.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tsujikiri-0.18.0.tar.gz
Algorithm Hash digest
SHA256 bbe34995f4019d35c7f7618c68513f5ac82e955abd241e0365cbe5055ab42624
MD5 e908d6ac73643643f4c9574beb74901b
BLAKE2b-256 bb4c471fd1b36631f38637ea7318da7ef31116206291f3450fd7b6b1356b5530

See more details on using hashes here.

Provenance

The following attestation bundles were made for tsujikiri-0.18.0.tar.gz:

Publisher: release.yml on kunitoki/tsujikiri

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tsujikiri-0.18.0-py3-none-any.whl.

File metadata

  • Download URL: tsujikiri-0.18.0-py3-none-any.whl
  • Upload date:
  • Size: 81.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tsujikiri-0.18.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c6c03c63d1705b37f7e5813b8ee2f554a833d624b54a666eb5be3b918cd48503
MD5 f9b1f99f9e570cefa7dcb31a5a9090fc
BLAKE2b-256 94db3255d0658915a8723e2601869a391b941d633f0bec981781e60f10f104ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for tsujikiri-0.18.0-py3-none-any.whl:

Publisher: release.yml on kunitoki/tsujikiri

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page