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,230 @@
|
|
|
1
|
+
module RubyBindgen
|
|
2
|
+
module Generators
|
|
3
|
+
class Rice
|
|
4
|
+
# Builds callable signatures and argument metadata for generated Rice
|
|
5
|
+
# bindings, including default-value handling and buffer detection.
|
|
6
|
+
class SignatureBuilder
|
|
7
|
+
STRING_POINTER_TYPES = [
|
|
8
|
+
:type_char_u,
|
|
9
|
+
:type_char_s,
|
|
10
|
+
:type_wchar
|
|
11
|
+
].freeze
|
|
12
|
+
|
|
13
|
+
def initialize(type_speller:, reference_qualifier:, cursor_literals:, fundamental_types:)
|
|
14
|
+
@type_speller = type_speller
|
|
15
|
+
@reference_qualifier = reference_qualifier
|
|
16
|
+
@cursor_literals = cursor_literals
|
|
17
|
+
@fundamental_types = fundamental_types
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Check if a type should use ArgBuffer (for parameters) or ReturnBuffer (for return types).
|
|
21
|
+
# Returns true if the type is:
|
|
22
|
+
# - A pointer to a fundamental type (int*, double*, char*, etc.)
|
|
23
|
+
# - A double pointer (T**) - pointer to any pointer type
|
|
24
|
+
# Types that look like pointers to fundamentals but are actually strings
|
|
25
|
+
# char* and wchar_t* are C strings, not buffers
|
|
26
|
+
def buffer_type?(type)
|
|
27
|
+
return false unless type.kind == :type_pointer
|
|
28
|
+
|
|
29
|
+
pointee = type.pointee
|
|
30
|
+
pointee_kind = pointee.canonical.kind
|
|
31
|
+
|
|
32
|
+
return false if STRING_POINTER_TYPES.include?(pointee_kind)
|
|
33
|
+
return true if @fundamental_types.include?(pointee_kind)
|
|
34
|
+
return true if pointee_kind == :type_pointer
|
|
35
|
+
|
|
36
|
+
false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def constructor_signature(cursor)
|
|
40
|
+
signature = Array.new
|
|
41
|
+
|
|
42
|
+
case cursor.kind
|
|
43
|
+
when :cursor_constructor
|
|
44
|
+
parent = cursor.semantic_parent
|
|
45
|
+
class_name = @type_speller.qualified_display_name(parent)
|
|
46
|
+
signature << class_name
|
|
47
|
+
params = constructor_type_spellings(cursor)
|
|
48
|
+
if cursor.semantic_parent&.kind == :cursor_class_template
|
|
49
|
+
params = params.map { |pt| @type_speller.qualify_class_template_typedefs(pt, cursor.semantic_parent) }
|
|
50
|
+
end
|
|
51
|
+
if parent
|
|
52
|
+
params = params.map { |pt| @type_speller.qualify_class_static_members(pt, parent) }
|
|
53
|
+
end
|
|
54
|
+
if parent&.kind == :cursor_class_template
|
|
55
|
+
params = params.map { |pt| @type_speller.preserve_template_parameter_names(pt, parent) }
|
|
56
|
+
end
|
|
57
|
+
signature += params
|
|
58
|
+
|
|
59
|
+
when :cursor_class_decl, :cursor_struct
|
|
60
|
+
signature << @type_speller.qualified_display_name(cursor)
|
|
61
|
+
else
|
|
62
|
+
raise("Unsupported cursor kind: #{cursor.kind}")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
result = signature.compact.join(", ")
|
|
66
|
+
result.match(/std::initializer_list/) ? nil : result
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def arguments(cursor)
|
|
70
|
+
(0...cursor.num_arguments).map do |index|
|
|
71
|
+
param = cursor.argument(index)
|
|
72
|
+
param_name = param.spelling.empty? ? "arg_#{index}" : param.spelling.underscore
|
|
73
|
+
|
|
74
|
+
type = param.type
|
|
75
|
+
if type.kind == :type_pointer && type.pointee.kind == :type_unexposed
|
|
76
|
+
type_param = type.pointee.spelling
|
|
77
|
+
arg_class = "std::conditional_t<std::is_fundamental_v<#{type_param}>, ArgBuffer, Arg>"
|
|
78
|
+
else
|
|
79
|
+
arg_class = buffer_type?(type) ? "ArgBuffer" : "Arg"
|
|
80
|
+
end
|
|
81
|
+
result = "#{arg_class}(\"#{param_name}\")"
|
|
82
|
+
|
|
83
|
+
default_value = find_default_value(param)
|
|
84
|
+
if default_value && param.type.copyable?
|
|
85
|
+
qualified_type = @type_speller.type_spelling(param.type)
|
|
86
|
+
if param.semantic_parent&.semantic_parent&.kind == :cursor_class_template
|
|
87
|
+
qualified_type = @type_speller.qualify_class_template_typedefs(qualified_type, param.semantic_parent.semantic_parent)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
decl = param.type.declaration
|
|
91
|
+
is_array_alias = (decl.kind == :cursor_type_alias_decl || decl.kind == :cursor_typedef_decl) &&
|
|
92
|
+
[:type_constant_array, :type_incomplete_array].include?(decl.underlying_type.canonical.kind)
|
|
93
|
+
if is_array_alias
|
|
94
|
+
result << " = #{default_value}"
|
|
95
|
+
elsif default_value == '{}'
|
|
96
|
+
# Strip cv-qualifiers and reference via libclang so T{} is
|
|
97
|
+
# well-formed regardless of whether the parameter is `T &`,
|
|
98
|
+
# `T &&`, or `const T &`.
|
|
99
|
+
base_type = @type_speller.type_spelling(param.type.non_reference_type.unqualified_type)
|
|
100
|
+
if param.semantic_parent&.semantic_parent&.kind == :cursor_class_template
|
|
101
|
+
base_type = @type_speller.qualify_class_template_typedefs(base_type, param.semantic_parent.semantic_parent)
|
|
102
|
+
end
|
|
103
|
+
result << " = static_cast<#{qualified_type}>(#{base_type}{})"
|
|
104
|
+
else
|
|
105
|
+
result << " = static_cast<#{qualified_type}>(#{default_value})"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
result
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def method_signature(cursor)
|
|
113
|
+
param_types = @type_speller.type_spellings(cursor)
|
|
114
|
+
result_type = @type_speller.type_spelling(cursor.type.result_type)
|
|
115
|
+
|
|
116
|
+
if cursor.semantic_parent&.kind == :cursor_class_template
|
|
117
|
+
result_type = @type_speller.qualify_class_template_typedefs(result_type, cursor.semantic_parent)
|
|
118
|
+
param_types = param_types.map { |pt| @type_speller.qualify_class_template_typedefs(pt, cursor.semantic_parent) }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
parent = cursor.semantic_parent
|
|
122
|
+
if parent
|
|
123
|
+
result_type = @type_speller.qualify_class_static_members(result_type, parent)
|
|
124
|
+
param_types = param_types.map { |pt| @type_speller.qualify_class_static_members(pt, parent) }
|
|
125
|
+
if same_self_type?(cursor.type.result_type, parent)
|
|
126
|
+
result_type = self_type_spelling(cursor.type.result_type, @type_speller.qualified_display_name(parent))
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
if parent&.kind == :cursor_class_template
|
|
130
|
+
result_type = @type_speller.preserve_template_parameter_names(result_type, parent)
|
|
131
|
+
param_types = param_types.map { |pt| @type_speller.preserve_template_parameter_names(pt, parent) }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
signature = Array.new
|
|
135
|
+
if cursor.kind == :cursor_function || cursor.static?
|
|
136
|
+
signature << "#{result_type}(*)(#{param_types.join(', ')})"
|
|
137
|
+
else
|
|
138
|
+
signature << "#{result_type}(#{@type_speller.qualified_display_name(cursor.semantic_parent)}::*)(#{param_types.join(', ')})"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
signature << "const" if cursor.const?
|
|
142
|
+
signature << "noexcept" if cursor.type.exception_specification == :basic_noexcept
|
|
143
|
+
|
|
144
|
+
result = "<#{signature.join(' ')}>"
|
|
145
|
+
result.match(/std::initializer_list/) ? nil : result
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
|
|
150
|
+
def constructor_type_spellings(cursor)
|
|
151
|
+
cursor.type.arg_types.each_with_index.map do |arg_type, index|
|
|
152
|
+
argument = cursor.argument(index)
|
|
153
|
+
constructor_type_spelling(arg_type, argument&.type)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def constructor_type_spelling(type, argument_type = nil)
|
|
158
|
+
if [:type_constant_array, :type_incomplete_array].include?(type.kind)
|
|
159
|
+
return "#{@type_speller.type_spelling(type.element_type)} *"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
decl = argument_type&.declaration || type.declaration
|
|
163
|
+
is_array_alias = decl &&
|
|
164
|
+
[:cursor_type_alias_decl, :cursor_typedef_decl].include?(decl.kind) &&
|
|
165
|
+
[:type_constant_array, :type_incomplete_array].include?(decl.underlying_type.canonical.kind)
|
|
166
|
+
if is_array_alias
|
|
167
|
+
return "#{@type_speller.type_spelling(decl.underlying_type.element_type)} *"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
@type_speller.type_spelling(type)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def same_self_type?(type, parent)
|
|
174
|
+
check_type = type
|
|
175
|
+
check_type = check_type.non_reference_type if [:type_lvalue_ref, :type_rvalue_ref].include?(check_type.kind)
|
|
176
|
+
normalize_spelling(check_type.spelling) == normalize_spelling(parent.display_name)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def self_type_spelling(type, qualified_parent)
|
|
180
|
+
check_type = type
|
|
181
|
+
suffix = ""
|
|
182
|
+
if check_type.kind == :type_lvalue_ref
|
|
183
|
+
suffix = " &"
|
|
184
|
+
check_type = check_type.non_reference_type
|
|
185
|
+
elsif check_type.kind == :type_rvalue_ref
|
|
186
|
+
suffix = " &&"
|
|
187
|
+
check_type = check_type.non_reference_type
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
prefix = check_type.const_qualified? ? "const " : ""
|
|
191
|
+
"#{prefix}#{qualified_parent}#{suffix}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def normalize_spelling(spelling)
|
|
195
|
+
spelling.to_s.gsub(/\s+/, ' ').strip
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Finds the default value expression for a parameter and returns it with qualified names.
|
|
199
|
+
#
|
|
200
|
+
# Architecture: Separates text extraction from semantic analysis to avoid macro expansion issues.
|
|
201
|
+
# - Text extraction: Uses param.extent.text (original source) to get the default value
|
|
202
|
+
# - Semantic analysis: Uses cursor traversal only to identify what needs qualification
|
|
203
|
+
#
|
|
204
|
+
# This approach is necessary because cursor extent text can reflect macro expansion on some platforms.
|
|
205
|
+
# For example, on Windows UCRT, 'stdout' expands to '__acrt_iob_func', but we want to preserve 'stdout'.
|
|
206
|
+
def find_default_value(param)
|
|
207
|
+
extracted = @reference_qualifier.extract_default_text(param)
|
|
208
|
+
return nil unless extracted
|
|
209
|
+
default_text, default_text_offset = extracted
|
|
210
|
+
|
|
211
|
+
default_value_kinds = [:cursor_unexposed_expr, :cursor_call_expr, :cursor_decl_ref_expr,
|
|
212
|
+
:cursor_c_style_cast_expr, :cursor_cxx_static_cast_expr,
|
|
213
|
+
:cursor_cxx_functional_cast_expr, :cursor_cxx_typeid_expr,
|
|
214
|
+
:cursor_paren_expr] + @cursor_literals
|
|
215
|
+
default_expr = param.find_by_kind(false, *default_value_kinds).find do |expr|
|
|
216
|
+
if expr.kind == :cursor_decl_ref_expr
|
|
217
|
+
ref = expr.referenced
|
|
218
|
+
ref && ref.kind != :cursor_non_type_template_parameter && ref.kind != :cursor_template_type_parameter
|
|
219
|
+
else
|
|
220
|
+
true
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
return nil unless default_expr
|
|
224
|
+
|
|
225
|
+
@reference_qualifier.qualify_source_references(default_expr, default_text, default_text_offset)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|