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,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 @@
|
|
|
1
|
+
typedef <%= figure_ffi_type(cursor.underlying_type, :typedef) %>, :<%= cursor.ruby_name %>
|
|
@@ -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,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 @@
|
|
|
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,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
|
+
}
|