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
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_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, 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 >= 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.