Skip to content

Commit 503244c

Browse files
committed
Extract FunctionPointer and switch IteratorTraits to a class
Lift the callable_reference/requires_callable_cast?/overload_candidate? trio out of rice.rb into RubyBindgen::Generators::FunctionPointer. The cluster builds the C++ pointer-to-function expression Rice needs to bind a method or free function — `&Foo::bar` for non-overloaded names, wrapped in `static_cast<...>(...)` under MSVC when the name refers to an overload set. Renames along the way so the extracted file reads cleanly out of context: * `callable_reference` → `FunctionPointer.format`. The "reference" in the original name collided with C++ references (T&); what the expression actually produces is a function pointer. * `requires_callable_cast?` → `cast_required?` (private). Reads as a question rather than a directive. Also flip both extracted utilities (FunctionPointer and IteratorTraits) from `module ... def self.x` to `class ... def self.x`. Neither is mixed in or extended, so the module keyword was claiming a mixin intent these helpers don't have. Class with `def self.foo` matches the sibling files in this directory (SignatureBuilder, TemplateResolver, TypeSpeller, ReferenceQualifier). Three ERB templates updated to call `FunctionPointer.format(...)`.
1 parent 8f1f944 commit 503244c

5 files changed

Lines changed: 71 additions & 48 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<%- all_args = return_buffer ? (args + ["ReturnBuffer()"]) : args -%>
22
<%- if cursor.static? -%>
3-
<%= is_template && signature && !signature.empty? ? "template " : "" %>define_singleton_function<%= signature %>("<%= name %>", <%= callable_reference(cursor, "#{qualified_parent}::#{cursor.spelling}", signature) %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>
3+
<%= is_template && signature && !signature.empty? ? "template " : "" %>define_singleton_function<%= signature %>("<%= name %>", <%= FunctionPointer.format(cursor, "#{qualified_parent}::#{cursor.spelling}", signature) %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>
44
<%- else -%>
55
<%= is_template && signature && !signature.empty? ? "template " : "" %>define_method<%= signature %>("<%= name %>", &<%= qualified_parent %>::<%= cursor.spelling %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>
66
<%- end -%>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<%- all_args = return_buffer ? (args + ["ReturnBuffer()"]) : args -%>
22
<%- if under -%>
3-
<%= under.cruby_name %>.define_module_function<%= signature %>("<%= name %>", <%= callable_reference(cursor, cursor.qualified_name, signature) %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>;
3+
<%= under.cruby_name %>.define_module_function<%= signature %>("<%= name %>", <%= FunctionPointer.format(cursor, cursor.qualified_name, signature) %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>;
44
<%- else -%>
5-
define_global_function<%= signature %>("<%= name %>", <%= callable_reference(cursor, cursor.qualified_name, signature) %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>;
5+
define_global_function<%= signature %>("<%= name %>", <%= FunctionPointer.format(cursor, cursor.qualified_name, signature) %><%= all_args.empty? ? ")" : ",\n #{all_args.join(", ")})" %>;
66
<%- end -%>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
module RubyBindgen
2+
module Generators
3+
# Build the C++ pointer-to-function expression Rice needs to bind a
4+
# method or free function. Most compilers can resolve `&Foo::bar`
5+
# against the surrounding template-deduced signature even when `bar`
6+
# is overloaded, but MSVC cannot, so when the name refers to an
7+
# overload set we wrap the address in a `static_cast` whose target
8+
# type *is* the signature:
9+
#
10+
# non-overloaded: &Foo::bar
11+
# overloaded MSVC: static_cast<void(int, float)>(&Foo::bar)
12+
class FunctionPointer
13+
# Returns the address-of expression for `cursor`, optionally wrapped
14+
# in a disambiguating `static_cast`.
15+
def self.format(cursor, qualified_name, signature)
16+
reference = "&#{qualified_name}"
17+
return reference unless cast_required?(cursor, signature)
18+
19+
"static_cast<#{signature[1...-1]}>(#{reference})"
20+
end
21+
22+
# True when `cursor` shares its spelling with another overload
23+
# candidate in the same semantic parent — i.e. when MSVC would need
24+
# the cast to pick which overload `&qualified_name` refers to.
25+
def self.cast_required?(cursor, signature)
26+
return false unless signature
27+
return false unless cursor.kind == :cursor_function || cursor.static?
28+
29+
parent = cursor.semantic_parent
30+
return false unless parent
31+
32+
overload_count = 0
33+
parent.each(false) do |sibling, _|
34+
next unless overload_candidate?(cursor, sibling)
35+
36+
overload_count += 1
37+
return true if overload_count > 1
38+
end
39+
40+
false
41+
end
42+
private_class_method :cast_required?
43+
44+
# A sibling counts as another overload of `cursor` only if the
45+
# spellings match AND the kinds are compatible. Free functions
46+
# collide with other free functions and function templates; static
47+
# methods collide with other methods (any static-ness) and method
48+
# templates. Non-static methods are excluded — they're addressed
49+
# as `&Class::method` and Rice dispatches them through a different
50+
# path that doesn't need disambiguation here.
51+
def self.overload_candidate?(cursor, sibling)
52+
return false unless sibling.spelling == cursor.spelling
53+
54+
case cursor.kind
55+
when :cursor_function
56+
[:cursor_function, :cursor_function_template].include?(sibling.kind)
57+
when :cursor_cxx_method
58+
cursor.static? && [:cursor_cxx_method, :cursor_function_template].include?(sibling.kind)
59+
else
60+
false
61+
end
62+
end
63+
private_class_method :overload_candidate?
64+
end
65+
end
66+
end

lib/ruby-bindgen/generators/rice/iterator_traits.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module Generators
99
# iterator. Inputs that already declare the full set of traits, or
1010
# that live in `std::`, or whose value type can't be recovered, are
1111
# left alone — the caller emits no specialization for them.
12-
module IteratorTraits
12+
class IteratorTraits
1313
# Inferred traits for one iterator. The hash key under which the
1414
# caller stores this is the iterator's qualified name; we don't
1515
# repeat it here.

lib/ruby-bindgen/generators/rice/rice.rb

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require 'set'
2+
require_relative 'function_pointer'
23
require_relative 'iterator_traits'
34
require_relative 'reference_qualifier'
45
require_relative 'signature_builder'
@@ -1544,50 +1545,6 @@ def create_project_files
15441545
self.outputter.write(rice_cpp, content)
15451546
end
15461547

1547-
# Return a callable address expression for free/static functions.
1548-
# MSVC needs an explicit cast when the name refers to an overload set,
1549-
# including when a concrete overload coexists with a function template.
1550-
def callable_reference(cursor, qualified_name, signature)
1551-
reference = "&#{qualified_name}"
1552-
return reference unless requires_callable_cast?(cursor, signature)
1553-
1554-
"static_cast<#{signature[1...-1]}>(#{reference})"
1555-
end
1556-
1557-
# Check whether a free/static callable shares its spelling with another
1558-
# overload candidate in the same semantic parent.
1559-
def requires_callable_cast?(cursor, signature)
1560-
return false unless signature
1561-
return false unless cursor.kind == :cursor_function || cursor.static?
1562-
1563-
parent = cursor.semantic_parent
1564-
return false unless parent
1565-
1566-
overload_count = 0
1567-
parent.each(false) do |sibling, _|
1568-
next unless overload_candidate?(cursor, sibling)
1569-
1570-
overload_count += 1
1571-
return true if overload_count > 1
1572-
end
1573-
1574-
false
1575-
end
1576-
1577-
def overload_candidate?(cursor, sibling)
1578-
return false unless sibling.spelling == cursor.spelling
1579-
1580-
case cursor.kind
1581-
when :cursor_function
1582-
[:cursor_function, :cursor_function_template].include?(sibling.kind)
1583-
when :cursor_cxx_method
1584-
cursor.static? && [:cursor_cxx_method, :cursor_function_template].include?(sibling.kind)
1585-
else
1586-
false
1587-
end
1588-
end
1589-
1590-
15911548
# Map a cursor kind such as `:cursor_class_decl` to the corresponding
15921549
# visitor method symbol, for example `:visit_class_decl`.
15931550
def figure_method(cursor)

0 commit comments

Comments
 (0)