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,39 @@
1
+ extend FFI::Library
2
+
3
+ def self.library_names
4
+ <%= @library_names %>
5
+ end
6
+
7
+ def self.library_versions
8
+ <%= @library_versions.sort.reverse %>
9
+ end
10
+
11
+ def self.search_names
12
+ result = Array.new
13
+ self.library_names.each do |name|
14
+ result << "lib#{name}"
15
+ self.library_versions.each do |version|
16
+ case RbConfig::CONFIG['host_os']
17
+ when /darwin|mac os/
18
+ result << "lib#{name}.#{version}"
19
+ when /mingw/
20
+ result << "lib#{name}-#{version}"
21
+ when /mswin/
22
+ result << "#{name}_#{version}"
23
+ else
24
+ result << "lib#{name}.so.#{version}"
25
+ end
26
+ end
27
+ end
28
+ result
29
+ end
30
+
31
+ <% if @library_search_path -%>
32
+ if ENV['<%= @library_search_path %>']
33
+ ffi_lib self.search_names.map { |name| File.join(ENV['<%= @library_search_path %>'], name) } + self.search_names
34
+ else
35
+ ffi_lib self.search_names
36
+ end
37
+ <% else -%>
38
+ ffi_lib self.search_names
39
+ <% end -%>
@@ -0,0 +1,18 @@
1
+ # Generated by ruby-bindgen (<%= RubyBindgen::VERSION %>)
2
+
3
+ require 'ffi'
4
+
5
+ <% module_parts.each_with_index do |part, i| -%>
6
+ <%= " " * i %>module <%= part %>
7
+ <% end -%>
8
+ <%= library %>
9
+ <% (module_parts.length - 1).downto(0) do |i| -%>
10
+ <%= " " * i %>end
11
+ <% end -%>
12
+
13
+ <% if version_file -%>
14
+ require_relative '<%= version_file %>'
15
+ <% end -%>
16
+ <% files.each do |file| -%>
17
+ require_relative '<%= file %>'
18
+ <% end -%>
@@ -0,0 +1,6 @@
1
+
2
+ class <%= cursor.ruby_name %> < FFI::Struct
3
+ <%- if cursor.find_by_kind(false, :cursor_field_decl).count > 0 -%>
4
+ layout <%= children %>
5
+ <%- end -%>
6
+ end
@@ -0,0 +1,9 @@
1
+ # Generated by ruby-bindgen (<%= RubyBindgen::VERSION %>)
2
+
3
+ <% module_parts.each_with_index do |part, i| -%>
4
+ <%= " " * i %>module <%= part %>
5
+ <% end -%>
6
+ <%= content %>
7
+ <% (module_parts.length - 1).downto(0) do |i| -%>
8
+ <%= " " * i %>end
9
+ <% end -%>
@@ -0,0 +1 @@
1
+ typedef <%= figure_ffi_type(cursor.underlying_type, :typedef) %>, :<%= cursor.ruby_name %>
@@ -0,0 +1,6 @@
1
+
2
+ class <%= cursor.ruby_name %> < FFI::Union
3
+ <%- if cursor.find_by_kind(false, :cursor_field_decl).count > 0 -%>
4
+ layout <%= children %>
5
+ <%- end -%>
6
+ end
@@ -0,0 +1 @@
1
+ attach_variable :<%= cursor.ruby_name %>, :<%= cursor.spelling %>, <%= figure_ffi_type(cursor.type)%>
@@ -0,0 +1,9 @@
1
+ # Generated by ruby-bindgen (<%= RubyBindgen::VERSION %>)
2
+
3
+ <% module_parts.each_with_index do |part, i| -%>
4
+ <%= " " * i %>module <%= part %>
5
+ <% end -%>
6
+ <%= method_body %>
7
+ <% (module_parts.length - 1).downto(0) do |i| -%>
8
+ <%= " " * i %>end
9
+ <% end -%>
@@ -0,0 +1,5 @@
1
+ def self.<%= method_name %>
2
+ # Return the runtime library version as an integer.
3
+ # Example: 90602 for version 9.6.2
4
+ raise NotImplementedError, "Implement <%= method_name %> to return the runtime library version number"
5
+ end
@@ -0,0 +1,52 @@
1
+ require 'erb'
2
+
3
+ module RubyBindgen
4
+ module Generators
5
+ class Generator
6
+ attr_reader :inputter, :outputter, :config
7
+
8
+ def initialize(inputter, outputter, config)
9
+ @inputter = inputter
10
+ @outputter = outputter
11
+ @config = config
12
+ @project = config[:project]&.gsub(/-/, '_')
13
+ end
14
+
15
+ def project
16
+ @project
17
+ end
18
+
19
+ def generate
20
+ raise NotImplementedError
21
+ end
22
+
23
+ def render_template(template, local_variables = {})
24
+ template_path = File.join(self.class.template_dir, "#{template}.erb")
25
+ template_content = File.read(template_path)
26
+ template = ERB.new(template_content, :trim_mode => '-')
27
+ template.filename = template_path # This allows debase to stop at breakpoints in templates!
28
+ b = self.binding
29
+ local_variables.each do |key, value|
30
+ b.local_variable_set(key, value)
31
+ end
32
+ template.result(b)
33
+ end
34
+
35
+ # Check whether a cursor originates from the translation unit's main file.
36
+ # This intentionally uses libclang file-object equality rather than comparing
37
+ # raw file-name strings. Cursor locations only expose the file path string,
38
+ # so we resolve that path back through `translation_unit.file(...)` and let
39
+ # libclang compare the resulting file objects.
40
+ def translation_unit_file?(cursor)
41
+ file_name = cursor.file_location.file
42
+ translation_unit_file = cursor.translation_unit.file
43
+ file = file_name && cursor.translation_unit.file(file_name)
44
+ file && translation_unit_file && file == translation_unit_file
45
+ end
46
+
47
+ def self.template_dir
48
+ raise NotImplementedError
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ <%- if under -%>
2
+ Rice::Data_Type<<%= base_spelling %>> <%= cruby_name %> = <%= base_template.spelling %>_instantiate<<%= template_arguments %>>(<%= under.cruby_name %>, "<%= ruby_name %>");
3
+ <%- else -%>
4
+ Rice::Data_Type<<%= base_spelling %>> <%= cruby_name %> = <%= base_template.spelling %>_instantiate<<%= template_arguments %>>(Rice::Module(rb_cObject), "<%= ruby_name %>");
5
+ <%- end -%>
@@ -0,0 +1,31 @@
1
+ <%- if has_incomplete_classes -%>
2
+ <%# Split: define class, define incomplete inner classes, then add methods -%>
3
+ <%= auto_generated_base -%>
4
+ <%- if under && base -%>
5
+ Rice::Data_Type<<%= cpp_type %>> <%= cursor.cruby_name %> = define_class_under<<%= cpp_type %>, <%= base %>>(<%= under.cruby_name %>, "<%= ruby_class_name %>");
6
+ <%- elsif under -%>
7
+ Rice::Data_Type<<%= cpp_type %>> <%= cursor.cruby_name %> = define_class_under<<%= cpp_type %>>(<%= under.cruby_name %>, "<%= ruby_class_name %>");
8
+ <%- elsif base -%>
9
+ Rice::Data_Type<<%= cpp_type %>> <%= cursor.cruby_name %> = define_class<<%= cpp_type %>, <%= base %>>("<%= ruby_class_name %>");
10
+ <%- else -%>
11
+ Rice::Data_Type<<%= cpp_type %>> <%= cursor.cruby_name %> = define_class<<%= cpp_type %>>("<%= ruby_class_name %>");
12
+ <%- end -%>
13
+
14
+ <%= incomplete_classes -%>
15
+ <%- unless children == ";" -%>
16
+
17
+ <%= cursor.cruby_name %><%= children -%>
18
+ <%- end -%>
19
+ <%- else -%>
20
+ <%# No incomplete classes - single statement -%>
21
+ <%= auto_generated_base -%>
22
+ <%- if under && base -%>
23
+ Rice::Data_Type<<%= cpp_type %>> <%= cursor.cruby_name %> = define_class_under<<%= cpp_type %>, <%= base %>>(<%= under.cruby_name %>, "<%= ruby_class_name %>")<%= children -%>
24
+ <%- elsif under -%>
25
+ Rice::Data_Type<<%= cpp_type %>> <%= cursor.cruby_name %> = define_class_under<<%= cpp_type %>>(<%= under.cruby_name %>, "<%= ruby_class_name %>")<%= children -%>
26
+ <%- elsif base -%>
27
+ Rice::Data_Type<<%= cpp_type %>> <%= cursor.cruby_name %> = define_class<<%= cpp_type %>, <%= base %>>("<%= ruby_class_name %>")<%= children -%>
28
+ <%- else -%>
29
+ Rice::Data_Type<<%= cpp_type %>> <%= cursor.cruby_name %> = define_class<<%= cpp_type %>>("<%= ruby_class_name %>")<%= children -%>
30
+ <%- end -%>
31
+ <%- end -%>
@@ -0,0 +1,9 @@
1
+ template<<%= template_signature %>>
2
+ inline Rice::Data_Type<<%= fully_qualified_type %>> <%= cursor.spelling %>_instantiate(Rice::Module parent, const char* name)
3
+ {
4
+ <%- if base_spelling -%>
5
+ return Rice::define_class_under<<%= fully_qualified_type %>, <%= base_spelling %>>(parent, name)<%= children %>
6
+ <%- else -%>
7
+ return Rice::define_class_under<<%= fully_qualified_type %>>(parent, name)<%= children %>
8
+ <%- end -%>
9
+ }
@@ -0,0 +1,10 @@
1
+ <%- var_name = defined?(cruby_name) ? cruby_name : cursor.cruby_name -%>
2
+ <%- # Use typedef/using name if cursor is a typedef, otherwise generate from template -%>
3
+ <%- raw_name = if cursor.kind == :cursor_typedef_decl || cursor.kind == :cursor_type_alias_decl
4
+ cursor.spelling.camelize
5
+ else
6
+ template_specialization.gsub(/<.*>/, "").split("::").last.camelize + template_arguments.split(",").map(&:strip).map { |t| t.gsub(/[^a-zA-Z0-9]/, " ").split.map(&:capitalize).join }.join
7
+ end -%>
8
+ <%- ruby_class_name = @namer.apply_rename_types(raw_name) -%>
9
+ <%- parent_module = under ? under.cruby_name : "Rice::Module(rb_cObject)" -%>
10
+ Rice::Data_Type<<%= template_specialization %>> <%= var_name %> = <%= cursor_template.spelling %>_instantiate<<%= template_arguments %>>(<%= parent_module %>, "<%= ruby_class_name %>");
@@ -0,0 +1,9 @@
1
+ <%- if cursor.semantic_parent.kind == :cursor_translation_unit ||
2
+ cursor.semantic_parent.kind == :cursor_namespace ||
3
+ cursor.semantic_parent.anonymous? -%>
4
+ <%= cursor.semantic_parent.cruby_name %>.define_constant("<%= name %>", <%= qualified_name %>);
5
+ <%- elsif cursor.kind == :cursor_macro_definition -%>
6
+ Class(rb_cObject).define_constant("<%= name %>", <%= qualified_name %>);
7
+ <%- else -%>
8
+ define_constant("<%= name %>", <%= qualified_name %>)
9
+ <%- end -%>
@@ -0,0 +1 @@
1
+ define_constructor(Constructor<<%= signature %>>()<%= args.empty? ? ")" : ",\n #{args.join(", ")})" %>
@@ -0,0 +1,4 @@
1
+ define_method("<%= ruby_name %>", [](<%= is_const ? "const " : "" %><%= qualified_parent %>& self) -> <%= result_type %>
2
+ {
3
+ return static_cast<<%= result_type %>>(self);
4
+ })
@@ -0,0 +1 @@
1
+ <%= is_template && signature && !signature.empty? ? "template " : "" %>define_iterator<%= signature %>(&<%= qualified_parent %>::<%= begin_method %>, &<%= qualified_parent %>::<%= end_method %>, "<%= name %>")
@@ -0,0 +1,6 @@
1
+ <%- all_args = return_buffer ? (args + ["ReturnBuffer()"]) : args -%>
2
+ <%- if cursor.static? -%>
3
+ <%= is_template && signature && !signature.empty? ? "template " : "" %>define_singleton_function<%= signature %>("<%= name %>", <%= RubyBindgen::Generators::Rice::FunctionPointer.format(cursor, "#{qualified_parent}::#{cursor.spelling}", signature) %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>
4
+ <%- else -%>
5
+ <%= is_template && signature && !signature.empty? ? "template " : "" %>define_method<%= signature %>("<%= name %>", &<%= qualified_parent %>::<%= cursor.spelling %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>
6
+ <%- end -%>
@@ -0,0 +1,7 @@
1
+ <%- if anonymous_class_scope -%>
2
+ define_constant("<%= cursor.spelling.upcase_first %>", (int)<%= qualified_name %>)
3
+ <%- elsif anonymous_parent -%>
4
+ <%= owner_cruby_name %>.define_constant("<%= cursor.spelling.upcase_first %>", (int)<%= qualified_name %>);
5
+ <%- else -%>
6
+ define_value("<%= cursor.spelling %>", <%= value_name %>)
7
+ <%- end -%>
@@ -0,0 +1,6 @@
1
+ <%- if cursor.anonymous? -%>
2
+ <%- elsif under -%>
3
+ Enum<<%= cursor.qualified_display_name %>> <%= cursor.cruby_name %> = define_enum_under<<%= cursor.qualified_display_name %>>("<%= cursor.ruby_name %>", <%= under.cruby_name %>)<%= children -%>
4
+ <%- else -%>
5
+ Enum<<%= cursor.qualified_display_name %>> <%= cursor.cruby_name %> = define_enum<<%= cursor.qualified_display_name %>>("<%= cursor.ruby_name %>")<%= children -%>
6
+ <%- end -%>
@@ -0,0 +1,8 @@
1
+ <% decl = cursor.type.declaration -%>
2
+ <% if cursor.type.const_qualified? || cursor.type.is_a?(::FFI::Clang::Types::Array) -%>
3
+ define_attr("<%= cursor.ruby_name %>", &<%= qualified_parent %>::<%= cursor.spelling %>, Rice::AttrAccess::Read)
4
+ <% elsif decl.copy_assignable? -%>
5
+ define_attr("<%= cursor.ruby_name %>", &<%= qualified_parent %>::<%= cursor.spelling %>)
6
+ <% else -%>
7
+ define_attr("<%= cursor.ruby_name %>", &<%= qualified_parent %>::<%= cursor.spelling %>, Rice::AttrAccess::Read)
8
+ <% end -%>
@@ -0,0 +1,6 @@
1
+ <%- all_args = return_buffer ? (args + ["ReturnBuffer()"]) : args -%>
2
+ <%- if under -%>
3
+ <%= under.cruby_name %>.define_module_function<%= signature %>("<%= name %>", <%= RubyBindgen::Generators::Rice::FunctionPointer.format(cursor, cursor.qualified_name, signature) %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>;
4
+ <%- else -%>
5
+ define_global_function<%= signature %>("<%= name %>", <%= RubyBindgen::Generators::Rice::FunctionPointer.format(cursor, cursor.qualified_name, signature) %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>;
6
+ <%- end -%>
@@ -0,0 +1,68 @@
1
+ module RubyBindgen
2
+ module Generators
3
+ class Rice
4
+ # Build the C++ pointer-to-function expression Rice needs to bind a
5
+ # method or free function. Most compilers can resolve `&Foo::bar`
6
+ # against the surrounding template-deduced signature even when `bar`
7
+ # is overloaded, but MSVC cannot, so when the name refers to an
8
+ # overload set we wrap the address in a `static_cast` whose target
9
+ # type *is* the signature:
10
+ #
11
+ # non-overloaded: &Foo::bar
12
+ # overloaded MSVC: static_cast<void(int, float)>(&Foo::bar)
13
+ class FunctionPointer
14
+ # Returns the address-of expression for `cursor`, optionally wrapped
15
+ # in a disambiguating `static_cast`.
16
+ def self.format(cursor, qualified_name, signature)
17
+ reference = "&#{qualified_name}"
18
+ return reference unless cast_required?(cursor, signature)
19
+
20
+ "static_cast<#{signature[1...-1]}>(#{reference})"
21
+ end
22
+
23
+ # True when `cursor` shares its spelling with another overload
24
+ # candidate in the same semantic parent — i.e. when MSVC would need
25
+ # the cast to pick which overload `&qualified_name` refers to.
26
+ def self.cast_required?(cursor, signature)
27
+ return false unless signature
28
+ return false unless cursor.kind == :cursor_function || cursor.static?
29
+
30
+ parent = cursor.semantic_parent
31
+ return false unless parent
32
+
33
+ overload_count = 0
34
+ parent.each(false) do |sibling, _|
35
+ next unless overload_candidate?(cursor, sibling)
36
+
37
+ overload_count += 1
38
+ return true if overload_count > 1
39
+ end
40
+
41
+ false
42
+ end
43
+ private_class_method :cast_required?
44
+
45
+ # A sibling counts as another overload of `cursor` only if the
46
+ # spellings match AND the kinds are compatible. Free functions
47
+ # collide with other free functions and function templates; static
48
+ # methods collide with other methods (any static-ness) and method
49
+ # templates. Non-static methods are excluded — they're addressed
50
+ # as `&Class::method` and Rice dispatches them through a different
51
+ # path that doesn't need disambiguation here.
52
+ def self.overload_candidate?(cursor, sibling)
53
+ return false unless sibling.spelling == cursor.spelling
54
+
55
+ case cursor.kind
56
+ when :cursor_function
57
+ [:cursor_function, :cursor_function_template].include?(sibling.kind)
58
+ when :cursor_cxx_method
59
+ cursor.static? && [:cursor_cxx_method, :cursor_function_template].include?(sibling.kind)
60
+ else
61
+ false
62
+ end
63
+ end
64
+ private_class_method :overload_candidate?
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ <%# Template for forward-declared (incomplete) inner classes -%>
2
+ <%# These need to be registered with Rice so that types like Ptr<Impl> work -%>
3
+ Rice::Data_Type<<%= cursor.qualified_name %>> <%= cursor.cruby_name %> = define_class_under<<%= cursor.qualified_name %>>(<%= under.cruby_name %>, "<%= @namer.apply_rename_types(cursor.spelling, cursor.spelling.camelize) %>");
@@ -0,0 +1 @@
1
+ Rice::detail::protect(rb_alias, <%= cruby_name %>, rb_intern("each"), rb_intern("each_const"));
@@ -0,0 +1,159 @@
1
+ require 'set'
2
+
3
+ module RubyBindgen
4
+ module Generators
5
+ class Rice
6
+ # Tracks iterator-related state across one translation-unit visit:
7
+ #
8
+ # * Which Ruby iterator names a class exposes (each, each_const,
9
+ # each_reverse, each_reverse_const). Drives the each_const-only
10
+ # `alias each each_const` emission.
11
+ # * Which custom iterator types still need a generated
12
+ # `std::iterator_traits` specialization (for iterators that don't
13
+ # specialize std::iterator_traits themselves), de-duplicated
14
+ # across the multiple begin/rbegin callsites that hit the same
15
+ # iterator type.
16
+ #
17
+ # `record(cursor)` is the single entry point per iterator method.
18
+ # `clear` resets between translation units.
19
+ class IteratorCollector
20
+ # Inferred traits for one custom iterator. The hash key under which
21
+ # this is stored is the iterator's qualified name; we don't repeat
22
+ # it here. Consumed directly by the ERB template.
23
+ Inference = Data.define(:value_type, :is_const)
24
+
25
+ # Returns the qualified-name → Inference map of iterators that need
26
+ # a generated std::iterator_traits specialization.
27
+ attr_reader :incomplete_iterators
28
+
29
+ def initialize
30
+ @incomplete_iterators = {}
31
+ @iterator_names_by_class = Hash.new { |h, k| h[k] = Set.new }
32
+ end
33
+
34
+ def clear
35
+ @incomplete_iterators.clear
36
+ @iterator_names_by_class.clear
37
+ end
38
+
39
+ # Record an iterator method on its parent class. Returns the Ruby
40
+ # iterator name to render (e.g. "each", "each_const") or nil for
41
+ # variants we deliberately skip:
42
+ #
43
+ # * cbegin/crbegin — would emit a duplicate each_const /
44
+ # each_reverse_const since C++ allows them on non-const
45
+ # receivers but Ruby has no const distinction.
46
+ # * end/cend/rend/crend — handled implicitly by Rice's
47
+ # define_iterator.
48
+ def record(cursor)
49
+ ruby_name = ruby_iterator_name(cursor)
50
+ return nil unless ruby_name
51
+
52
+ @iterator_names_by_class[cursor.semantic_parent.cruby_name] << ruby_name
53
+ record_traits_if_needed(cursor.result_type)
54
+ ruby_name
55
+ end
56
+
57
+ # True when `cruby_name` exposes only const iteration. The caller
58
+ # emits an `alias each each_const` so Ruby code can iterate without
59
+ # spelling the const variant.
60
+ def each_const_only?(cruby_name)
61
+ names = @iterator_names_by_class[cruby_name]
62
+ names.include?("each_const") && !names.include?("each")
63
+ end
64
+
65
+ private
66
+
67
+ def ruby_iterator_name(cursor)
68
+ case cursor.spelling
69
+ when "begin" then cursor.const? ? "each_const" : "each"
70
+ when "rbegin" then cursor.const? ? "each_reverse_const" : "each_reverse"
71
+ end
72
+ end
73
+
74
+ def record_traits_if_needed(iterator_type)
75
+ inference = infer_traits(iterator_type)
76
+ return unless inference
77
+
78
+ # Key by qualified iterator name so the same iterator hit via
79
+ # different begin/rbegin callsites gets one specialization.
80
+ @incomplete_iterators[iterator_type.declaration.qualified_name] = inference
81
+ end
82
+
83
+ # Returns an Inference if `iterator_type` needs a generated
84
+ # std::iterator_traits specialization, or nil if it doesn't.
85
+ # Inputs that already declare the full trait set, that live in
86
+ # `std::`, or whose value type can't be recovered are left alone.
87
+ def infer_traits(iterator_type)
88
+ decl = iterator_type.declaration
89
+ return nil if decl.kind == :cursor_no_decl_found
90
+ return nil if decl.qualified_name&.start_with?('std::')
91
+
92
+ # If the type is reached through a typedef (e.g.
93
+ # `typedef SparseMatConstIterator_<uchar> SparseMatConstIterator`)
94
+ # walk to the underlying class so trait/operator lookup hits
95
+ # the actual class members rather than the typedef cursor.
96
+ if decl.kind == :cursor_typedef_decl || decl.kind == :cursor_type_alias_decl
97
+ underlying = iterator_type.canonical.declaration
98
+ decl = underlying if underlying.kind != :cursor_no_decl_found
99
+ end
100
+
101
+ return nil if traits_already_complete?(decl)
102
+
103
+ value_type = infer_value_type(decl)
104
+ return nil unless value_type
105
+
106
+ Inference.new(
107
+ value_type: value_type_qualified_name(value_type),
108
+ is_const: value_type.const_qualified?
109
+ )
110
+ end
111
+
112
+ REQUIRED_TRAITS = %w[
113
+ value_type
114
+ reference
115
+ pointer
116
+ difference_type
117
+ iterator_category
118
+ ].freeze
119
+ private_constant :REQUIRED_TRAITS
120
+
121
+ def traits_already_complete?(decl)
122
+ present = Set.new
123
+ decl.each(false) do |child, _|
124
+ next unless child.kind == :cursor_type_alias_decl ||
125
+ child.kind == :cursor_typedef_decl
126
+ present << child.spelling if REQUIRED_TRAITS.include?(child.spelling)
127
+ end
128
+ present.size == REQUIRED_TRAITS.size
129
+ end
130
+
131
+ # Recursive iteration so inherited operator* on a base class is
132
+ # found. Iterators without operator* (e.g. OpenCV's
133
+ # SparseMatConstIterator which exposes node()) cannot have traits
134
+ # inferred and must be skipped via the symbols config.
135
+ def infer_value_type(decl)
136
+ decl.each do |child, _|
137
+ next unless child.kind == :cursor_cxx_method &&
138
+ child.spelling == "operator*"
139
+ # non_reference_type is a no-op on non-references, so this
140
+ # handles `T`, `T &`, and `const T &` uniformly.
141
+ return child.result_type.non_reference_type
142
+ end
143
+ nil
144
+ end
145
+
146
+ # Prefer the value type's qualified name from its declaration;
147
+ # fall back to the unqualified spelling for primitives (no decl).
148
+ def value_type_qualified_name(value_type)
149
+ decl = value_type.declaration
150
+ if decl && decl.kind != :cursor_no_decl_found
151
+ decl.qualified_name
152
+ else
153
+ value_type.unqualified_type.spelling
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,5 @@
1
+ <%- if under -%>
2
+ Module <%= cursor.cruby_name %> = define_module_under(<%= under.cruby_name %>, "<%= cursor.ruby_name %>");
3
+ <%- else -%>
4
+ Module <%= cursor.cruby_name %> = define_module("<%= cursor.ruby_name %>");
5
+ <%- end -%>
@@ -0,0 +1,4 @@
1
+ define_method("<%= ruby_name %>", [](<%= arg0_type %> self, <%= arg1_type %> other) -> <%= result_type %>
2
+ {
3
+ <%= return_stmt %>
4
+ })
@@ -0,0 +1,6 @@
1
+ define_method("inspect", [](<%= arg_type %> self) -> std::string
2
+ {
3
+ std::ostringstream stream;
4
+ stream << self;
5
+ return stream.str();
6
+ })
@@ -0,0 +1,4 @@
1
+ define_method("<%= ruby_name %>", [](<%= arg0_type %> self) -> <%= result_type %>
2
+ {
3
+ return <%= op_symbol %>self;
4
+ })
@@ -0,0 +1,4 @@
1
+ define_method("[]=", [](<%= qualified_parent %>&self, <%= index_type %> <%= index_name %>, <%= value_type %> value)
2
+ {
3
+ self[<%= index_name %>] = value;
4
+ })
@@ -0,0 +1,18 @@
1
+ // Generated by ruby-bindgen (<%= RubyBindgen::VERSION %>)
2
+
3
+ #include "<%= project_header %>"
4
+
5
+ <% init_names.keys.each do |init_name| -%>
6
+ #include "<%= init_name %>"
7
+ <% end -%>
8
+
9
+ extern "C"
10
+ void <%= init_name %>()
11
+ {
12
+ return Rice::detail::cpp_protect([]
13
+ {
14
+ <% init_names.values.each do |init_function| -%>
15
+ <%= init_function %>();
16
+ <% end -%>
17
+ });
18
+ }
@@ -0,0 +1,13 @@
1
+ // Generated by ruby-bindgen (<%= RubyBindgen::VERSION %>)
2
+
3
+ #pragma once
4
+
5
+ #include <rice/rice.hpp>
6
+
7
+ extern "C"
8
+ #if defined(_WIN32)
9
+ __declspec(dllexport)
10
+ #else
11
+ __attribute__((visibility("default")))
12
+ #endif
13
+ void <%= init_name %>();