Skip to content

Commit 2a0591e

Browse files
committed
Update C/ffi bindings to also support export macros.
1 parent 50c834e commit 2a0591e

File tree

8 files changed

+73
-32
lines changed

8 files changed

+73
-32
lines changed

docs/c_bindings.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ This generates search names like `libproj.so.25`, `libproj.so.22`, etc. on Linux
144144

145145
If `library_versions` is omitted, only the unversioned name is searched. This works on most systems where the package manager creates an unversioned symlink (e.g., `libproj.so` → `libproj.so.25`).
146146

147+
## Filtering
148+
149+
`ruby-bindgen` can filter which symbols are included in the generated bindings:
150+
151+
- [`skip_symbols`](configuration.md#skip-symbols) - Skip specific functions, structs, enums, or typedefs by name or regex pattern
152+
- [`export_macros`](configuration.md#export-macros) - Only include functions marked with specific visibility macros
153+
147154
## Usage Tips
148155

149156
Since C is procedural rather than object-oriented, you may want to wrap the generated FFI bindings in Ruby classes to provide a more idiomatic API:

docs/configuration.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ These options apply to all formats.
2727
| `match` | `["**/*.{h,hpp}"]` | Glob patterns specifying which header files to process. |
2828
| `skip` | `[]` | Glob patterns specifying which header files to skip. |
2929
| `skip_symbols` | `[]` | List of symbol names to skip. Supports simple names, fully qualified names, or regex patterns. See [Skip Symbols](#skip-symbols). |
30+
| `export_macros` | `[]` | List of macros that indicate a function is exported. When set, only functions whose source text contains one of these macros are included. See [Export Macros](#export-macros). |
3031

3132
## C (FFI) Options
3233

@@ -41,7 +42,6 @@ These options apply to all formats.
4142
|-----------------|----------------|-------------|
4243
| `extension` | none | Name of the Ruby extension. Used for the `Init_` function name. Must be a valid C/C++ identifier. When provided, generates project wrapper files (`{extension}-rb.cpp`, `{extension}-rb.hpp`). When omitted, only per-file bindings are generated. |
4344
| `include` | auto-generated | Path to a custom Rice include header. See [Include Header](cpp/cpp_bindings.md#include-header). |
44-
| `export_macros` | `[]` | List of macros that indicate a symbol is exported. See [Export Macros](cpp/filtering.md#export-macros). |
4545

4646
## CMake Options
4747

@@ -162,3 +162,23 @@ Regex patterns are enclosed in forward slashes (`/pattern/`) and are matched aga
162162
- Deprecated functions (marked with `__attribute__((deprecated))`)
163163
- Internal functions (names ending with `_`)
164164
- Methods returning pointers to incomplete types (pimpl pattern)
165+
166+
## Export Macros
167+
168+
The `export_macros` option filters functions based on the presence of specific macros in the source code. This is useful for libraries that use macros to control symbol visibility.
169+
170+
When specified, only functions whose source text contains at least one of the listed macros will be included in the bindings. This prevents linker errors from trying to wrap internal functions that aren't exported from the shared library.
171+
172+
```yaml
173+
export_macros:
174+
- CV_EXPORTS
175+
- CV_EXPORTS_W
176+
```
177+
178+
### Common Library Macros
179+
180+
| Library | Export Macros |
181+
|---------|--------------|
182+
| OpenCV | `CV_EXPORTS`, `CV_EXPORTS_W`, `CV_EXPORTS_W_SIMPLE` |
183+
| Qt | `Q_DECL_EXPORT`, `Q_CORE_EXPORT` |
184+
| Boost | `BOOST_*_DECL` |

docs/cpp/customizing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ However, complex libraries may require some customization. Customizations fall i
1313
Some issues are best solved via the [configuration](../configuration.md) file rather than editing generated code:
1414

1515
- Skip symbols: Functions that cause linker errors or aren’t meant for external use can be added to [`skip_symbols`](../configuration.md#skip-symbols)
16-
- Export macros: Use [`export_macros`](filtering.md#export-macros) to limit bindings to exported symbols, preventing linker errors from internal functions
16+
- Export macros: Use [`export_macros`](../configuration.md#export-macros) to limit bindings to exported symbols, preventing linker errors from internal functions
1717

1818
## Refinements
1919

docs/cpp/filtering.md

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,7 @@
44

55
## Export Macros
66

7-
The `export_macros` option filters functions based on the presence of specific macros in the source code. This is particularly useful for libraries like OpenCV that use macros to control symbol visibility.
8-
9-
When specified, only functions whose source text contains at least one of the listed macros will be included in the bindings. This prevents linker errors from trying to wrap internal functions that aren't exported from the shared library.
10-
11-
```yaml
12-
export_macros:
13-
- CV_EXPORTS
14-
- CV_EXPORTS_W
15-
```
16-
17-
### Common Library Macros
18-
19-
| Library | Export Macros |
20-
|---------|--------------|
21-
| OpenCV | `CV_EXPORTS`, `CV_EXPORTS_W`, `CV_EXPORTS_W_SIMPLE` |
22-
| Qt | `Q_DECL_EXPORT`, `Q_CORE_EXPORT` |
23-
| Boost | `BOOST_*_DECL` |
7+
See [`export_macros`](../configuration.md#export-macros) in the configuration documentation.
248

259
## Skip Symbols
2610

lib/ruby-bindgen/visitors/ffi/ffi.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def initialize(outputter, config)
1111
@library_names = config[:library_names] || []
1212
@library_versions = config[:library_versions] || []
1313
@skip_symbols = config[:skip_symbols] || []
14+
@export_macros = config[:export_macros] || []
1415
@indentation = 0
1516
end
1617

@@ -27,6 +28,20 @@ def skip_symbol?(cursor)
2728
end
2829
end
2930

31+
# Check if cursor has one of the required export macros in its source text.
32+
# When export_macros is empty, all symbols pass (no filtering).
33+
def has_export_macro?(cursor)
34+
return true if @export_macros.empty?
35+
36+
begin
37+
source_text = cursor.extent.text
38+
return true if source_text.nil?
39+
@export_macros.any? { |macro| source_text.include?(macro) }
40+
rescue
41+
true
42+
end
43+
end
44+
3045
def visit_start
3146
end
3247

@@ -134,6 +149,7 @@ def visit_enum_constant_decl(cursor)
134149

135150
def visit_function(cursor)
136151
return if skip_symbol?(cursor)
152+
return unless has_export_macro?(cursor)
137153

138154
result = Array.new
139155
parameter_types = cursor.find_by_kind(false, :cursor_parm_decl).map do |parameter|

test/bindings/c/filtering.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ def self.search_names
3535

3636
ffi_lib self.search_names
3737

38-
attach_function :included_function, :includedFunction, [:int], :int
39-
attach_function :keep_this_function, :keepThisFunction, [:double, :double], :double
38+
attach_function :exported_function, :exportedFunction, [:int], :int
39+
attach_function :another_exported, :anotherExported, [:double, :double], :double
4040

4141
class IncludedStruct < FFI::Struct
4242
layout :x, :int,

test/ffi_test.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def test_sqlite3
2626
def test_filtering
2727
run_ffi_test("filtering.h",
2828
library_names: ["filtering"], library_versions: [],
29+
export_macros: ["MY_EXPORT"],
2930
skip_symbols: ["skippedFunction", "alsoSkipped", "/internal_helper.*/", "SkippedStruct", "SkippedEnum", "SkippedTypedef"])
3031
end
3132

test/headers/c/filtering.h

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,38 @@
1-
// Test cases for FFI skip_symbols filtering
1+
// Test cases for FFI filtering:
2+
// 1. Export macros - only include functions with specified macros
3+
// 2. Skip symbols - explicitly skip certain symbol names
24

3-
// --- Functions ---
5+
#if defined(_MSC_VER)
6+
#define MY_EXPORT __declspec(dllexport)
7+
#else
8+
#define MY_EXPORT __attribute__((visibility("default")))
9+
#endif
410

5-
// Normal function - should be INCLUDED
6-
int includedFunction(int x);
11+
// --- Export Macro Tests ---
12+
13+
// Exported function - should be INCLUDED when export_macros contains MY_EXPORT
14+
MY_EXPORT int exportedFunction(int x);
15+
16+
// Non-exported function - should be SKIPPED when export_macros is set
17+
void internalFunction(int x);
18+
19+
// Another exported function - should be INCLUDED
20+
MY_EXPORT double anotherExported(double a, double b);
21+
22+
// --- Skip Symbol Tests ---
723

824
// Function to skip by name - should be SKIPPED
9-
void skippedFunction(int x);
25+
MY_EXPORT void skippedFunction(int x);
1026

1127
// Another function to skip by name - should be SKIPPED
12-
int alsoSkipped(double y);
28+
MY_EXPORT int alsoSkipped(double y);
1329

1430
// Function to skip by regex - should be SKIPPED
15-
void internal_helper_init(void);
16-
17-
// Normal function - should be INCLUDED
18-
double keepThisFunction(double a, double b);
31+
MY_EXPORT void internal_helper_init(void);
1932

2033
// --- Structs ---
2134

22-
// Normal struct - should be INCLUDED
35+
// Normal struct - should be INCLUDED (export macros only filter functions)
2336
struct IncludedStruct
2437
{
2538
int x;

0 commit comments

Comments
 (0)