ruby-bindgen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +7 -0
  3. data/LICENSE +25 -0
  4. data/README.md +68 -0
  5. data/Rakefile +8 -0
  6. data/bin/ruby-bindgen +133 -0
  7. data/docs/architecture.md +238 -0
  8. data/docs/c/c_bindings.md +65 -0
  9. data/docs/c/constants.md +21 -0
  10. data/docs/c/customizing.md +19 -0
  11. data/docs/c/filtering.md +24 -0
  12. data/docs/c/getting_started.md +56 -0
  13. data/docs/c/library_loading.md +53 -0
  14. data/docs/c/output.md +96 -0
  15. data/docs/c/types.md +61 -0
  16. data/docs/c/version_guards.md +105 -0
  17. data/docs/cmake/cmake_bindings.md +19 -0
  18. data/docs/cmake/filtering.md +26 -0
  19. data/docs/cmake/getting_started.md +52 -0
  20. data/docs/cmake/output.md +110 -0
  21. data/docs/configuration.md +351 -0
  22. data/docs/contributing.md +68 -0
  23. data/docs/cpp/buffers.md +24 -0
  24. data/docs/cpp/classes.md +139 -0
  25. data/docs/cpp/cpp_bindings.md +29 -0
  26. data/docs/cpp/customizing.md +124 -0
  27. data/docs/cpp/enums.md +42 -0
  28. data/docs/cpp/filtering.md +35 -0
  29. data/docs/cpp/getting_started.md +80 -0
  30. data/docs/cpp/iterators.md +94 -0
  31. data/docs/cpp/operators.md +170 -0
  32. data/docs/cpp/output.md +125 -0
  33. data/docs/cpp/templates.md +114 -0
  34. data/docs/examples.md +133 -0
  35. data/docs/index.md +133 -0
  36. data/docs/prior_art.md +37 -0
  37. data/docs/troubleshooting.md +243 -0
  38. data/docs/type_spelling.md +200 -0
  39. data/docs/updating_bindings.md +55 -0
  40. data/docs/version_guards.md +69 -0
  41. data/lib/ruby-bindgen/config.rb +63 -0
  42. data/lib/ruby-bindgen/generators/cmake/cmake.rb +171 -0
  43. data/lib/ruby-bindgen/generators/cmake/directory.erb +29 -0
  44. data/lib/ruby-bindgen/generators/cmake/guard.rb +55 -0
  45. data/lib/ruby-bindgen/generators/cmake/presets.erb +232 -0
  46. data/lib/ruby-bindgen/generators/cmake/project.erb +89 -0
  47. data/lib/ruby-bindgen/generators/ffi/callback.erb +1 -0
  48. data/lib/ruby-bindgen/generators/ffi/constant.erb +1 -0
  49. data/lib/ruby-bindgen/generators/ffi/enum_constant_decl.erb +1 -0
  50. data/lib/ruby-bindgen/generators/ffi/enum_decl.erb +4 -0
  51. data/lib/ruby-bindgen/generators/ffi/enum_decl_anonymous.erb +4 -0
  52. data/lib/ruby-bindgen/generators/ffi/ffi.rb +687 -0
  53. data/lib/ruby-bindgen/generators/ffi/field_decl.erb +1 -0
  54. data/lib/ruby-bindgen/generators/ffi/function.erb +5 -0
  55. data/lib/ruby-bindgen/generators/ffi/library.erb +39 -0
  56. data/lib/ruby-bindgen/generators/ffi/project.erb +18 -0
  57. data/lib/ruby-bindgen/generators/ffi/struct.erb +6 -0
  58. data/lib/ruby-bindgen/generators/ffi/translation_unit.erb +9 -0
  59. data/lib/ruby-bindgen/generators/ffi/typedef_decl.erb +1 -0
  60. data/lib/ruby-bindgen/generators/ffi/union.erb +6 -0
  61. data/lib/ruby-bindgen/generators/ffi/variable.erb +1 -0
  62. data/lib/ruby-bindgen/generators/ffi/version.erb +9 -0
  63. data/lib/ruby-bindgen/generators/ffi/version_method.erb +5 -0
  64. data/lib/ruby-bindgen/generators/generator.rb +52 -0
  65. data/lib/ruby-bindgen/generators/rice/auto_generated_base_class.erb +5 -0
  66. data/lib/ruby-bindgen/generators/rice/class.erb +31 -0
  67. data/lib/ruby-bindgen/generators/rice/class_template.erb +9 -0
  68. data/lib/ruby-bindgen/generators/rice/class_template_specialization.erb +10 -0
  69. data/lib/ruby-bindgen/generators/rice/constant.erb +9 -0
  70. data/lib/ruby-bindgen/generators/rice/constructor.erb +1 -0
  71. data/lib/ruby-bindgen/generators/rice/conversion_function.erb +4 -0
  72. data/lib/ruby-bindgen/generators/rice/cxx_iterator_method.erb +1 -0
  73. data/lib/ruby-bindgen/generators/rice/cxx_method.erb +6 -0
  74. data/lib/ruby-bindgen/generators/rice/enum_constant_decl.erb +7 -0
  75. data/lib/ruby-bindgen/generators/rice/enum_decl.erb +6 -0
  76. data/lib/ruby-bindgen/generators/rice/field_decl.erb +8 -0
  77. data/lib/ruby-bindgen/generators/rice/function.erb +6 -0
  78. data/lib/ruby-bindgen/generators/rice/function_pointer.rb +68 -0
  79. data/lib/ruby-bindgen/generators/rice/incomplete_class.erb +3 -0
  80. data/lib/ruby-bindgen/generators/rice/iterator_alias.erb +1 -0
  81. data/lib/ruby-bindgen/generators/rice/iterator_collector.rb +159 -0
  82. data/lib/ruby-bindgen/generators/rice/namespace.erb +5 -0
  83. data/lib/ruby-bindgen/generators/rice/non_member_operator_binary.erb +4 -0
  84. data/lib/ruby-bindgen/generators/rice/non_member_operator_inspect.erb +6 -0
  85. data/lib/ruby-bindgen/generators/rice/non_member_operator_unary.erb +4 -0
  86. data/lib/ruby-bindgen/generators/rice/operator[].erb +4 -0
  87. data/lib/ruby-bindgen/generators/rice/project.cpp.erb +18 -0
  88. data/lib/ruby-bindgen/generators/rice/project.hpp.erb +13 -0
  89. data/lib/ruby-bindgen/generators/rice/reference_qualifier.rb +495 -0
  90. data/lib/ruby-bindgen/generators/rice/rice.rb +1724 -0
  91. data/lib/ruby-bindgen/generators/rice/rice_include.hpp.erb +7 -0
  92. data/lib/ruby-bindgen/generators/rice/signature_builder.rb +230 -0
  93. data/lib/ruby-bindgen/generators/rice/template_resolver.rb +585 -0
  94. data/lib/ruby-bindgen/generators/rice/translation_unit.cpp.erb +40 -0
  95. data/lib/ruby-bindgen/generators/rice/translation_unit.hpp.erb +7 -0
  96. data/lib/ruby-bindgen/generators/rice/translation_unit.ipp.erb +3 -0
  97. data/lib/ruby-bindgen/generators/rice/type_index.rb +117 -0
  98. data/lib/ruby-bindgen/generators/rice/type_speller.rb +509 -0
  99. data/lib/ruby-bindgen/generators/rice/union.erb +5 -0
  100. data/lib/ruby-bindgen/generators/rice/variable.erb +5 -0
  101. data/lib/ruby-bindgen/inputter.rb +54 -0
  102. data/lib/ruby-bindgen/name_mapper.rb +65 -0
  103. data/lib/ruby-bindgen/namer.rb +138 -0
  104. data/lib/ruby-bindgen/outputter.rb +40 -0
  105. data/lib/ruby-bindgen/parser.rb +82 -0
  106. data/lib/ruby-bindgen/refinements/cursor.rb +57 -0
  107. data/lib/ruby-bindgen/refinements/string.rb +41 -0
  108. data/lib/ruby-bindgen/symbol_candidates.rb +282 -0
  109. data/lib/ruby-bindgen/symbol_entry.rb +21 -0
  110. data/lib/ruby-bindgen/symbols.rb +107 -0
  111. data/lib/ruby-bindgen/type_pointer_formatter.rb +35 -0
  112. data/lib/ruby-bindgen/version.rb +3 -0
  113. data/lib/ruby-bindgen.rb +19 -0
  114. data/ruby-bindgen.gemspec +52 -0
  115. metadata +260 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f6c7ab91006aee7a6926a5e8539984035e810270aa1acead7a85cf84421a7509
4
+ data.tar.gz: e6da98262ed5f7c588caf9e20d174613fd90b8bafecdfe02d0490321371d6cf5
5
+ SHA512:
6
+ metadata.gz: a879be6c008ce0067f92e842837d57c8129316431baf059e9c608c4a646ded3e97825ff7738bd43bee559dfe6199a2b9366ecaac32aca1b03d35eaad408138e3
7
+ data.tar.gz: 8466219b1802377f9149f416c5836f0801daed0c0107eeb9b1ad242d7df8d94ce5037c54498756813c6a97bdfe5e2b5966642b9ed1fa80627ba576f332291a1d
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## 1.0.0 (2026-05-10)
2
+
3
+ First public release.
4
+
5
+ ruby-bindgen generates Rice C++ bindings, raw FFI bindings, and CMake build files
6
+ for Ruby extensions wrapping C and C++ libraries. It is driven by libclang and
7
+ configured via YAML.
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (C) 2024 Charlie Savage
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions
7
+ are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
+ SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # ruby-bindgen
2
+
3
+ `ruby-bindgen` generates Ruby bindings from C and C++ header files. It can even generate a CMake build system if needed. It has been battle-tested against large C/C++ libraries such as Proj and OpenCV.
4
+
5
+ Traditionally, wrapping large C and C++ libraries for use in Ruby has been a long, arduous task, especially for large, complex libraries . 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.
6
+
7
+ For much more information, read the extensive [documentation](https://ruby-rice.github.io/ruby-bindgen/).
8
+
9
+ ## Quick Start
10
+
11
+ Create a config file (`rice-bindings.yaml`):
12
+
13
+ ```yaml
14
+ project: my_extension
15
+ input: ./include
16
+ output: ./ext/generated
17
+ format: Rice
18
+
19
+ match:
20
+ - "**/*.hpp"
21
+
22
+ clang:
23
+ args:
24
+ - -I./include
25
+ - -std=c++17
26
+ - -xc++
27
+ ```
28
+
29
+ Generate bindings:
30
+
31
+ ```bash
32
+ ruby-bindgen rice-bindings.yaml
33
+ ```
34
+
35
+ This produces `.cpp`, `.hpp`, and `.ipp` files ready to compile as a Ruby extension.
36
+
37
+ Relative paths (`./include`, `./ext/generated`, `-I./include`) are resolved relative to the config file's directory, so you can run `ruby-bindgen` from anywhere.
38
+
39
+ ## Install
40
+
41
+ ```console
42
+ gem install ruby-bindgen
43
+ ```
44
+
45
+ ## Requirements
46
+
47
+ - Ruby 3.2+
48
+ - libclang from LLVM/Clang 17 or newer
49
+
50
+ ## Documentation
51
+
52
+ Full documentation is at [ruby-rice.github.io/ruby-bindgen](https://ruby-rice.github.io/ruby-bindgen/).
53
+
54
+ - [C++ (Rice) Getting Started](https://ruby-rice.github.io/ruby-bindgen/cpp/getting_started/)
55
+ - [C (FFI) Getting Started](https://ruby-rice.github.io/ruby-bindgen/c/getting_started/)
56
+ - [Configuration Reference](https://ruby-rice.github.io/ruby-bindgen/configuration/)
57
+
58
+ ## Real-world bindings
59
+
60
+ These projects use `ruby-bindgen` to generate their Ruby bindings:
61
+
62
+ - [BitmapPlusPlus-ruby](https://github.com/ruby-rice/BitmapPlusPlus-ruby) — C++ bitmap library, fully automated end-to-end example.
63
+ - [proj4rb](https://github.com/cfis/proj4rb) — bindings for the PROJ cartographic projections C library.
64
+ - [opencv-ruby](https://github.com/cfis/opencv-ruby) — bindings for OpenCV (thousands of classes, tens of thousands of methods).
65
+
66
+ ## License
67
+
68
+ BSD-2-Clause
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require "bundler/gem_tasks"
6
+
7
+ require "minitest/test_task"
8
+ Minitest::TestTask.create
data/bin/ruby-bindgen ADDED
@@ -0,0 +1,133 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'ruby-bindgen/config'
4
+ require 'ruby-bindgen/version'
5
+
6
+ module RubyBindgen
7
+ class Cmd
8
+ attr_reader :config
9
+
10
+ def initialize
11
+ parse_args
12
+ @config = RubyBindgen::Config.new(@config_path)
13
+ require 'ruby-bindgen'
14
+ validate_config
15
+ end
16
+
17
+ def parse_args
18
+ if ARGV.empty? || ARGV[0] == '-h' || ARGV[0] == '--help'
19
+ puts usage
20
+ exit(ARGV.empty? ? 1 : 0)
21
+ end
22
+
23
+ if ARGV[0] == '-v' || ARGV[0] == '--version'
24
+ puts "ruby-bindgen #{RubyBindgen::VERSION}"
25
+ exit 0
26
+ end
27
+
28
+ @config_path = ARGV[0]
29
+
30
+ unless File.exist?(@config_path)
31
+ raise "Config file not found: #{@config_path}"
32
+ end
33
+ end
34
+
35
+ def usage
36
+ <<~USAGE
37
+ ruby-bindgen
38
+
39
+ Usage: ruby-bindgen <config.yaml>
40
+
41
+ Generates Ruby bindings for C and C++ libraries using a YAML configuration file.
42
+
43
+ See documentation for config file format: https://github.com/ruby-rice/ruby-bindgen/blob/main/docs/configuration.md
44
+
45
+ Options:
46
+ -h, --help Show this help message
47
+ -v, --version Print the ruby-bindgen version and exit
48
+ USAGE
49
+ end
50
+
51
+ def validate_config
52
+ raise "Config must specify 'output'" unless @config[:output]
53
+ raise "Config must specify 'format'" unless @config[:format]
54
+
55
+ unless %w[FFI Rice CMake].include?(@config[:format])
56
+ raise "Format must be 'FFI', 'Rice', or 'CMake', got: #{@config[:format]}"
57
+ end
58
+
59
+ # Input defaults to output for CMake (scans generated files in output dir)
60
+ @config[:input] ||= @config[:output]
61
+
62
+ unless File.directory?(@config[:input])
63
+ raise "Input path must be a directory: #{@config[:input]}"
64
+ end
65
+
66
+ unless File.directory?(@config[:output])
67
+ raise "Output path must be a directory: #{@config[:output]}"
68
+ end
69
+
70
+ # Validate project name (if provided). Hyphens are allowed and normalized to underscores.
71
+ if @config[:project] && !@config[:project].match?(/\A[A-Za-z_][A-Za-z0-9_-]*\z/)
72
+ raise "Project name must be a valid C/C++ identifier (hyphens allowed): #{@config[:project]}"
73
+ end
74
+
75
+ # User-supplied Rice include header must exist under the output directory
76
+ # at config time. If it is missing, every generated -rb.hpp file would
77
+ # emit `#include "..."` for a nonexistent header and the C++ build would
78
+ # fail with a confusing 'file not found' error far from the cause.
79
+ if @config[:format] == "Rice" && @config[:include]
80
+ include_path = File.join(@config[:output], @config[:include])
81
+ unless File.exist?(include_path)
82
+ raise "Rice include header not found: #{include_path}"
83
+ end
84
+ end
85
+
86
+ # Validate libclang shared library if specified.
87
+ if @config[:libclang] && !File.exist?(@config[:libclang])
88
+ raise "libclang library not found: #{@config[:libclang]}"
89
+ end
90
+
91
+ # clang/clang-cl args must be an array, not a string.
92
+ if @config[:clang_args] && !@config[:clang_args].is_a?(Array)
93
+ raise "clang args must be a YAML list, got: #{@config[:clang_args].class}"
94
+ end
95
+ end
96
+
97
+ def run
98
+ input = @config[:input]
99
+ default_match = @config[:format] == "CMake" ? ["**/*-rb.cpp"] : ["**/*.{h,hpp}"]
100
+ match_patterns = @config[:match] || default_match
101
+ skip_patterns = @config[:skip] || []
102
+
103
+ inputter = RubyBindgen::Inputter.new(input, match_patterns, skip_patterns)
104
+ outputter = RubyBindgen::Outputter.new(@config[:output])
105
+
106
+ generator_klass = RubyBindgen::Generators.const_get(@config[:format])
107
+ generator = generator_klass.new(inputter, outputter, @config)
108
+ generator.generate
109
+ end
110
+ end
111
+ end
112
+
113
+ debug = ENV['BINDGEN_DEBUG']
114
+
115
+ begin
116
+ cmd = RubyBindgen::Cmd.new
117
+ rescue => e
118
+ # Setup-time errors (missing/invalid config, bad arguments). Treat as user
119
+ # errors and skip the backtrace unless BINDGEN_DEBUG is set.
120
+ STDERR.puts "Error: #{e.message}"
121
+ STDERR.puts e.backtrace if debug
122
+ exit 1
123
+ end
124
+
125
+ begin
126
+ cmd.run
127
+ rescue => e
128
+ # Runtime errors during generation. Print the backtrace because these
129
+ # usually indicate a bug worth reporting.
130
+ STDERR.puts "Error: #{e.message}"
131
+ STDERR.puts e.backtrace
132
+ exit 1
133
+ end
@@ -0,0 +1,238 @@
1
+ # Architecture
2
+
3
+ `ruby-bindgen` uses [libclang](https://clang.llvm.org/doxygen/group__CINDEX.html), via [ffi-clang](https://github.com/ioquatix/ffi-clang), to parse C and C++ header files.
4
+
5
+ Libclang represents the [Abstract Syntax Tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree) as a hierarchy of cursors. Each cursor is a node representing a declaration, statement, or expression. For example, parsing this header:
6
+
7
+ ```cpp
8
+ namespace cv {
9
+ class Mat {
10
+ Mat(int rows, int cols, int type);
11
+ int rows;
12
+ bool empty() const;
13
+ };
14
+ }
15
+ ```
16
+
17
+ Produces a cursor tree like:
18
+
19
+ ```
20
+ namespace (cv)
21
+ └── class_decl (Mat)
22
+ ├── constructor (Mat)
23
+ │ ├── parm_decl (rows)
24
+ │ ├── parm_decl (cols)
25
+ │ └── parm_decl (type)
26
+ ├── field_decl (rows)
27
+ └── cxx_method (empty)
28
+ ```
29
+
30
+ For C and C++ bindings, `ruby-bindgen` uses a [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern)-style traversal to walk this tree. Each cursor kind is dispatched to a corresponding `visit_*` method (e.g., `visit_class_decl`, `visit_cxx_method`), which generates binding code via ERB templates.
31
+
32
+ ``` mermaid
33
+ flowchart TD
34
+ subgraph Input
35
+ H["C/C++ headers"]
36
+ Y["bindings.yaml"]
37
+ end
38
+
39
+ subgraph Parsing
40
+ FC["ffi-clang"]
41
+ AST["AST"]
42
+ FC --> AST
43
+ end
44
+
45
+ subgraph "Code Generation"
46
+ V1["Generator"]
47
+ ERB1["ERB templates"]
48
+ V1 --> ERB1
49
+ end
50
+
51
+ subgraph Output
52
+ F["Rice/FFI files"]
53
+ end
54
+
55
+ H --> FC
56
+ Y --> V1
57
+ AST --> V1
58
+ ERB1 --> F
59
+ ```
60
+
61
+ For CMake bindings, `ruby-bindgen` runs as a second pass, scanning the output directory for previously generated `*-rb.cpp` files.
62
+
63
+ ``` mermaid
64
+ flowchart TD
65
+ subgraph Input
66
+ Y["cmake-bindings.yaml"]
67
+ S["*-rb.cpp"]
68
+ end
69
+
70
+ subgraph "Code Generation"
71
+ V2["CMake generator"]
72
+ ERB2["ERB templates"]
73
+ V2 --> ERB2
74
+ end
75
+
76
+ subgraph Output
77
+ F["CMakeLists.txt / CMakePresets.json"]
78
+ end
79
+
80
+ Y --> V2
81
+ S --> V2
82
+ ERB2 --> F
83
+ ```
84
+
85
+ ## Source Layout
86
+
87
+ The [key classes](#key-classes) live under `lib/ruby-bindgen/`. Each output format (Rice, FFI, CMake) has its own directory under `generators/` containing both the generator implementation and its ERB templates.
88
+
89
+ ```
90
+ lib/ruby-bindgen/
91
+ ├── config.rb # YAML config loading
92
+ ├── inputter.rb # Header file discovery
93
+ ├── outputter.rb # File writing with cleanup
94
+ ├── parser.rb # ffi-clang AST parsing
95
+ ├── name_mapper.rb # Exact/regex name remapping
96
+ ├── namer.rb # C++ → Ruby name conversion
97
+ ├── symbols.rb # skip / version / override matching
98
+ ├── symbol_entry.rb # Per-symbol skip/version/override record
99
+ ├── symbol_candidates.rb # Candidate name generation for lookup
100
+ ├── type_pointer_formatter.rb # Pointer type formatting helpers
101
+ ├── version.rb
102
+ ├── refinements/ # Extensions to ffi-clang and stdlib classes
103
+ │ ├── cursor.rb # Cursor: ruby_name, cruby_name, anonymous_definer, namer
104
+ │ └── string.rb # String: camelize, underscore, upcase_first
105
+ └── generators/
106
+ ├── generator.rb # Base class shared by generators
107
+ ├── rice/ # C++ Rice binding generator
108
+ │ ├── rice.rb # Main C++ generator (AST visitor); the helpers
109
+ │ │ # below are nested under Rice (e.g.
110
+ │ │ # RubyBindgen::Generators::Rice::TypeSpeller).
111
+ │ ├── type_speller.rb # Reconstructs qualified C++ type names
112
+ │ ├── type_index.rb # Index of typedefs and template instantiations
113
+ │ ├── signature_builder.rb # Builds Rice method signatures
114
+ │ ├── template_resolver.rb # Class template resolution
115
+ │ ├── iterator_collector.rb# Detects begin/end iterator pairs
116
+ │ ├── function_pointer.rb # Function pointer typedef handling
117
+ │ ├── reference_qualifier.rb# Reference / const qualifiers
118
+ │ └── *.erb # ERB templates
119
+ ├── ffi/ # C FFI binding generator
120
+ │ ├── ffi.rb # Main C generator
121
+ │ └── *.erb # ERB templates
122
+ └── cmake/ # CMake build file generator
123
+ ├── cmake.rb # Main CMake generator
124
+ ├── guard.rb # Per-target build guards
125
+ └── *.erb # ERB templates
126
+ ```
127
+
128
+ Most generator methods delegate to ERB templates for code generation. Each template receives the current cursor and any generator state as local variables, and outputs a string of generated code.
129
+
130
+ For example, the Rice generator's `cxx_method.erb` template generates a `define_method` call:
131
+
132
+ ```cpp
133
+ define_method<%= signature %>("<%= name %>", &<%= qualified_parent %>::<%= cursor.spelling %>,
134
+ <%= all_args.join(", ") %>)
135
+ ```
136
+
137
+ ## Key Classes
138
+
139
+ ### Config
140
+
141
+ The `Config` class loads the YAML configuration file and resolves platform-specific settings. It detects whether to use `clang:` or `clang-cl:` based on `RUBY_PLATFORM` (`mswin` uses `clang-cl:`; Linux, macOS, and MinGW use `clang:`), resolves relative paths against the config file's directory, and provides hash-like access to all configuration values.
142
+
143
+ ### Inputter
144
+
145
+ The `Inputter` class discovers header files to process. Given a base directory and glob patterns from the config (`match:` and `skip:`), it iterates over matching files and yields both absolute and relative paths.
146
+
147
+ ### Parser
148
+
149
+ The `Parser` class wraps ffi-clang's `Index` and drives the processing loop:
150
+
151
+ ```ruby
152
+ def generate(visitor)
153
+ visitor.visit_start
154
+
155
+ inputter.each do |path, relative_path|
156
+ translation_unit = @index.parse_translation_unit(path, clang_args, [],
157
+ [:detailed_preprocessing_record, :skip_function_bodies])
158
+ visitor.visit_translation_unit(translation_unit, path, relative_path)
159
+ end
160
+
161
+ visitor.visit_end
162
+ end
163
+ ```
164
+
165
+ For each header file, it calls `parse_translation_unit`, which returns a translation unit object. Visitors access the root cursor via `translation_unit.cursor`.
166
+
167
+ Parse options include `:skip_function_bodies` (we only need declarations, not implementations) and `:detailed_preprocessing_record` (to see preprocessor directives).
168
+ The parser also checks diagnostics after each translation unit and raises on fatal/error diagnostics.
169
+
170
+ ### Outputter
171
+
172
+ The `Outputter` class writes generated files to the output directory. It tracks all written paths and applies whitespace cleanup (removing excessive blank lines and blank lines before closing braces) to keep the output tidy.
173
+
174
+ ### Namer
175
+
176
+ The `Namer` class converts C++ names to Ruby conventions:
177
+
178
+ - `CamelCase` class names stay as-is (Ruby classes are CamelCase)
179
+ - `camelCase` and `PascalCase` method names become `snake_case`
180
+ - `isFoo()` / `is_foo()` become `foo?` (any `bool`-returning method whose name starts with `is`, plus any zero-arg `bool`-returning method, gets the `?` suffix)
181
+ - C++ operators map to Ruby operators (`operator+` → `+`, `operator==` → `==`)
182
+ - `operator[]` maps to both `[]` and `[]=` (if the return type is a reference)
183
+ - `operator()` maps to `call`
184
+ - Conversion operators like `operator int()` map to `to_i`, `operator string()` to `to_s`
185
+ - C variable names for Rice classes use the `rb_c` prefix (e.g., `rb_cCvMat`)
186
+
187
+ ## Extensions to ffi-clang and stdlib
188
+
189
+ The `lib/ruby-bindgen/refinements/` directory holds open monkey-patches that
190
+ extend ffi-clang and Ruby's `String`. They are loaded globally — not actual
191
+ Ruby refinements — so any code that loads `ruby-bindgen` sees the additions.
192
+
193
+ - **Cursor** (`refinements/cursor.rb`) — adds `ruby_name`, `cruby_name`,
194
+ `anonymous_definer`, and a class-level `namer` accessor that the generators
195
+ set during `generate`.
196
+ - **String** (`refinements/string.rb`) — adds `camelize`, `underscore`, and
197
+ `upcase_first` for name conversion.
198
+
199
+ Reconstructing qualified C++ type names is handled by the `TypeSpeller` class
200
+ (`lib/ruby-bindgen/generators/rice/type_speller.rb`), which calls
201
+ `FFI::Clang::Type#fully_qualified_name` (provided directly by ffi-clang ≥
202
+ 0.16) and post-processes the result. See [Type Spelling](type_spelling.md).
203
+
204
+ ## Rice Generator Details
205
+
206
+ The Rice generator is the largest part of the codebase because C++ has the most features to handle. Some notable aspects:
207
+
208
+ ### Traversal
209
+
210
+ The generator traverses the AST recursively. Each cursor kind is dispatched to a `visit_*` method (e.g., `visit_class_decl`, `visit_cxx_method`) which checks whether the cursor should be skipped and, if not, renders an ERB template to generate the binding code.
211
+
212
+ ### Filtering
213
+
214
+ Before generating code for a cursor, the visitor applies several filters. See [filtering](cpp/filtering.md) for details.
215
+
216
+ ### Template Handling
217
+
218
+ C++ class templates require special treatment. See [templates](cpp/templates.md) for details.
219
+
220
+ ### Type Spelling
221
+
222
+ Reconstructing correct C++ type names from libclang's type information is one of the trickiest parts of the codebase. The `TypeSpeller` class (`lib/ruby-bindgen/generators/rice/type_speller.rb`) handles:
223
+
224
+ - Namespace qualification (`cv::Mat` not `Mat`)
225
+ - Template argument qualification (`std::vector<cv::Point>` not `std::vector<Point>`)
226
+ - Typedef resolution (knowing when to use the alias vs the underlying type)
227
+ - Dependent types in templates (adding `typename` where required)
228
+ - Elaborated types (`enum Foo` vs `Foo`)
229
+
230
+ ### Default Arguments
231
+
232
+ Libclang provides limited information about default argument values. `ruby-bindgen` extracts default values from the source text and wraps them in `static_cast` with fully qualified types to ensure they compile in the generated context:
233
+
234
+ ```cpp
235
+ // Original: void resize(int size, const Scalar& value = Scalar())
236
+ // Generated:
237
+ Arg("value") = static_cast<const cv::Scalar&>(cv::Scalar())
238
+ ```
@@ -0,0 +1,65 @@
1
+ # C Bindings
2
+
3
+ `ruby-bindgen` generates Ruby files that use the [FFI](https://github.com/ffi/ffi) gem to call C library functions. FFI allows Ruby to directly load and call functions from C shared libraries without writing a C extension. This means:
4
+
5
+ - **No compilation step** - Ruby loads the library at runtime
6
+ - **Easier distribution** - No need to compile native code for each platform
7
+ - **Simpler development** - No C code to write or debug
8
+
9
+ If a library provides a C API then use it!
10
+
11
+ ## Supported Features
12
+
13
+ `ruby-bindgen` supports the following FFI features:
14
+
15
+ - [Functions](https://github.com/ffi/ffi/wiki/Basic-Usage) via `attach_function`, including variadic functions (`:varargs`). Functions taking `va_list` parameters are skipped since `va_list` cannot be constructed from Ruby.
16
+ - [Structs](https://github.com/ffi/ffi/wiki/Structs) map to `FFI::Struct`
17
+ - Unions map to `FFI::Union`
18
+ - [Enums](https://github.com/ffi/ffi/wiki/Enums) map to FFI enum types
19
+ - [Callbacks](https://github.com/ffi/ffi/wiki/Callbacks) for function pointer types
20
+ - [Typedefs](https://github.com/ffi/ffi/wiki/Types) are preserved
21
+ - Forward declarations map to opaque pointer types
22
+ - Global variables via [`attach_variable`](https://www.rubydoc.info/gems/ffi/FFI/Library#attach_variable-instance_method)
23
+ - Constants from `const` variables (see [Constants and Macros](constants.md))
24
+
25
+ ## Getting Started
26
+
27
+ See [Getting Started](getting_started.md) for a step-by-step guide to creating your first FFI bindings.
28
+
29
+ ## Output
30
+
31
+ See [FFI Output](output.md) for details on the generated files.
32
+
33
+ ## Examples
34
+
35
+ The test suite includes bindings generated from some popular C libraries:
36
+
37
+ | Library | Description |
38
+ |-----------------|-----------------------------------|
39
+ | [PROJ](https://proj.org/) | Coordinate transformation library |
40
+ | [SQLite](https://sqlite.org/) | Database engine |
41
+ | [libclang](https://clang.llvm.org/) | C/C++ parsing library |
42
+
43
+ See [test/headers/c](https://github.com/ruby-rice/ruby-bindgen/tree/main/test/headers/c) for the input headers and [test/bindings/c](https://github.com/ruby-rice/ruby-bindgen/tree/main/test/bindings/c) for the generated Ruby bindings.
44
+
45
+ ## Ruby Wrapper Classes
46
+
47
+ Since C is procedural rather than object-oriented, you may wish to wrap the generated FFI bindings in Ruby classes to provide a more idiomatic API:
48
+
49
+ ```ruby
50
+ require_relative 'generated/mylib_ffi'
51
+
52
+ class MyLibWrapper
53
+ def initialize
54
+ @handle = Mylib.create_handle()
55
+ end
56
+
57
+ def process(data)
58
+ Mylib.process_data(@handle, data)
59
+ end
60
+
61
+ def close
62
+ Mylib.destroy_handle(@handle)
63
+ end
64
+ end
65
+ ```
@@ -0,0 +1,21 @@
1
+ # Constants and Macros
2
+
3
+ `ruby-bindgen` generates Ruby constants from top-level `const` variables with literal initializers:
4
+
5
+ ```c
6
+ const int CONST_INT = 10;
7
+ static const int STATIC_CONST_INT = 20;
8
+ const double CONST_DOUBLE = 3.14;
9
+ ```
10
+
11
+ Generates:
12
+
13
+ ```ruby
14
+ CONST_INT = 10
15
+ STATIC_CONST_INT = 20
16
+ CONST_DOUBLE = 3.14
17
+ ```
18
+
19
+ ## Macros
20
+
21
+ Macros are not added to bindings because their values are baked in at generation time and thus not read at runtime. This easily causes confusion. For example, a version macro like `PROJ_VERSION_MAJOR` would reflect whichever library version was used to generate bindings, not the version actually being used on your machine.
@@ -0,0 +1,19 @@
1
+ # Customizing
2
+
3
+ `ruby-bindgen` can customize the generated bindings in several ways:
4
+
5
+ - [`symbols: skip:`](../configuration.md#skip) — exclude specific functions, structs, enums, or typedefs by name or regex pattern
6
+ - [`symbols: overrides:`](../configuration.md#overrides-ffi-only) — replace the generated signature for specific functions when the heuristics pick the wrong FFI type
7
+ - [`export_macros`](../configuration.md#export-macros) — only include functions marked with specific visibility macros
8
+ - [`rename_types`](../configuration.md#name-mappings) — override generated Ruby module/class names
9
+ - [`rename_methods`](../configuration.md#name-mappings) — override generated Ruby method names
10
+
11
+ ## Module Name
12
+
13
+ By default, the generated Ruby module is named after the header file (e.g., `proj.h` → `module Proj`). Use the [`module`](../configuration.md#c-ffi-options) option to override this, including nested modules:
14
+
15
+ ```yaml
16
+ module: Proj::Api
17
+ ```
18
+
19
+ This generates properly nested `module Proj` / `module Api` with correct indentation.
@@ -0,0 +1,24 @@
1
+ # Filtering
2
+
3
+ `ruby-bindgen` provides several mechanisms to control which symbols are included in the generated bindings.
4
+
5
+ ## Export Macros
6
+
7
+ Many libraries use macros to control which functions are exported. `ruby-bindgen` can honor these macros so that only exported functions are included in the generated bindings. See [`export_macros`](../configuration.md#export-macros) for details.
8
+
9
+ ## Skipping Symbols
10
+
11
+ Sometimes you need to exclude specific symbols from the generated bindings — for example, internal APIs, symbols that cause linker errors, or platform-specific functions. `ruby-bindgen` supports skipping by name, qualified name, or regex pattern. See [`symbols.skip`](../configuration.md#skip) for details.
12
+
13
+ ## Version Guards
14
+
15
+ When a library evolves across versions, some symbols are only available in newer releases. `ruby-bindgen` can wrap these symbols in version guards so the same bindings compile against multiple library versions. See [`symbols.versions`](../configuration.md#versions) for details.
16
+
17
+ ## Automatic Skipping
18
+
19
+ The following are automatically skipped:
20
+
21
+ - **Deprecated**: Functions marked with `__attribute__((deprecated))` or `[[deprecated]]`
22
+ - **va_list**: Functions taking `va_list` parameters (cannot be constructed from Ruby; use the variadic `...` version instead)
23
+ - **Private/Protected**: Non-public members
24
+ - **System headers**: Declarations from system include paths
@@ -0,0 +1,56 @@
1
+ # Getting Started with FFI Bindings
2
+
3
+ This guide walks you through generating Ruby FFI bindings for a C library.
4
+
5
+ ## 1. Create a configuration file
6
+
7
+ Create a file named `ffi-bindings.yaml`:
8
+
9
+ ```yaml
10
+ project: mylib
11
+ input: ./include
12
+ output: ./lib/generated
13
+ format: FFI
14
+
15
+ match:
16
+ - "**/*.h"
17
+
18
+ library_names:
19
+ - mylib
20
+
21
+ clang:
22
+ args:
23
+ - -I./include
24
+ - -xc
25
+ ```
26
+
27
+ Key options:
28
+
29
+ - `project` — name used for the project file (`mylib_ffi.rb`) and the Ruby module
30
+ - `input` — directory containing header files
31
+ - `output` — where generated Ruby files are written
32
+ - `library_names` — shared library names to load at runtime (e.g., `mylib` for `libmylib.so`)
33
+ - `match` — glob patterns selecting which headers to process
34
+ - `clang.args` — compiler arguments; `-xc` tells clang to parse as C
35
+
36
+ Paths are relative to the config file's directory, not the working directory.
37
+
38
+ See [Configuration](../configuration.md) for all options, including [library versioning](../configuration.md#c-ffi-options), [symbol filtering](../configuration.md#symbols), and [version guards](../configuration.md#versions).
39
+
40
+ ## 2. Generate bindings
41
+
42
+ ```bash
43
+ ruby-bindgen ffi-bindings.yaml
44
+ ```
45
+
46
+ This produces a project file, one Ruby file per matched header, and optionally a version stub file. See [FFI Output](output.md) for details.
47
+
48
+ ## 3. Use the bindings
49
+
50
+ ```ruby
51
+ require_relative 'lib/generated/mylib_ffi'
52
+
53
+ result = Mylib.my_function(42, "hello")
54
+ ```
55
+
56
+ The project file (`mylib_ffi.rb`) loads the native library and requires all content files. You only need to require the project file.