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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE +25 -0
- data/README.md +68 -0
- data/Rakefile +8 -0
- data/bin/ruby-bindgen +133 -0
- data/docs/architecture.md +238 -0
- data/docs/c/c_bindings.md +65 -0
- data/docs/c/constants.md +21 -0
- data/docs/c/customizing.md +19 -0
- data/docs/c/filtering.md +24 -0
- data/docs/c/getting_started.md +56 -0
- data/docs/c/library_loading.md +53 -0
- data/docs/c/output.md +96 -0
- data/docs/c/types.md +61 -0
- data/docs/c/version_guards.md +105 -0
- data/docs/cmake/cmake_bindings.md +19 -0
- data/docs/cmake/filtering.md +26 -0
- data/docs/cmake/getting_started.md +52 -0
- data/docs/cmake/output.md +110 -0
- data/docs/configuration.md +351 -0
- data/docs/contributing.md +68 -0
- data/docs/cpp/buffers.md +24 -0
- data/docs/cpp/classes.md +139 -0
- data/docs/cpp/cpp_bindings.md +29 -0
- data/docs/cpp/customizing.md +124 -0
- data/docs/cpp/enums.md +42 -0
- data/docs/cpp/filtering.md +35 -0
- data/docs/cpp/getting_started.md +80 -0
- data/docs/cpp/iterators.md +94 -0
- data/docs/cpp/operators.md +170 -0
- data/docs/cpp/output.md +125 -0
- data/docs/cpp/templates.md +114 -0
- data/docs/examples.md +133 -0
- data/docs/index.md +133 -0
- data/docs/prior_art.md +37 -0
- data/docs/troubleshooting.md +243 -0
- data/docs/type_spelling.md +200 -0
- data/docs/updating_bindings.md +55 -0
- data/docs/version_guards.md +69 -0
- data/lib/ruby-bindgen/config.rb +63 -0
- data/lib/ruby-bindgen/generators/cmake/cmake.rb +171 -0
- data/lib/ruby-bindgen/generators/cmake/directory.erb +29 -0
- data/lib/ruby-bindgen/generators/cmake/guard.rb +55 -0
- data/lib/ruby-bindgen/generators/cmake/presets.erb +232 -0
- data/lib/ruby-bindgen/generators/cmake/project.erb +89 -0
- data/lib/ruby-bindgen/generators/ffi/callback.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/constant.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/enum_constant_decl.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/enum_decl.erb +4 -0
- data/lib/ruby-bindgen/generators/ffi/enum_decl_anonymous.erb +4 -0
- data/lib/ruby-bindgen/generators/ffi/ffi.rb +687 -0
- data/lib/ruby-bindgen/generators/ffi/field_decl.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/function.erb +5 -0
- data/lib/ruby-bindgen/generators/ffi/library.erb +39 -0
- data/lib/ruby-bindgen/generators/ffi/project.erb +18 -0
- data/lib/ruby-bindgen/generators/ffi/struct.erb +6 -0
- data/lib/ruby-bindgen/generators/ffi/translation_unit.erb +9 -0
- data/lib/ruby-bindgen/generators/ffi/typedef_decl.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/union.erb +6 -0
- data/lib/ruby-bindgen/generators/ffi/variable.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/version.erb +9 -0
- data/lib/ruby-bindgen/generators/ffi/version_method.erb +5 -0
- data/lib/ruby-bindgen/generators/generator.rb +52 -0
- data/lib/ruby-bindgen/generators/rice/auto_generated_base_class.erb +5 -0
- data/lib/ruby-bindgen/generators/rice/class.erb +31 -0
- data/lib/ruby-bindgen/generators/rice/class_template.erb +9 -0
- data/lib/ruby-bindgen/generators/rice/class_template_specialization.erb +10 -0
- data/lib/ruby-bindgen/generators/rice/constant.erb +9 -0
- data/lib/ruby-bindgen/generators/rice/constructor.erb +1 -0
- data/lib/ruby-bindgen/generators/rice/conversion_function.erb +4 -0
- data/lib/ruby-bindgen/generators/rice/cxx_iterator_method.erb +1 -0
- data/lib/ruby-bindgen/generators/rice/cxx_method.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/enum_constant_decl.erb +7 -0
- data/lib/ruby-bindgen/generators/rice/enum_decl.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/field_decl.erb +8 -0
- data/lib/ruby-bindgen/generators/rice/function.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/function_pointer.rb +68 -0
- data/lib/ruby-bindgen/generators/rice/incomplete_class.erb +3 -0
- data/lib/ruby-bindgen/generators/rice/iterator_alias.erb +1 -0
- data/lib/ruby-bindgen/generators/rice/iterator_collector.rb +159 -0
- data/lib/ruby-bindgen/generators/rice/namespace.erb +5 -0
- data/lib/ruby-bindgen/generators/rice/non_member_operator_binary.erb +4 -0
- data/lib/ruby-bindgen/generators/rice/non_member_operator_inspect.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/non_member_operator_unary.erb +4 -0
- data/lib/ruby-bindgen/generators/rice/operator[].erb +4 -0
- data/lib/ruby-bindgen/generators/rice/project.cpp.erb +18 -0
- data/lib/ruby-bindgen/generators/rice/project.hpp.erb +13 -0
- data/lib/ruby-bindgen/generators/rice/reference_qualifier.rb +495 -0
- data/lib/ruby-bindgen/generators/rice/rice.rb +1724 -0
- data/lib/ruby-bindgen/generators/rice/rice_include.hpp.erb +7 -0
- data/lib/ruby-bindgen/generators/rice/signature_builder.rb +230 -0
- data/lib/ruby-bindgen/generators/rice/template_resolver.rb +585 -0
- data/lib/ruby-bindgen/generators/rice/translation_unit.cpp.erb +40 -0
- data/lib/ruby-bindgen/generators/rice/translation_unit.hpp.erb +7 -0
- data/lib/ruby-bindgen/generators/rice/translation_unit.ipp.erb +3 -0
- data/lib/ruby-bindgen/generators/rice/type_index.rb +117 -0
- data/lib/ruby-bindgen/generators/rice/type_speller.rb +509 -0
- data/lib/ruby-bindgen/generators/rice/union.erb +5 -0
- data/lib/ruby-bindgen/generators/rice/variable.erb +5 -0
- data/lib/ruby-bindgen/inputter.rb +54 -0
- data/lib/ruby-bindgen/name_mapper.rb +65 -0
- data/lib/ruby-bindgen/namer.rb +138 -0
- data/lib/ruby-bindgen/outputter.rb +40 -0
- data/lib/ruby-bindgen/parser.rb +82 -0
- data/lib/ruby-bindgen/refinements/cursor.rb +57 -0
- data/lib/ruby-bindgen/refinements/string.rb +41 -0
- data/lib/ruby-bindgen/symbol_candidates.rb +282 -0
- data/lib/ruby-bindgen/symbol_entry.rb +21 -0
- data/lib/ruby-bindgen/symbols.rb +107 -0
- data/lib/ruby-bindgen/type_pointer_formatter.rb +35 -0
- data/lib/ruby-bindgen/version.rb +3 -0
- data/lib/ruby-bindgen.rb +19 -0
- data/ruby-bindgen.gemspec +52 -0
- metadata +260 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Customizing Bindings
|
|
2
|
+
|
|
3
|
+
`ruby-bindgen` tries its best to generate compilable Rice code. It has been battle tested against [OpenCV](https://github.com/opencv/opencv), which is a large, complex C++ API with over a thousand classes and ten thousand methods.
|
|
4
|
+
|
|
5
|
+
However, complex libraries may require some customization. Customizations fall into three categories:
|
|
6
|
+
|
|
7
|
+
* Configuration
|
|
8
|
+
* Refinements
|
|
9
|
+
* Fixes
|
|
10
|
+
|
|
11
|
+
## Configuration
|
|
12
|
+
|
|
13
|
+
Some issues are best solved via the [configuration](../configuration.md) file rather than editing generated code:
|
|
14
|
+
|
|
15
|
+
- [Symbol filtering](../configuration.md#skip): Skip functions that cause linker errors or aren’t meant for external use by name or regex pattern
|
|
16
|
+
- [Version guards](../configuration.md#versions): Wrap symbols in `#if VERSION >= N` guards so bindings compile against multiple library versions
|
|
17
|
+
- [Export macros](../configuration.md#export-macros): Limit bindings to exported symbols, preventing linker errors from internal functions
|
|
18
|
+
- [Name mappings](../configuration.md#name-mappings): Override generated Ruby class and method names with exact strings or regex patterns
|
|
19
|
+
|
|
20
|
+
## Refinements
|
|
21
|
+
|
|
22
|
+
Ruby classes are open, meaning you can reopen an existing class and add methods to it. Refinements take advantage of this to extend generated bindings with additional functionality. Note that this is not the same as Ruby's built-in [refinements](https://docs.ruby-lang.org/en/master/syntax/refinements_rdoc.html), which are scoped. These additions are global.
|
|
23
|
+
|
|
24
|
+
Common reasons to add refinements include:
|
|
25
|
+
|
|
26
|
+
| Addition | What It Adds |
|
|
27
|
+
|-------------------------|----------------------------------------------------------|
|
|
28
|
+
| Expected methods | `inspect`, `to_s` |
|
|
29
|
+
| Modules | `include_module(rb_mComparable)`, `define_method("<=>")` |
|
|
30
|
+
| Template instantiations | `Mat<unsigned char>` |
|
|
31
|
+
| Type conversions | `to_*` |
|
|
32
|
+
| Exceptions | Custom exception class hierarchy |
|
|
33
|
+
| Custom type handling | `Type<T>`, `From_Ruby<T>`, and `To_Ruby<T>` |
|
|
34
|
+
| Custom STL names | `define_vector<cv::Vec3b>(...)` |
|
|
35
|
+
| Non-member operators | `+`, `-`, `*`, `/`, `==` |
|
|
36
|
+
| Iteration | Add `std::iterator_traits` |
|
|
37
|
+
| Method renames | `rb_alias` to rename methods to match Ruby idioms |
|
|
38
|
+
|
|
39
|
+
Just like in Ruby, you can take advantage of Ruby’s open classes to add new functionality.
|
|
40
|
+
|
|
41
|
+
To do this, create a directory to contain additions. The directory can be named anything, but a suggested convention is to name it `refinements`. Here is an example layout:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
ext/
|
|
45
|
+
├── generated/ # Generated files (output: ./ext/generated)
|
|
46
|
+
│ ├── matrix-rb.hpp
|
|
47
|
+
│ ├── matrix-rb.cpp
|
|
48
|
+
│ ├── matrix-rb.ipp
|
|
49
|
+
│ └── my_extension-rb.cpp
|
|
50
|
+
├── include/
|
|
51
|
+
│ └── matrix.hpp
|
|
52
|
+
├── refinements/ # Manual additions (never overwritten)
|
|
53
|
+
│ ├── CMakeLists.txt
|
|
54
|
+
│ ├── matrix_refinements.hpp
|
|
55
|
+
│ ├── matrix_refinements.cpp
|
|
56
|
+
│ └── ...
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Create a new file in `refinements/` for each generated Rice file you want to extend. For example, if you want to add functionality to `matrix-rb.cpp`, create `refinements/matrix_refinements.hpp` and `refinements/matrix_refinements.cpp`. Define a new init function such as `Init_Matrix_Refinements` in those files.
|
|
60
|
+
|
|
61
|
+
Next add the `.cpp` file to `refinements/CMakeLists.txt`:
|
|
62
|
+
```cmake
|
|
63
|
+
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
|
|
64
|
+
"matrix_refinements.cpp")
|
|
65
|
+
```
|
|
66
|
+
Then in the generated `generated/my_extension-rb.cpp` file, include the refinement header and call `Init_Matrix_Refinements()`.
|
|
67
|
+
|
|
68
|
+
Using refinements has a lot of advantages:
|
|
69
|
+
|
|
70
|
+
- Regeneration Safe: Your updates will not be overwritten with the exception of the `my_extension-rb.cpp` file
|
|
71
|
+
- Reuse `_instantiate` methods - Refinements can `#include` generated `.ipp` files to call [`_instantiate` functions](templates.md#template-instantiate-files-ipp) with new type arguments.
|
|
72
|
+
|
|
73
|
+
### Example
|
|
74
|
+
|
|
75
|
+
Here is an example `refinements/matrix_refinements.cpp` file:
|
|
76
|
+
|
|
77
|
+
```cpp
|
|
78
|
+
#include <sstream>
|
|
79
|
+
#include "../include/matrix.hpp"
|
|
80
|
+
#include "../generated/matrix-rb.hpp"
|
|
81
|
+
#include "../generated/matrix-rb.ipp"
|
|
82
|
+
#include "matrix_refinements.hpp"
|
|
83
|
+
|
|
84
|
+
using namespace Rice;
|
|
85
|
+
|
|
86
|
+
void Init_Matrix_Refinements()
|
|
87
|
+
{
|
|
88
|
+
// Reopen the class that was already defined by generated code
|
|
89
|
+
Rice::Data_Type<Matrix> matrix;
|
|
90
|
+
|
|
91
|
+
matrix.
|
|
92
|
+
define_method("to_s", [](const Matrix& self) -> std::string
|
|
93
|
+
{
|
|
94
|
+
std::ostringstream stream;
|
|
95
|
+
stream << self;
|
|
96
|
+
return stream.str();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Rename a method to better match Ruby idioms
|
|
100
|
+
rb_alias(matrix, rb_intern("[]"), rb_intern("call"));
|
|
101
|
+
|
|
102
|
+
// Instantiate additional templates not in the generated code
|
|
103
|
+
Matrix_instantiate<double>(rb_mMyExtension, "MatrixDouble");
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Fixes
|
|
108
|
+
|
|
109
|
+
### Manual Edits
|
|
110
|
+
|
|
111
|
+
Sometimes you need to **modify** the generated Rice code. Common reasons include:
|
|
112
|
+
|
|
113
|
+
| Change Type | Example | Why |
|
|
114
|
+
|-------------------|---------------------------------|----------------------------------------------------------------------------------------|
|
|
115
|
+
| Missing includes | `#include <core.hpp>` | Generated file references types from headers it doesn't include |
|
|
116
|
+
| Version guards | `#if VERSION >= 2` | API only available in certain library versions |
|
|
117
|
+
| Default values | Remove `Stream::Null()` default | Avoid hardware-dependent functions such as CUDA initialization |
|
|
118
|
+
|
|
119
|
+
- Mark manual changes with `// Manual` comments so they're searchable: `grep -r "// Manual" ext/`
|
|
120
|
+
- Include order matters: System/library headers should come before the primary header, local project headers after
|
|
121
|
+
- Watch fluent chain syntax when commenting out methods: change the preceding `.` to `;` to terminate the chain
|
|
122
|
+
- Refinements can include generated `.ipp` files to reuse `_instantiate` functions with additional type arguments
|
|
123
|
+
|
|
124
|
+
If you have manual edits, you will need a strategy for preserving them when you [regenerate bindings](../updating_bindings.md).
|
data/docs/cpp/enums.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Enums
|
|
2
|
+
|
|
3
|
+
`ruby-bindgen` generates Rice enum bindings for both scoped and unscoped C++ enums. For details on the generated Ruby enum API (comparison, conversion, bitwise operations), see the Rice [Enums](https://ruby-rice.github.io/4.x/bindings/enums/) documentation.
|
|
4
|
+
|
|
5
|
+
## Scoped Enums (enum class)
|
|
6
|
+
|
|
7
|
+
```cpp
|
|
8
|
+
enum class Color
|
|
9
|
+
{
|
|
10
|
+
Red, Green, Blue
|
|
11
|
+
};
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Generates a Rice enum with properly scoped values.
|
|
15
|
+
|
|
16
|
+
## Unscoped Enums
|
|
17
|
+
|
|
18
|
+
Unscoped enums in namespaces have values at namespace scope:
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
namespace cv
|
|
22
|
+
{
|
|
23
|
+
enum BorderType
|
|
24
|
+
{
|
|
25
|
+
BORDER_CONSTANT, BORDER_REPLICATE
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// Values are cv::BORDER_CONSTANT, not cv::BorderType::BORDER_CONSTANT
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Unscoped enums inside classes have values qualified with the enum name:
|
|
32
|
+
|
|
33
|
+
```cpp
|
|
34
|
+
class Buffer
|
|
35
|
+
{
|
|
36
|
+
enum Target
|
|
37
|
+
{
|
|
38
|
+
ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
// Values are Buffer::Target::ARRAY_BUFFER
|
|
42
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
- **Variadic**: Functions with `...` parameters
|
|
23
|
+
- **Deleted**: Methods marked `= delete`
|
|
24
|
+
- **Private/Protected**: Non-public members
|
|
25
|
+
- **Template functions**: Non-member function templates (e.g., `template<typename T> void func()`)
|
|
26
|
+
- **Anonymous namespaces**: Internal implementation details
|
|
27
|
+
|
|
28
|
+
## std:: Typedefs
|
|
29
|
+
|
|
30
|
+
Typedefs to `std::` types are skipped since Rice handles them automatically:
|
|
31
|
+
|
|
32
|
+
```cpp
|
|
33
|
+
typedef std::string String; // Skipped - Rice handles std::string
|
|
34
|
+
```
|
|
35
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Getting Started with Rice Bindings
|
|
2
|
+
|
|
3
|
+
This guide walks you through generating Rice (C++) bindings for a C++ library.
|
|
4
|
+
|
|
5
|
+
## 1. Create a configuration file
|
|
6
|
+
|
|
7
|
+
Create a file named `rice-bindings.yaml`:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
project: my_extension
|
|
11
|
+
input: ./include
|
|
12
|
+
output: ./ext/generated
|
|
13
|
+
format: Rice
|
|
14
|
+
|
|
15
|
+
match:
|
|
16
|
+
- "**/*.hpp"
|
|
17
|
+
|
|
18
|
+
clang:
|
|
19
|
+
args:
|
|
20
|
+
- -I./include
|
|
21
|
+
- -std=c++17
|
|
22
|
+
- -xc++
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Key options:
|
|
26
|
+
|
|
27
|
+
- `project` — name used for the extension init function (`Init_my_extension`) and project wrapper files
|
|
28
|
+
- `input` — directory containing header files
|
|
29
|
+
- `output` — where generated C++ files are written
|
|
30
|
+
- `match` — glob patterns selecting which headers to process
|
|
31
|
+
- `clang.args` — compiler arguments; `-xc++` tells clang to parse as C++, `-std=c++17` sets the language standard
|
|
32
|
+
|
|
33
|
+
Paths are relative to the config file's directory, not the working directory.
|
|
34
|
+
|
|
35
|
+
See [Configuration](../configuration.md) for all options, including [symbol filtering](../configuration.md#symbols), [export macros](../configuration.md#export-macros), [name mappings](../configuration.md#name-mappings), and [version guards](../configuration.md#versions).
|
|
36
|
+
|
|
37
|
+
## 2. Generate bindings
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
ruby-bindgen rice-bindings.yaml
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This produces `.cpp`, `.hpp`, and optionally `.ipp` files for each matched header, plus project wrapper files. See [Rice Output](output.md) for details.
|
|
44
|
+
|
|
45
|
+
## 3. Generate CMake build files (optional)
|
|
46
|
+
|
|
47
|
+
If you want `ruby-bindgen` to generate CMake build files, create a second config:
|
|
48
|
+
|
|
49
|
+
`cmake-bindings.yaml`:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
project: my_extension
|
|
53
|
+
output: ./ext/generated
|
|
54
|
+
format: CMake
|
|
55
|
+
|
|
56
|
+
include_dirs:
|
|
57
|
+
- "${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Then run it **after** the Rice generation:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
ruby-bindgen cmake-bindings.yaml
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
See [CMake Bindings](../cmake/cmake_bindings.md) for details.
|
|
67
|
+
|
|
68
|
+
## 4. Build the extension
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
cd ./ext/generated
|
|
72
|
+
cmake --preset linux-debug # or macos-debug, msvc-debug, etc.
|
|
73
|
+
cmake --build build/linux-debug
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 5. Packaging
|
|
77
|
+
|
|
78
|
+
For packaging your extension as a gem, see the Rice [Packaging](https://ruby-rice.github.io/4.x/packaging/packaging/) documentation.
|
|
79
|
+
|
|
80
|
+
For a complete, fully automated example see [BitmapPlusPlus-ruby](https://ruby-rice.github.io/BitmapPlusPlus-ruby/).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Iterators
|
|
2
|
+
|
|
3
|
+
`ruby-bindgen` automatically generates Rice `define_iterator` calls for C++ classes that expose iterator methods. This provides Ruby's `Enumerable` module support for C++ containers. For details on how Rice bridges C++ iterators to Ruby, see the Rice [Iterators](https://ruby-rice.github.io/4.x/bindings/iterators/) documentation.
|
|
4
|
+
|
|
5
|
+
## Automatic Iterator Detection
|
|
6
|
+
|
|
7
|
+
When `ruby-bindgen` encounters a C++ class with `begin()` and `end()` methods that return iterators, it automatically generates the appropriate `define_iterator` calls. For example, a C++ class like:
|
|
8
|
+
|
|
9
|
+
```cpp
|
|
10
|
+
class Bitmap {
|
|
11
|
+
public:
|
|
12
|
+
PixelIterator begin();
|
|
13
|
+
PixelIterator end();
|
|
14
|
+
};
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Will generate:
|
|
18
|
+
|
|
19
|
+
```cpp
|
|
20
|
+
define_class_under<Bitmap>(rb_mModule, "Bitmap").
|
|
21
|
+
define_iterator<PixelIterator(Bitmap::*)()>(&Bitmap::begin, &Bitmap::end, "each");
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Note:** If a class only defines `begin() const` and `end() const` (without non-const versions), the generated method will be `each_const`. Since Ruby conventions expect `each`, `ruby-bindgen` automatically aliases `each` to `each_const` in this case.
|
|
25
|
+
|
|
26
|
+
## Multiple Iterator Types
|
|
27
|
+
|
|
28
|
+
`ruby-bindgen` handles classes with multiple iterator types, such as const iterators, reverse iterators, and const reverse iterators:
|
|
29
|
+
|
|
30
|
+
| Method Pair | Ruby Method Name |
|
|
31
|
+
|-----------------------|----------------------|
|
|
32
|
+
| `begin()`/`end()` | `each` |
|
|
33
|
+
| `begin() const`/`end() const` | `each_const` |
|
|
34
|
+
| `rbegin()`/`rend()` | `each_reverse` |
|
|
35
|
+
| `rbegin() const`/`rend() const` | `each_reverse_const` |
|
|
36
|
+
|
|
37
|
+
## Incomplete Iterator Traits
|
|
38
|
+
|
|
39
|
+
Some C++ libraries define iterators that lack the required `std::iterator_traits` typedefs. These iterators are missing one or more of:
|
|
40
|
+
|
|
41
|
+
- `value_type`
|
|
42
|
+
- `reference`
|
|
43
|
+
- `pointer`
|
|
44
|
+
- `difference_type`
|
|
45
|
+
- `iterator_category`
|
|
46
|
+
|
|
47
|
+
Rice's `define_iterator` requires these traits to function properly. Without them, you'll see compile errors like:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
error C2039: 'value_type': is not a member of 'std::iterator_traits<MyIterator>'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Automatic Traits Generation
|
|
54
|
+
|
|
55
|
+
`ruby-bindgen` automatically detects incomplete iterators and generates `std::iterator_traits` specializations for them. For example, given an iterator like:
|
|
56
|
+
|
|
57
|
+
```cpp
|
|
58
|
+
// Iterator WITHOUT proper std::iterator_traits
|
|
59
|
+
class IncompleteIterator
|
|
60
|
+
{
|
|
61
|
+
public:
|
|
62
|
+
IncompleteIterator() : ptr_(nullptr) {}
|
|
63
|
+
explicit IncompleteIterator(Pixel* p) : ptr_(p) {}
|
|
64
|
+
Pixel& operator*() const { return *ptr_; }
|
|
65
|
+
IncompleteIterator& operator++() { ++ptr_; return *this; }
|
|
66
|
+
bool operator!=(const IncompleteIterator& other) const { return ptr_ != other.ptr_; }
|
|
67
|
+
|
|
68
|
+
private:
|
|
69
|
+
Pixel* ptr_;
|
|
70
|
+
// NOTE: Missing value_type, reference, pointer, difference_type, iterator_category
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`ruby-bindgen` will generate:
|
|
75
|
+
|
|
76
|
+
```cpp
|
|
77
|
+
// Iterator traits specializations for iterators missing std::iterator_traits
|
|
78
|
+
namespace std
|
|
79
|
+
{
|
|
80
|
+
template<>
|
|
81
|
+
struct iterator_traits<iter::IncompleteIterator>
|
|
82
|
+
{
|
|
83
|
+
using iterator_category = forward_iterator_tag;
|
|
84
|
+
using value_type = iter::Pixel;
|
|
85
|
+
using difference_type = ptrdiff_t;
|
|
86
|
+
using pointer = iter::Pixel*;
|
|
87
|
+
using reference = iter::Pixel&;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Examples
|
|
93
|
+
|
|
94
|
+
See [test/headers/cpp/iterators.hpp](https://github.com/ruby-rice/ruby-bindgen/blob/main/test/headers/cpp/iterators.hpp) for example iterator classes, including both complete and incomplete iterators. The generated bindings are in [test/bindings/cpp/iterators-rb.cpp](https://github.com/ruby-rice/ruby-bindgen/blob/main/test/bindings/cpp/iterators-rb.cpp).
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Operators
|
|
2
|
+
|
|
3
|
+
`ruby-bindgen` automatically generates Rice bindings for C++ operators, mapping them to Ruby methods. For the complete C++ to Ruby operator mapping tables, see the Rice [Operators](https://ruby-rice.github.io/4.x/bindings/operators/) documentation.
|
|
4
|
+
|
|
5
|
+
## Non-Member Operators
|
|
6
|
+
|
|
7
|
+
C++ allows operators to be defined as non-member (free) functions. Ruby doesn't have the concept of non-member functions — all methods belong to a class. Therefore, `ruby-bindgen` automatically converts non-member operators to instance methods on the first argument's class:
|
|
8
|
+
|
|
9
|
+
```cpp
|
|
10
|
+
class Matrix
|
|
11
|
+
{
|
|
12
|
+
public:
|
|
13
|
+
int rows, cols;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Non-member operators
|
|
17
|
+
Matrix& operator+=(Matrix& a, const Matrix& b);
|
|
18
|
+
Matrix& operator-=(Matrix& a, const Matrix& b);
|
|
19
|
+
Matrix& operator*=(Matrix& a, double scalar);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`ruby-bindgen` generates:
|
|
23
|
+
|
|
24
|
+
```cpp
|
|
25
|
+
rb_cMatrix.
|
|
26
|
+
define_method("assign_plus", [](Matrix& self, const Matrix& other) -> Matrix&
|
|
27
|
+
{
|
|
28
|
+
self += other;
|
|
29
|
+
return self;
|
|
30
|
+
}).
|
|
31
|
+
define_method("assign_minus", [](Matrix& self, const Matrix& other) -> Matrix&
|
|
32
|
+
{
|
|
33
|
+
self -= other;
|
|
34
|
+
return self;
|
|
35
|
+
}).
|
|
36
|
+
define_method("assign_multiply", [](Matrix& self, double other) -> Matrix&
|
|
37
|
+
{
|
|
38
|
+
self *= other;
|
|
39
|
+
return self;
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Ruby usage:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
matrix1 = Matrix.new
|
|
47
|
+
matrix2 = Matrix.new
|
|
48
|
+
|
|
49
|
+
matrix1.assign_plus(matrix2) # Calls operator+=(matrix1, matrix2)
|
|
50
|
+
matrix1.assign_multiply(2.0) # Calls operator*=(matrix1, 2.0)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Non-Member Unary Operators
|
|
54
|
+
|
|
55
|
+
Non-member unary operators (common in libraries like OpenCV) are wrapped via lambdas:
|
|
56
|
+
|
|
57
|
+
```cpp
|
|
58
|
+
MatExpr operator~(const Mat& m); // Bitwise NOT
|
|
59
|
+
MatExpr operator-(const Mat& m); // Negation
|
|
60
|
+
MatExpr operator+(const Mat& m); // Unary plus
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`ruby-bindgen` generates:
|
|
64
|
+
|
|
65
|
+
```cpp
|
|
66
|
+
rb_cMat.
|
|
67
|
+
define_method("~", [](const Mat& self) -> MatExpr
|
|
68
|
+
{
|
|
69
|
+
return ~self;
|
|
70
|
+
}).
|
|
71
|
+
define_method("-@", [](const Mat& self) -> MatExpr
|
|
72
|
+
{
|
|
73
|
+
return -self;
|
|
74
|
+
}).
|
|
75
|
+
define_method("+@", [](const Mat& self) -> MatExpr
|
|
76
|
+
{
|
|
77
|
+
return +self;
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Ruby usage:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
mat = Mat.new
|
|
85
|
+
result = ~mat # Bitwise NOT
|
|
86
|
+
result = -mat # Negation
|
|
87
|
+
result = +mat # Unary plus
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Streaming Operator
|
|
91
|
+
|
|
92
|
+
The `<<` operator is often used for stream-like semantics in C++. `ruby-bindgen` handles two cases:
|
|
93
|
+
|
|
94
|
+
1. **Output streaming** (`std::ostream& operator<<(std::ostream&, const T&)`) — converted to `inspect` methods on the streamed class.
|
|
95
|
+
|
|
96
|
+
2. **Other streaming** (e.g., `FileStorage& operator<<(FileStorage&, const T&)`) — converted to `<<` instance methods.
|
|
97
|
+
|
|
98
|
+
```cpp
|
|
99
|
+
// Output streaming - generates inspect method on Printable
|
|
100
|
+
std::ostream& operator<<(std::ostream& os, const Printable& p);
|
|
101
|
+
|
|
102
|
+
// FileStorage streaming - generates << method on FileStorage
|
|
103
|
+
FileStorage& operator<<(FileStorage& fs, const std::string& value);
|
|
104
|
+
FileStorage& operator<<(FileStorage& fs, int value);
|
|
105
|
+
FileStorage& operator<<(FileStorage& fs, const cv::Mat& mat);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The generated Ruby code groups all non-member operators by their target class:
|
|
109
|
+
|
|
110
|
+
```cpp
|
|
111
|
+
rb_cPrintable.
|
|
112
|
+
define_method("inspect", [](const Printable& self) -> std::string
|
|
113
|
+
{
|
|
114
|
+
std::ostringstream stream;
|
|
115
|
+
stream << self;
|
|
116
|
+
return stream.str();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
rb_cFileStorage.
|
|
120
|
+
define_method("<<", [](FileStorage& self, const std::string& other) -> FileStorage&
|
|
121
|
+
{
|
|
122
|
+
self << other;
|
|
123
|
+
return self;
|
|
124
|
+
}).
|
|
125
|
+
define_method("<<", [](FileStorage& self, int other) -> FileStorage&
|
|
126
|
+
{
|
|
127
|
+
self << other;
|
|
128
|
+
return self;
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Conversion Operators
|
|
133
|
+
|
|
134
|
+
C++ [conversion operators](https://en.cppreference.com/w/cpp/language/cast_operator) are mapped to Ruby `to_*` methods:
|
|
135
|
+
|
|
136
|
+
```cpp
|
|
137
|
+
operator double() const; // to_f
|
|
138
|
+
operator bool() const; // to_bool
|
|
139
|
+
operator std::string() const; // to_s
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Conversion Type Mappings
|
|
143
|
+
|
|
144
|
+
| C++ Type | Ruby Method | Ruby Class |
|
|
145
|
+
|:---------|:------------|:-----------|
|
|
146
|
+
| `int` | `to_i` | `Integer` |
|
|
147
|
+
| `long` | `to_l` | `Integer` |
|
|
148
|
+
| `long long` | `to_i64` | `Integer` |
|
|
149
|
+
| `short` | `to_i16` | `Integer` |
|
|
150
|
+
| `unsigned int` | `to_u` | `Integer` |
|
|
151
|
+
| `unsigned long` | `to_ul` | `Integer` |
|
|
152
|
+
| `unsigned long long` | `to_u64` | `Integer` |
|
|
153
|
+
| `unsigned short` | `to_u16` | `Integer` |
|
|
154
|
+
| `int8_t` | `to_i8` | `Integer` |
|
|
155
|
+
| `int16_t` | `to_i16` | `Integer` |
|
|
156
|
+
| `int32_t` | `to_i32` | `Integer` |
|
|
157
|
+
| `int64_t` | `to_i64` | `Integer` |
|
|
158
|
+
| `uint8_t` | `to_u8` | `Integer` |
|
|
159
|
+
| `uint16_t` | `to_u16` | `Integer` |
|
|
160
|
+
| `uint32_t` | `to_u32` | `Integer` |
|
|
161
|
+
| `uint64_t` | `to_u64` | `Integer` |
|
|
162
|
+
| `size_t` | `to_size` | `Integer` |
|
|
163
|
+
| `float` | `to_f32` | `Float` |
|
|
164
|
+
| `double` | `to_f` | `Float` |
|
|
165
|
+
| `long double` | `to_ld` | `Float` |
|
|
166
|
+
| `bool` | `to_bool` | `TrueClass` / `FalseClass` |
|
|
167
|
+
| `std::string` | `to_s` | `String` |
|
|
168
|
+
| `const char*` | `to_s` | `String` |
|
|
169
|
+
|
|
170
|
+
Custom types are mapped to `to_<typename>` in snake_case (e.g., `operator MyClass()` becomes `to_my_class`). Pointer conversions map to `to_ptr` or `to_const_ptr`.
|
data/docs/cpp/output.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Rice Output
|
|
2
|
+
|
|
3
|
+
## Header Files
|
|
4
|
+
|
|
5
|
+
For every C++ header file, `ruby-bindgen` generates a .hpp and .cpp file. These files have the same name as the C++ header but with the addition of `-rb` at the end. For example, a C++ header file called `matrix.hpp` will generate in `matrix-rb.hpp` and `matrix-rb.cpp`. If the C++ header file declares a template class, then a third file will be generated called `matrix-rb.ipp`.
|
|
6
|
+
|
|
7
|
+
``` mermaid
|
|
8
|
+
flowchart LR
|
|
9
|
+
subgraph Input
|
|
10
|
+
H1["matrix.hpp"]
|
|
11
|
+
CF["rice-bindings.yaml"]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
H1 & CF --> RB["ruby-bindgen"]
|
|
15
|
+
|
|
16
|
+
subgraph "Rice Output"
|
|
17
|
+
R1["matrix-rb.cpp"]
|
|
18
|
+
R2["matrix-rb.hpp"]
|
|
19
|
+
R3["matrix-rb.ipp"]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
RB --> R1 & R2 & R3
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The `.hpp` file declares an `Init_` function (e.g., `Init_Matrix`) that registers the classes, methods, and enums from that header with Rice.
|
|
26
|
+
|
|
27
|
+
The `.cpp` file defines that `Init_` function with the actual Rice bindings code.
|
|
28
|
+
|
|
29
|
+
The `.ipp` file is only generated when the header declares template classes. It contains one `_instantiate` function per template class that can be used to instantiate the template. The `-rb.ipp` files can be reused across translated units. See [Templates](templates.md#template-instantiate-files-ipp) for details.
|
|
30
|
+
|
|
31
|
+
## Init Function Names
|
|
32
|
+
|
|
33
|
+
Each generated file has an `Init_` function. To avoid conflicts when multiple files have the same name in different directories (e.g., `core/version.hpp` and `dnn/version.hpp`), the function name includes the directory path:
|
|
34
|
+
|
|
35
|
+
| File Path | Init Function |
|
|
36
|
+
|-----------|---------------|
|
|
37
|
+
| `version.hpp` | `Init_Version` |
|
|
38
|
+
| `core/version.hpp` | `Init_Core_Version` |
|
|
39
|
+
| `dnn/version.hpp` | `Init_Dnn_Version` |
|
|
40
|
+
| `core/hal/interface.hpp` | `Init_Core_Hal_Interface` |
|
|
41
|
+
|
|
42
|
+
The top-level directory is always removed to avoid overly long names (e.g., `opencv2/calib3d.hpp` becomes `Init_Calib3d`, and `opencv2/core/version.hpp` becomes `Init_Core_Version`).
|
|
43
|
+
|
|
44
|
+
## Project Files
|
|
45
|
+
|
|
46
|
+
When you set the `project` option in your configuration, `ruby-bindgen` also generates project-level wrapper files that tie everything together:
|
|
47
|
+
|
|
48
|
+
```mermaid
|
|
49
|
+
flowchart LR
|
|
50
|
+
subgraph Input
|
|
51
|
+
CF["rice-bindings.yaml"]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
CF --> RB["ruby-bindgen"]
|
|
55
|
+
|
|
56
|
+
subgraph "Project Files"
|
|
57
|
+
P1["my_extension-rb.cpp"]
|
|
58
|
+
P2["my_extension-rb.hpp"]
|
|
59
|
+
P3["rice_include.hpp"]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
RB --> P1 & P2 & P3
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
* `my_extension-rb.hpp` declares the main Ruby init function `Init_my_extension` with the appropriate export attribute (`__declspec(dllexport)` on Windows, `__attribute__((visibility("default")))` on Linux/macOS)
|
|
66
|
+
* `my_extension-rb.cpp` defines `Init_my_extension`, which calls each per-header `Init_` function (e.g., `Init_Matrix`, `Init_Image`, `Init_Filter`)
|
|
67
|
+
* `rice_include.hpp` is the default [include header](#include-header) that all generated translation units include
|
|
68
|
+
|
|
69
|
+
To suppress project file generation, omit the `project` option from your configuration. This is useful when you want to manage the top-level init function yourself.
|
|
70
|
+
|
|
71
|
+
## Include Header
|
|
72
|
+
|
|
73
|
+
By default `ruby-bindgen` creates a default include files called `rice_include.hpp`. Its content is:
|
|
74
|
+
|
|
75
|
+
```cpp
|
|
76
|
+
#pragma once
|
|
77
|
+
|
|
78
|
+
// Default Rice include header generated by ruby-bindgen
|
|
79
|
+
// To customize, create your own header and specify it with the 'include:' config option
|
|
80
|
+
#include <rice/rice.hpp>
|
|
81
|
+
#include <rice/stl.hpp>
|
|
82
|
+
```
|
|
83
|
+
If the `rice_include.hpp` already exists, `ruby-bindgen` will not overwrite it.
|
|
84
|
+
|
|
85
|
+
All generated headers include this file:
|
|
86
|
+
|
|
87
|
+
```cpp
|
|
88
|
+
#include "../../rice_include.hpp" // relative path computed automatically
|
|
89
|
+
|
|
90
|
+
void Init_MyClass();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Remember that C++ templates are instantiated per translation unit. If different translation units see different template definitions (e.g., one sees a `Type<T>` specialization and another doesn't), this causes an ODR (One Definition Rule) violation. The linker may silently pick the wrong instantiation, leading to subtle bugs.
|
|
94
|
+
|
|
95
|
+
By centralizing all Rice includes in a single header that every generated file includes, all translation units see the same template definitions, preventing ODR violations.
|
|
96
|
+
|
|
97
|
+
The include header is an ideal candidate for precompiled headers (PCH). Since every generated file includes it, precompiling this header can significantly speed up build times:
|
|
98
|
+
|
|
99
|
+
```cmake
|
|
100
|
+
# CMake example
|
|
101
|
+
target_precompile_headers(my_extension PRIVATE
|
|
102
|
+
"${CMAKE_SOURCE_DIR}/rice_include.hpp"
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
You can specify a custom include file by setting the `include` option in the configuration file. This will replace the default `rice_include.hpp` file and thus allow you to specify your own `include` files if necessary. Your custom header must include the Rice headers, plus any additional includes or definitions you want to be global.
|
|
107
|
+
|
|
108
|
+
For example, it is a good place to add support for custom smart pointers. For example, OpenCV defines `cv::Ptr<T>` which inherits from `std::shared_ptr<T>`. The opencv-ruby bindings wrap this smart pointer in a custom header file that is then added to `rice_include.hpp`. That means all translation units see it, preventing ODR violations.
|
|
109
|
+
|
|
110
|
+
A custom include file will not be overwritten by `ruby-bindgen`.
|
|
111
|
+
|
|
112
|
+
## Init Function Call Graph
|
|
113
|
+
|
|
114
|
+
When Ruby loads an extension, it calls the top-level `Init_<extension>` function, which in turn calls each per-header `Init_*` function to register classes, methods, and enums.
|
|
115
|
+
|
|
116
|
+
```mermaid
|
|
117
|
+
flowchart TD
|
|
118
|
+
A["Ruby loads extension"] --> B["Init_my_extension"]
|
|
119
|
+
B --> C["Init_Core"]
|
|
120
|
+
B --> D["Init_Mat"]
|
|
121
|
+
B --> E["Init_*"]
|
|
122
|
+
B --> G["Init_*_Refinements (manual, optional)"]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The extension `Init_<extension>` function is the top-level entry point and calls all per-header `Init_*` functions. Refinement init functions are optional manual hooks that run after generated definitions. See [refinements](customizing.md#refinements) for details.
|