|
| 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 |
0 commit comments