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
@@ -0,0 +1,585 @@
1
+ module RubyBindgen
2
+ module Generators
3
+ class Rice
4
+ # Resolves template specializations, omitted defaults, and inherited template
5
+ # bases using libclang's semantic APIs plus source-written fallback text when
6
+ # libclang does not expose a complete argument string.
7
+ class TemplateResolver
8
+ # Captures the semantic and source-written pieces for one template argument.
9
+ # TemplateResolver builds these first, then later code renders them into
10
+ # emitted C++ text.
11
+ TemplateArgumentInfo = Struct.new(:kind, :type, :value, :unsigned_value, :source_text, keyword_init: true)
12
+
13
+ def initialize(reference_qualifier:, type_speller:, namer:)
14
+ @reference_qualifier = reference_qualifier
15
+ @type_speller = type_speller
16
+ @namer = namer
17
+ end
18
+
19
+ # Get full template arguments including default values.
20
+ # When a typedef uses a template with default arguments, libclang reports
21
+ # only the written arguments. Type arguments come from the semantic type
22
+ # API, while non-type and template-template args fall back to source text
23
+ # so expressions like `1 + 2` and names like `Box` are preserved.
24
+ #
25
+ # Examples:
26
+ # ExprValue<1 + 2>
27
+ # stays
28
+ # ExprValue_instantiate<1 + 2>
29
+ #
30
+ # Matrix<int, 2>
31
+ # becomes
32
+ # Matrix_instantiate<int, 2, 1>
33
+ def full_template_arguments(cursor, underlying_type, template_cursor)
34
+ actual_args = specialization_template_arguments(cursor, underlying_type, template_cursor)
35
+
36
+ return actual_args if template_cursor.nil?
37
+
38
+ params = template_parameters(template_cursor)
39
+ return actual_args if actual_args.length >= params.length
40
+
41
+ argument_cursor = specialization_argument_cursor(underlying_type)
42
+ missing_params = params.drop(actual_args.length)
43
+ default_values = missing_params.each_with_index.map do |param, offset|
44
+ template_param_default(param, argument_cursor: argument_cursor, argument_index: actual_args.length + offset)
45
+ end.compact
46
+
47
+ return actual_args if default_values.length != missing_params.length
48
+
49
+ actual_args + default_values
50
+ end
51
+
52
+ # Build the C++ specialization spelling for a typedef/alias specialization
53
+ # using the semantic template cursor plus the written template arguments.
54
+ #
55
+ # This is narrower than `full_template_arguments`: the Data_Type<T> side
56
+ # should preserve the number of arguments written at the use site rather
57
+ # than eagerly expanding omitted defaults. When libclang does not expose a
58
+ # complete argument list for a non-type specialization, fall back to the
59
+ # type speller's direct output.
60
+ #
61
+ # Examples:
62
+ # typedef FunctionTemplate<callback_ints> FunctionTemplateCallback;
63
+ # => Tests::FunctionTemplate<&Tests::callback_ints>
64
+ #
65
+ # typedef MultiDefault<int> MultiDefaultInt;
66
+ # => MultiDefault<int>
67
+ def specialization_spelling(specialization_cursor, specialized_type, template_cursor)
68
+ return @type_speller.type_spelling(specialized_type) unless template_cursor
69
+
70
+ actual_args = specialization_template_arguments(specialization_cursor, specialized_type, template_cursor)
71
+ count = specialized_type.num_template_arguments
72
+ return @type_speller.type_spelling(specialized_type) if count <= 0
73
+ return @type_speller.type_spelling(specialized_type) unless actual_args.length == count
74
+
75
+ "#{template_cursor.qualified_name}<#{actual_args.join(', ')}>"
76
+ end
77
+
78
+ def template_parameters(template_cursor)
79
+ template_parameter_kinds = [:cursor_template_type_parameter,
80
+ :cursor_non_type_template_parameter,
81
+ :cursor_template_template_parameter]
82
+ template_cursor.find_by_kind(false, *template_parameter_kinds).to_a
83
+ end
84
+
85
+ # Render a class template parameter for the instantiate helper's own
86
+ # template declaration.
87
+ #
88
+ # Examples:
89
+ # `typename T`
90
+ # stays
91
+ # `typename T`
92
+ #
93
+ # `int N = 4`
94
+ # becomes
95
+ # `int N`
96
+ #
97
+ # `void (*Fn)(int, int)`
98
+ # stays
99
+ # `void (*Fn)(int, int)`
100
+ #
101
+ # `template<typename> class Container = Box`
102
+ # becomes
103
+ # `template<typename> class Container`
104
+ #
105
+ # `template<typename U = int> class Container = Box`
106
+ # becomes
107
+ # `template<typename U = int> class Container`
108
+ def template_parameter_signature(template_parameter)
109
+ case template_parameter.kind
110
+ when :cursor_template_type_parameter
111
+ declaration = template_parameter.extent.text
112
+ declaration = nil unless usable_template_parameter_declaration?(template_parameter, declaration)
113
+ return "typename #{template_parameter_argument(template_parameter)}" if declaration.nil? || declaration.empty?
114
+
115
+ separator_offset = @reference_qualifier.top_level_default_separator_offset(declaration)
116
+ signature = separator_offset ? declaration.byteslice(0, separator_offset).rstrip : declaration.rstrip
117
+
118
+ signature.sub(/\A(\s*)class\b/, '\1typename')
119
+ when :cursor_non_type_template_parameter
120
+ declaration = template_parameter.extent.text
121
+ declaration = nil unless usable_template_parameter_declaration?(template_parameter, declaration)
122
+ return "int #{template_parameter.spelling}" if declaration.nil? || declaration.empty?
123
+
124
+ separator_offset = @reference_qualifier.top_level_default_separator_offset(declaration)
125
+ return declaration.rstrip unless separator_offset
126
+
127
+ declaration.byteslice(0, separator_offset).rstrip
128
+ when :cursor_template_template_parameter
129
+ declaration = template_parameter.extent.text
130
+ declaration = nil unless usable_template_parameter_declaration?(template_parameter, declaration)
131
+ return "template<typename> class #{template_parameter.spelling}" if declaration.nil? || declaration.empty?
132
+
133
+ separator_offset = @reference_qualifier.top_level_default_separator_offset(declaration)
134
+ return declaration.rstrip unless separator_offset
135
+
136
+ declaration.byteslice(0, separator_offset).rstrip
137
+ else
138
+ raise("Unsupported template parameter kind: #{template_parameter.kind}")
139
+ end
140
+ end
141
+
142
+ def template_parameter_argument(template_parameter)
143
+ name = template_parameter.spelling
144
+ return name if name.empty?
145
+
146
+ declaration = template_parameter.extent.text
147
+ return name unless declaration&.match?(/\.\.\.\s*#{Regexp.escape(name)}\b/)
148
+
149
+ "#{name}..."
150
+ end
151
+
152
+ def usable_template_parameter_declaration?(template_parameter, declaration)
153
+ return false if declaration.nil? || declaration.empty?
154
+
155
+ parent_declaration = template_parameter.semantic_parent&.extent&.text
156
+ declaration != parent_declaration
157
+ end
158
+
159
+ # Split a comma-separated template argument list while keeping nested
160
+ # templates, function pointer signatures, and string literals intact.
161
+ #
162
+ # Examples:
163
+ # 'unsigned char, 2, 1'
164
+ # => ['unsigned char', '2', '1']
165
+ #
166
+ # 'void (*)(int, int)'
167
+ # => ['void (*)(int, int)']
168
+ #
169
+ # 'Support::Box<Support::Tag>, callback_t'
170
+ # => ['Support::Box<Support::Tag>', 'callback_t']
171
+ def template_argument_texts(args_text)
172
+ return [] if args_text.nil? || args_text.empty?
173
+
174
+ result = []
175
+ current = String.new
176
+ angle_depth = 0
177
+ paren_depth = 0
178
+ bracket_depth = 0
179
+ brace_depth = 0
180
+ quote = nil
181
+ escaped = false
182
+
183
+ args_text.each_char do |char|
184
+ current << char
185
+
186
+ if quote
187
+ if escaped
188
+ escaped = false
189
+ elsif char == '\\'
190
+ escaped = true
191
+ elsif char == quote
192
+ quote = nil
193
+ end
194
+ next
195
+ end
196
+
197
+ case char
198
+ when '"', "'"
199
+ quote = char
200
+ when '<'
201
+ angle_depth += 1
202
+ when '>'
203
+ angle_depth -= 1 if angle_depth > 0
204
+ when '('
205
+ paren_depth += 1
206
+ when ')'
207
+ paren_depth -= 1 if paren_depth > 0
208
+ when '['
209
+ bracket_depth += 1
210
+ when ']'
211
+ bracket_depth -= 1 if bracket_depth > 0
212
+ when '{'
213
+ brace_depth += 1
214
+ when '}'
215
+ brace_depth -= 1 if brace_depth > 0
216
+ when ','
217
+ if angle_depth.zero? && paren_depth.zero? && bracket_depth.zero? && brace_depth.zero?
218
+ current.chop!
219
+ piece = current.strip
220
+ result << piece unless piece.empty?
221
+ current = String.new
222
+ end
223
+ end
224
+ end
225
+
226
+ piece = current.strip
227
+ result << piece unless piece.empty?
228
+ result
229
+ end
230
+
231
+ # Extract only the outer template argument list from a fully resolved
232
+ # instantiation spelling.
233
+ #
234
+ # Examples:
235
+ # 'Tests::Matx<unsigned char, 2, 1>'
236
+ # => 'unsigned char, 2, 1'
237
+ #
238
+ # 'Tests::CallbackBase<void (*)(int, int)>'
239
+ # => 'void (*)(int, int)'
240
+ def template_argument_list_text(spelling)
241
+ start_index = spelling.index('<')
242
+ return nil unless start_index
243
+
244
+ suffix, = balanced_template_suffix(spelling, start_index)
245
+ return nil if suffix.empty?
246
+
247
+ suffix[1..-2]
248
+ end
249
+
250
+ # Generate Ruby class name from a C++ template instantiation spelling
251
+ # e.g., "Tests::Matx<unsigned char, 2, 1>" -> "MatxUnsignedChar21"
252
+ def ruby_name_from_template(base_spelling, template_arguments)
253
+ base_name = base_spelling.sub(/<.*>\z/, "").split("::").last.camelize
254
+ argument_values = template_arguments.is_a?(Array) ? template_arguments : template_argument_texts(template_arguments)
255
+ args_name = argument_values.map { |argument| ruby_name_from_template_argument(argument) }.join
256
+ @namer.apply_rename_types(base_name + args_name)
257
+ end
258
+
259
+ # Given a typedef cursor and its underlying type, resolve the base class
260
+ # to an actual instantiated type (e.g., PtrStep<unsigned char> instead of PtrStep<T>).
261
+ # Correctly handles cases where derived and base templates have different numbers of
262
+ # template parameters (e.g., Vec<_Tp, cn> : public Matx<_Tp, cn, 1>).
263
+ # Returns the resolved base class spelling or nil if no base class exists.
264
+ def resolve_base_instantiation(cursor, underlying_type)
265
+ derived_template = specialized_template_cursor(underlying_type)
266
+ if derived_template.nil?
267
+ template_ref = cursor.find_first_by_kind(false, :cursor_template_ref)
268
+ derived_template = template_ref&.referenced
269
+ end
270
+ return nil unless derived_template
271
+
272
+ base_spec = derived_template.find_first_by_kind(false, :cursor_cxx_base_specifier)
273
+ return nil unless base_spec
274
+
275
+ base_template_ref = base_spec.find_first_by_kind(false, :cursor_template_ref)
276
+
277
+ unless base_template_ref
278
+ base_type_ref = base_spec.find_first_by_kind(false, :cursor_type_ref)
279
+ return base_type_ref&.referenced&.qualified_name
280
+ end
281
+
282
+ template_params = template_parameters(derived_template).map(&:spelling)
283
+ template_arg_values = full_template_arguments(cursor, underlying_type, derived_template)
284
+
285
+ substitutions = {}
286
+ template_params.each_with_index do |param, index|
287
+ substitutions[param] = template_arg_values[index] if template_arg_values[index]
288
+ end
289
+
290
+ resolve_base_specifier_spelling(base_spec, substitutions: substitutions)
291
+ end
292
+
293
+ # Resolve a template base specifier by rewriting the written source with
294
+ # semantic names and any current template-parameter substitutions.
295
+ #
296
+ # Examples:
297
+ # substitutions = { 'T' => 'unsigned char' }
298
+ # source = 'BasePtr<T>'
299
+ # => 'Tests::BasePtr<unsigned char>'
300
+ #
301
+ # substitutions = { 'Result' => 'void', 'Left' => 'int', 'Right' => 'int' }
302
+ # source = 'CallbackBase<Result (*)(Left, Right)>'
303
+ # => 'Tests::CallbackBase<void (*)(int, int)>'
304
+ def resolve_base_specifier_spelling(base_specifier, substitutions: {})
305
+ base_template_ref = base_specifier.find_first_by_kind(false, :cursor_template_ref)
306
+
307
+ unless base_template_ref
308
+ base_type_ref = base_specifier.find_first_by_kind(false, :cursor_type_ref)
309
+ return base_type_ref&.referenced&.qualified_name
310
+ end
311
+
312
+ source = base_specifier_source(base_specifier, base_template_ref)
313
+ return nil unless source
314
+
315
+ source_text, source_offset = source
316
+ @reference_qualifier.qualify_source_references(base_specifier, source_text, source_offset, substitutions: substitutions)
317
+ end
318
+
319
+ private
320
+
321
+ def specialized_template_cursor(type)
322
+ [type, type.canonical].each do |check_type|
323
+ declaration = check_type.declaration
324
+ next if declaration.kind == :cursor_no_decl_found
325
+
326
+ template = declaration.specialized_template
327
+ return template unless template.kind == :cursor_invalid_file
328
+ end
329
+
330
+ nil
331
+ end
332
+
333
+ def specialization_template_arguments(specialization_cursor, specialized_type, template_cursor)
334
+ specialization_template_argument_infos(specialization_cursor, specialized_type, template_cursor)
335
+ .filter_map { |argument_info| specialization_template_argument_text(argument_info) }
336
+ end
337
+
338
+ def specialization_argument_cursor(specialized_type)
339
+ declaration = specialized_type.declaration
340
+ return nil if declaration.kind == :cursor_no_decl_found
341
+ return declaration if declaration.num_template_arguments >= 0
342
+
343
+ nil
344
+ end
345
+
346
+ def specialization_template_argument_infos(specialization_cursor, specialized_type, template_cursor)
347
+ argument_cursor = specialization_argument_cursor(specialized_type)
348
+ # The specialized declaration cursor includes omitted default arguments,
349
+ # but the type API reports only the arguments written at the use site.
350
+ count = specialized_type.num_template_arguments
351
+ return [] if count <= 0
352
+
353
+ source_fallbacks = specialization_template_argument_source_texts(specialization_cursor, template_cursor)
354
+
355
+ count.times.filter_map do |index|
356
+ specialization_template_argument_info(argument_cursor, specialized_type, index, source_fallbacks)
357
+ end
358
+ end
359
+
360
+ def specialization_template_argument_info(argument_cursor, specialized_type, index, source_fallbacks)
361
+ specialized_arg_type = specialized_type.template_argument_type(index)
362
+
363
+ if argument_cursor
364
+ kind = argument_cursor.template_argument_kind(index)
365
+ case kind
366
+ when :template_argument_type
367
+ arg_type = argument_cursor.template_argument_type(index)
368
+ arg_type = specialized_arg_type if arg_type.kind == :type_invalid
369
+ return nil if arg_type.kind == :type_invalid
370
+
371
+ return TemplateArgumentInfo.new(kind: kind, type: arg_type)
372
+ when :template_argument_pack, :template_argument_invalid
373
+ # Variadic type aliases can surface here as a pack plus invalid
374
+ # cursor-argument kinds even though the specialized semantic type
375
+ # still exposes each concrete template argument correctly.
376
+ unless specialized_arg_type.kind == :type_invalid
377
+ source_fallbacks.shift
378
+ return TemplateArgumentInfo.new(kind: :template_argument_type, type: specialized_arg_type)
379
+ end
380
+
381
+ return TemplateArgumentInfo.new(kind: kind, source_text: source_fallbacks.shift)
382
+ when :template_argument_integral
383
+ return TemplateArgumentInfo.new(kind: kind,
384
+ value: argument_cursor.template_argument_value(index),
385
+ unsigned_value: argument_cursor.template_argument_unsigned_value(index),
386
+ source_text: source_fallbacks.shift)
387
+ when :template_argument_null_ptr
388
+ return TemplateArgumentInfo.new(kind: kind, source_text: source_fallbacks.shift)
389
+ when :template_argument_template, :template_argument_template_expansion,
390
+ :template_argument_expression, :template_argument_declaration,
391
+ :template_argument_pack
392
+ return TemplateArgumentInfo.new(kind: kind, source_text: source_fallbacks.shift)
393
+ end
394
+ end
395
+
396
+ return nil if specialized_arg_type.kind == :type_invalid
397
+
398
+ TemplateArgumentInfo.new(kind: :template_argument_type, type: specialized_arg_type)
399
+ end
400
+
401
+ def specialization_template_argument_text(argument_info)
402
+ case argument_info.kind
403
+ when :template_argument_type
404
+ @type_speller.type_spelling(argument_info.type)
405
+ when :template_argument_integral
406
+ argument_info.source_text || argument_info.value.to_s
407
+ when :template_argument_null_ptr
408
+ argument_info.source_text || "nullptr"
409
+ when :template_argument_template, :template_argument_template_expansion,
410
+ :template_argument_expression, :template_argument_declaration,
411
+ :template_argument_pack
412
+ argument_info.source_text
413
+ end
414
+ end
415
+
416
+ # Collect source-written template arguments that libclang does not expose
417
+ # through the cursor template-argument APIs.
418
+ #
419
+ # Examples:
420
+ # typedef Vec<unsigned char, 2> Vec2b;
421
+ # => ['2']
422
+ #
423
+ # typedef Holder<int, Support::Box> HolderInt;
424
+ # => ['Support::Box']
425
+ #
426
+ # We skip the outer template name itself and any child cursors that are
427
+ # only details of a type argument, such as the `int` parameter cursors
428
+ # inside `void (*)(int, int)`.
429
+ def specialization_template_argument_source_texts(specialization_cursor, template_cursor)
430
+ specialization_cursor.each(false).filter_map do |child|
431
+ next if child.kind == :cursor_namespace_ref
432
+ next if child.kind == :cursor_parm_decl
433
+ next if child.kind == :cursor_type_ref
434
+ next if child.kind == :cursor_template_ref && child.referenced == template_cursor
435
+
436
+ source_text = child.extent.text
437
+ source_offset = child.extent.start.offset
438
+
439
+ if child.kind == :cursor_template_ref
440
+ range = child.reference_name_range([:want_qualifier, :want_template_args, :want_single_piece])
441
+ source_text = range&.text || source_text
442
+ ref = child.referenced
443
+ if ref && !ref.spelling.empty? && ref.spelling != ref.qualified_name
444
+ source_text = @reference_qualifier.replacement_from_name_span(source_text, ref.spelling, ref.qualified_name) || source_text
445
+ end
446
+ else
447
+ source_text = @reference_qualifier.qualify_source_references(child, source_text, source_offset)
448
+
449
+ ref = child.referenced
450
+ if ref &&
451
+ [:cursor_function, :cursor_function_template, :cursor_cxx_method].include?(ref.kind) &&
452
+ !source_text.lstrip.start_with?('&')
453
+ source_text = "&#{source_text}"
454
+ end
455
+ end
456
+
457
+ source_text
458
+ rescue ArgumentError
459
+ child.extent.text
460
+ end
461
+ end
462
+
463
+ def template_param_default(param, argument_cursor: nil, argument_index: nil)
464
+ case param.kind
465
+ when :cursor_non_type_template_parameter
466
+ qualify_template_non_type_default(param)
467
+ when :cursor_template_type_parameter
468
+ specialization_type_default(argument_cursor, argument_index) || qualify_template_type_default(param)
469
+ when :cursor_template_template_parameter
470
+ qualify_template_template_default(param)
471
+ end
472
+ end
473
+
474
+ def specialization_type_default(argument_cursor, argument_index)
475
+ return nil unless argument_cursor && argument_index
476
+ return nil unless argument_cursor.template_argument_kind(argument_index) == :template_argument_type
477
+
478
+ arg_type = argument_cursor.template_argument_type(argument_index)
479
+ return nil if arg_type.kind == :type_invalid
480
+
481
+ @type_speller.type_spelling(arg_type)
482
+ end
483
+
484
+ def qualify_template_type_default(param)
485
+ extracted = @reference_qualifier.extract_default_text(param)
486
+ return nil unless extracted
487
+
488
+ default_text, default_text_offset = extracted
489
+ @reference_qualifier.qualify_source_references(param, default_text, default_text_offset, qualify_decl_refs: false)
490
+ end
491
+
492
+ def qualify_template_non_type_default(param)
493
+ extracted = @reference_qualifier.extract_default_text(param)
494
+ return nil unless extracted
495
+
496
+ default_text, default_text_offset = extracted
497
+ @reference_qualifier.qualify_source_references(param, default_text, default_text_offset)
498
+ end
499
+
500
+ def qualify_template_template_default(param)
501
+ extracted = @reference_qualifier.extract_default_text(param)
502
+ return nil unless extracted
503
+
504
+ default_text, default_text_offset = extracted
505
+ @reference_qualifier.qualify_source_references(param, default_text, default_text_offset, qualify_decl_refs: false)
506
+ end
507
+
508
+ # Collapse one template argument spelling into a Ruby-safe class-name
509
+ # fragment for auto-generated base classes.
510
+ #
511
+ # Examples:
512
+ # 'unsigned char'
513
+ # => 'UnsignedChar'
514
+ #
515
+ # 'void (*)(int, int)'
516
+ # => 'VoidPtrIntInt'
517
+ #
518
+ # 'Support::Box<Support::Tag>'
519
+ # => 'BoxTag'
520
+ def ruby_name_from_template_argument(argument)
521
+ tokens = argument.scan(/::|&&|&|\*|[A-Za-z_]\w*|\d+/)
522
+
523
+ tokens.each_with_index.filter_map do |token, index|
524
+ next_token = tokens[index + 1]
525
+
526
+ case token
527
+ when '::'
528
+ nil
529
+ when '&&'
530
+ 'RvalueRef'
531
+ when '&'
532
+ 'Ref'
533
+ when '*'
534
+ 'Ptr'
535
+ else
536
+ next if next_token == '::'
537
+
538
+ token.camelize
539
+ end
540
+ end.join
541
+ end
542
+
543
+ # Extract the written base instantiation starting at the template name so
544
+ # access specifiers do not leak into the generated spelling.
545
+ #
546
+ # Examples:
547
+ # 'public CallbackBase<Result (*)(Left, Right)>'
548
+ # => ['CallbackBase<Result (*)(Left, Right)>', offset of the C]
549
+ #
550
+ # 'public Support::ForeignBase<T>'
551
+ # => ['ForeignBase<T>', offset of the F]
552
+ def base_specifier_source(base_specifier, base_template_ref)
553
+ base_extent = base_specifier.extent
554
+ source_text = base_extent.text
555
+ source_offset = base_template_ref.extent.start.offset
556
+ start_index = source_offset - base_extent.start.offset
557
+ return nil if start_index.negative? || start_index >= source_text.bytesize
558
+
559
+ [source_text.byteslice(start_index..), source_offset]
560
+ rescue ArgumentError
561
+ nil
562
+ end
563
+
564
+ def balanced_template_suffix(text, start_index)
565
+ return ["", start_index] unless text[start_index] == '<'
566
+
567
+ depth = 0
568
+ index = start_index
569
+ while index < text.length
570
+ case text[index]
571
+ when '<'
572
+ depth += 1
573
+ when '>'
574
+ depth -= 1
575
+ return [text[start_index..index], index + 1] if depth == 0
576
+ end
577
+ index += 1
578
+ end
579
+
580
+ ["", start_index]
581
+ end
582
+ end
583
+ end
584
+ end
585
+ end
@@ -0,0 +1,40 @@
1
+ // Generated by ruby-bindgen (<%= RubyBindgen::VERSION %>)
2
+
3
+ <%- includes.reject { |inc| inc.include?('.ipp') }.each do |include| -%>
4
+ <%= include %>
5
+ <%- end -%>
6
+ <%- unless incomplete_iterators.empty? -%>
7
+
8
+ // Iterator traits specializations for iterators missing std::iterator_traits
9
+ namespace std
10
+ {
11
+ <%- incomplete_iterators.each do |iterator_name, inference| -%>
12
+ template<>
13
+ struct iterator_traits<<%= iterator_name %>>
14
+ {
15
+ using iterator_category = forward_iterator_tag;
16
+ using value_type = <%= inference.value_type %>;
17
+ using difference_type = ptrdiff_t;
18
+ using pointer = <%= inference.value_type %>*;
19
+ using reference = <%= inference.is_const ? "const #{inference.value_type}&" : "#{inference.value_type}&" %>;
20
+ };
21
+
22
+ <%- end -%>
23
+ }
24
+ <%- end -%>
25
+
26
+ using namespace Rice;
27
+
28
+ <%- includes.select { |inc| inc.include?('.ipp') }.each do |include| -%>
29
+ <%= include %>
30
+ <%- end -%>
31
+ <%- if rice_ipp -%>
32
+ #include "<%= rice_ipp %>"
33
+ <%- else -%>
34
+ <%= class_templates %>
35
+ <%- end -%>
36
+
37
+ void <%= init_name %>()
38
+ {
39
+ <%= content %>
40
+ }
@@ -0,0 +1,7 @@
1
+ // Generated by ruby-bindgen (<%= RubyBindgen::VERSION %>)
2
+
3
+ #pragma once
4
+
5
+ #include "<%= rice_include_header %>"
6
+
7
+ void <%= init_name %>();
@@ -0,0 +1,3 @@
1
+ // Generated by ruby-bindgen (<%= RubyBindgen::VERSION %>)
2
+
3
+ <%= class_templates %>