Class: RubyBindgen::Generators::Rice::TypeSpeller

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby-bindgen/generators/rice/type_speller.rb

Overview

Builds fully qualified C++ type spellings for generated Rice code and applies the extra template/class qualification rules that libclang does not handle on its own for emitted binding code.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type_index:) ⇒ TypeSpeller

Returns a new instance of TypeSpeller.



12
13
14
15
# File 'lib/ruby-bindgen/generators/rice/type_speller.rb', line 12

def initialize(type_index:)
  @type_index = type_index
  clear
end

Instance Attribute Details

#printing_policy=(value) ⇒ Object (writeonly)

Sets the attribute printing_policy

Parameters:

  • value

    the value to set the attribute printing_policy to.



10
11
12
# File 'lib/ruby-bindgen/generators/rice/type_speller.rb', line 10

def printing_policy=(value)
  @printing_policy = value
end

Instance Method Details

#clearObject



17
18
19
20
# File 'lib/ruby-bindgen/generators/rice/type_speller.rb', line 17

def clear
  @class_template_typedefs = {}
  @class_static_members = {}
end

#preserve_template_parameter_names(spelling, template_cursor) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/ruby-bindgen/generators/rice/type_speller.rb', line 207

def preserve_template_parameter_names(spelling, template_cursor)
  return spelling unless template_cursor&.kind == :cursor_class_template
  
  result = spelling.dup
  template_parameter_names(template_cursor).each do |name|
    qualified_name = @type_index.qualified_name_for(name)
    next if qualified_name.nil? || qualified_name == name
  
    result = result.gsub(/(?<![:\w])#{Regexp.escape(qualified_name)}(?![:\w])/, name)
  end
  result
end

#qualified_class_name(cursor) ⇒ Object

Get the fully qualified class/struct type used in generated bindings.

Examples:

outer::Locale::Facet<Locale>

becomes

outer::Locale::Facet<outer::Locale>

This goes through type_spelling(cursor.type) instead of cursor-specific string surgery. A class cursor’s first child can be an unrelated type_ref, which makes first-match substitution double-qualify nested specializations like outer::outer::Locale::Facet<outer::Locale>.



33
34
35
# File 'lib/ruby-bindgen/generators/rice/type_speller.rb', line 33

def qualified_class_name(cursor)
  type_spelling(cursor.type)
end

#qualified_display_name(cursor) ⇒ Object

Get qualified display name for a cursor. Qualifies any template arguments that need namespace prefixes. Used for generating fully qualified names in enum constants, etc.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/ruby-bindgen/generators/rice/type_speller.rb', line 40

def qualified_display_name(cursor)
  if cursor&.kind == :cursor_class_template
    template_arguments = template_parameter_arguments(cursor)
    return "#{cursor.qualified_name}<#{template_arguments.join(', ')}>" unless template_arguments.empty?
  end
  
  display_name = qualify_template_parameter_packs(cursor.qualified_display_name, cursor)
  
  # For members of template specializations, use the parent's type for qualification
  # e.g., TypeTraits<lowercase_type>::type needs lowercase_type qualified,
  # but cursor.type is just 'const int' which has no template args
  type = cursor.type
  parent = cursor.semantic_parent
  if parent && parent.type.num_template_arguments > 0
    type = parent.type
  end
  qualify_template_args(display_name, type,
                        ignored_names: template_parameter_names(cursor))
end

#qualify_class_static_members(spelling, class_cursor) ⇒ Object

Qualify bare class members used as non-type template args. Within a class like GPCPatchDescriptor, a member can write Vec<double, nFeatures> but the generated binding code is outside the class, so it needs Vec<double, GPCPatchDescriptor::nFeatures>. qualify_template_args then handles qualifying GPCPatchDescriptor to cv::optflow::GPCPatchDescriptor.

Unscoped enum constants need the same treatment:

FixedBuffer<int, Size>

becomes

FixedBuffer<int, Tests::EnumSized<N>::Size>

Class templates need their template parameters preserved:

FixedBuffer<int, Size>

becomes

FixedBuffer<int, Tests::StaticSized<N>::Size>


161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/ruby-bindgen/generators/rice/type_speller.rb', line 161

def qualify_class_static_members(spelling, class_cursor)
  return spelling unless class_cursor
  
  parent_kind = class_cursor.kind
  return spelling unless parent_kind == :cursor_class_decl ||
                        parent_kind == :cursor_struct ||
                        parent_kind == :cursor_class_template
  
  cache_key = class_cursor.usr
  member_info = @class_static_members[cache_key] ||= begin
    names = []
    class_cursor.each(false) do |child|
      if child.kind == :cursor_variable
        names << child.spelling
      elsif child.kind == :cursor_enum_decl && !child.enum_scoped?
        child.each(false) do |enum_child|
          names << enum_child.spelling if enum_child.kind == :cursor_enum_constant_decl
        end
      end
    end
    qualified_parent = if parent_kind == :cursor_class_template
                         qualified_display_name(class_cursor)
                       else
                         class_cursor.qualified_name
                       end
    { names: names, qualified_parent: qualified_parent }
  end
  
  return spelling if member_info[:names].empty?
  
  result = spelling.dup
  qualified_parent = member_info[:qualified_parent]
  display_name = class_cursor.display_name
  member_info[:names].each do |name|
    if display_name && !display_name.empty? && display_name != qualified_parent
      partially_qualified = /(?<![:\w])#{Regexp.escape(display_name)}::#{Regexp.escape(name)}(?![:\w])/
      result = result.gsub(partially_qualified, "#{qualified_parent}::#{name}")
    end
  
    unqualified = /(?<![:\w])#{Regexp.escape(name)}(?![:\w])/
    result = result.gsub(unqualified, "#{qualified_parent}::#{name}")
  end
  
  result
end

#qualify_class_template_typedefs(spelling, class_template) ⇒ Object

Qualify nested typedefs from a class template in a type spelling e.g., “std::reverse_iterator<iterator>” -> “std::reverse_iterator<cv::Mat_<_Tp>::iterator>”



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/ruby-bindgen/generators/rice/type_speller.rb', line 97

def qualify_class_template_typedefs(spelling, class_template)
  return spelling unless class_template&.kind == :cursor_class_template
  
  cache_key = class_template.usr
  typedef_info = @class_template_typedefs[cache_key] ||= begin
    names = []
    class_template.each(false) do |child|
      child_kind = child.kind
      if child_kind == :cursor_typedef_decl || child_kind == :cursor_type_alias_decl
        names << child.spelling
      end
    end
    { names: names, qualified_parent: class_template.qualified_display_name }
  end
  
  return spelling if typedef_info[:names].empty?
  
  result = spelling.dup
  qualified_parent = typedef_info[:qualified_parent]
  qualified_name = class_template.qualified_name
  display_name = class_template.display_name
  simple_name = class_template.spelling
  typedef_info[:names].each do |name|
    fully_qualified = /(?<![:\w])(?:typename\s+)?#{Regexp.escape(qualified_parent)}::#{Regexp.escape(name)}(?![:\w])/
    result = result.gsub(fully_qualified, "typename #{qualified_parent}::#{name}")
  
    if qualified_name && !qualified_name.empty? && qualified_name != qualified_parent
      qualified_without_args = /(?<![:\w])#{Regexp.escape(qualified_name)}::#{Regexp.escape(name)}(?![:\w])/
      result = result.gsub(qualified_without_args, "typename #{qualified_parent}::#{name}")
    end
  
    if display_name && !display_name.empty? && display_name != qualified_parent
      partially_qualified = /(?<![:\w])#{Regexp.escape(display_name)}::#{Regexp.escape(name)}(?![:\w])/
      result = result.gsub(partially_qualified, "typename #{qualified_parent}::#{name}")
    end
  
    if simple_name && !simple_name.empty?
      uninstantiated_class = /(?<![:\w])#{Regexp.escape(simple_name)}::#{Regexp.escape(name)}(?![:\w])/
      result = result.gsub(uninstantiated_class, "typename #{qualified_parent}::#{name}")
    end
  
    unqualified = /(?<![:\w])#{Regexp.escape(name)}(?![:\w])/
    result = result.gsub(unqualified, "typename #{qualified_parent}::#{name}")
  end
  
  result
end

#type_spelling(type) ⇒ Object

Returns a fully-qualified C++ type spelling suitable for use in generated Rice bindings. Most type kinds are handled by Type#fully_qualified_name. This method only intercepts declared types that need generator-level context:

  • Class template types: fqn resolves template params, but template builders need them generic

  • Typedefs inside class templates: need ‘typename’ keyword for dependent types

  • Template instantiations: need qualify_template_args post-processing with TypeIndex



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/ruby-bindgen/generators/rice/type_speller.rb', line 72

def type_spelling(type)
  case type.kind
  when :type_pointer
    type_spelling_pointer(type)
  when :type_lvalue_ref
    "#{type_spelling(type.non_reference_type)} &"
  when :type_rvalue_ref
    "#{type_spelling(type.non_reference_type)} &&"
  when :type_constant_array
    "#{type_spelling(type.element_type)}[#{type.size}]"
  when :type_incomplete_array
    "#{type_spelling(type.element_type)}[]"
  when :type_elaborated
    type_spelling_declared(type)
  when :type_typedef
    type_spelling_declared(type)
  when :type_unexposed
    type_spelling_unexposed(type)
  else
    type.fully_qualified_name(@printing_policy)
  end
end

#type_spellings(cursor) ⇒ Object



60
61
62
63
64
# File 'lib/ruby-bindgen/generators/rice/type_speller.rb', line 60

def type_spellings(cursor)
  cursor.type.arg_types.map do |arg_type|
    type_spelling(arg_type)
  end
end