Skip to content

ruby-bindgen

Wrapping C and C++ libraries by hand is a long, arduous task. For large, complex libraries it can take months. As a result, many C/C++ libraries are either never exposed to Ruby or their bindings quickly become outdated, especially in scientific and technical domains.

ruby-bindgen and its ecosystem solve this problem by automating binding generation. For simpler libraries, it can generate ready-to-use bindings. For more complex libraries, it can also generate bindings but some customizations may be needed.

As an example, there are older, hand-crafted Ruby bindings for OpenCV. However, they are based on the C API which was subsequently removed by the OpenCV project. ruby-bindgen was used to create new OpenCV bindings based on the new C++ API. The bindings wrap over 1,000 C++ classes and almost 10,000 method calls. Imagine having to do that by hand!

Ecosystem

ruby-bindgen is part of the C/C++ to Ruby toolchain.

flowchart TD
  H["C/C++ headers"] --> CL["ffi-clang"]
  CL --> RB["ruby-bindgen"]
  RB --> F["C (ffi)"]
  RB --> R["C++ (Rice)"]
  RB --> C["Build (CMake)"]

  click CL "https://github.com/ioquatix/ffi-clang" "ffi-clang"
  click RB "https://github.com/ruby-rice/ruby-bindgen" "ruby-bindgen"
  click R "https://github.com/ruby-rice/rice" "Rice"
  click F "https://github.com/ffi/ffi" "FFI"
  click C "https://cmake.org/" "CMake"

The components of the toolchain include:

  • ffi-clang - exposes libclang parsing APIs to Ruby.
  • ruby-bindgen - generates bindings.
  • FFI - enables direct C library calls from Ruby without compiling a C extension.
  • Rice - handles C++/Ruby type conversion and native extension integration.
  • CMake - builds generated Rice wrappers into loadable extension binaries.

Prerequisites

  • Ruby 3.2 or later
  • libclang (provided by LLVM/Clang)

Installation

To install ruby-bindgen run the following command:

gem install ruby-bindgen

Getting Started

ruby-bindgen is driven by a configuration file. To get started, first decide what type of library you are wrapping:

flowchart TD
  A{"C or C++?"}
  A -->|C| B["FFI"]
  A -->|C++| C["Rice"]
  C --> D{"CMake or<br/>extconf.rb?"}
  D -->|CMake| E["CMake"]
  D -->|extconf.rb| F["Done"]
  B --> F
  E --> F

  click B "c/c_bindings.md" "C Bindings"
  click C "cpp/cpp_bindings.md" "C++ Bindings"
  click E "cmake/cmake_bindings.md" "CMake Bindings"
  click D "https://ruby-rice.github.io/4.x/packaging/extconf/" "Rice extconf.rb packaging"

If a library provides both C and C++ APIs, use the C API! It is usually simpler to wrap and maintain and does not require users to compile extensions.

Once you have decided the format, create a simple configuration file and set its format field to FFI, Rice or CMake.

  • output is always required
  • input is required for FFI and Rice; for CMake it defaults to output
  • project is required for FFI and optional for Rice and CMake

For example, a minimal Rice configuration looks like:

project: my_extension
input: /path/to/headers
output: /path/to/output
format: Rice

match:
  - "**/*.hpp"   # use "**/*.h" for C headers in FFI configs

clang:
  args:
    - -I/path/to/includes
    - -xc++      # omit for C libraries
See Configuration for all options.

For much more details, jump to the documentation page for each format:

Format Next Step
FFI C Bindings
Rice C++ Bindings
CMake CMake Bindings

Finally generate bindings by running the command:

ruby-bindgen /path/to/bindings.yaml

Naming Conventions

ruby-bindgen follows Ruby naming conventions for both C and C++ bindings:

  • Class/Module names: UpperCamelCase
  • Constants: UPPER_CASE
  • Methods/Functions: snake_case
  • Enum values: snake_case symbols (FFI) or scoped constants (Rice)

In addition, methods that return boolean values have ? appended to their names and is_ removed if present. For example, is_open becomes open?.

Customization

Out of the box, ruby-bindgen applies sensible defaults and heuristics. For most libraries you will need to fine-tune the output. The configuration file provides several knobs:

  • Symbol filtering — skip functions, classes, enums, typedefs, unions, or variables by name or regex pattern. Useful for internal APIs, linker-error symbols, or platform-specific code.
  • Symbol overrides (FFI) — replace a generated function signature when the heuristics pick the wrong type (e.g., int:bool, ulong:size_t).
  • Version guards — wrap symbols in #if VERSION >= N preprocessor guards so bindings compile against multiple library versions.
  • Name mappings — override generated Ruby class and method names with exact strings or regex patterns with capture-group substitution.
  • Export macros — only include functions marked with specific visibility macros (e.g., CV_EXPORTS), preventing linker errors from internal symbols.
  • Module naming (FFI) — set the Ruby module name, including nested modules like Proj::Api.

Packaging

For Rice (C++) bindings, see the Rice Packaging documentation for how to package your extension as a gem.