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,7 @@
1
+ // Generated by ruby-bindgen (<%= RubyBindgen::VERSION %>)
2
+
3
+ #pragma once
4
+
5
+ // To customize, create your own header and specify it with the 'include:' config option
6
+ #include <rice/rice.hpp>
7
+ #include <rice/stl.hpp>
@@ -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