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.
outputis always requiredinputis required forFFIandRice; forCMakeit defaults tooutputprojectis required forFFIand optional forRiceandCMake
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
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_casesymbols (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 >= Npreprocessor 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.