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. 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.13.0.tar.gz (65.8 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.13.0-py3-none-any.whl (76.9 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for tsujikiri-0.13.0.tar.gz
Algorithm Hash digest
SHA256 7c6c55035017d50a1e45d7257aef258686f575cab35dae27ae9d4a82396dc224
MD5 42b3cf143785c69122be2bab6ef17de6
BLAKE2b-256 c747ac25c365a299b1a1caf7ddad7aa260be2f5a00f616c4ea0985216711afd9

See more details on using hashes here.

Provenance

The following attestation bundles were made for tsujikiri-0.13.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.13.0-py3-none-any.whl.

File metadata

  • Download URL: tsujikiri-0.13.0-py3-none-any.whl
  • Upload date:
  • Size: 76.9 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.13.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b38e63f47eb3e93fdf69b9d4e8575dc3ab8893fe93d04f363bf17694c8c0522c
MD5 4dc3d604847e17b1e034af9ac6acaa1c
BLAKE2b-256 f3bcb31c4fe34f857bd3baa70fdc92072155dc7442c1824e4e8c6a3b644a6305

See more details on using hashes here.

Provenance

The following attestation bundles were made for tsujikiri-0.13.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