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 should be able to generate fully working bindings while complex libraries may require some customization.

As an example, there used to be hand-crafted Ruby bindings for OpenCV. However, they were based on the C API which was subsequently remove by the OpenCV project. ruby-bindgen was used to create new 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.

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_bindings.md" "C Bindings"
  click C "cpp/cpp_bindings.md" "C++ Bindings"
  click E "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. In addition, specify a project name, the input path to header files and the output path for generated bindings.

For example:

project: my_extension
input: /path/to/headers
output: /path/to/output
format: Rice     # or FFI/CMake

match:
  - "**/*.hpp"   # or "**/*.h" for C headers

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 rice-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, structs, or enums 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.