Class: RubyBindgen::Generators::Rice::TemplateResolver
- Inherits:
-
Object
- Object
- RubyBindgen::Generators::Rice::TemplateResolver
- Defined in:
- lib/ruby-bindgen/generators/rice/template_resolver.rb
Overview
Resolves template specializations, omitted defaults, and inherited template bases using libclang’s semantic APIs plus source-written fallback text when libclang does not expose a complete argument string.
Defined Under Namespace
Classes: TemplateArgumentInfo
Instance Method Summary collapse
-
#full_template_arguments(cursor, underlying_type, template_cursor) ⇒ Object
Get full template arguments including default values.
-
#initialize(reference_qualifier:, type_speller:, namer:) ⇒ TemplateResolver
constructor
A new instance of TemplateResolver.
-
#resolve_base_instantiation(cursor, underlying_type) ⇒ Object
Given a typedef cursor and its underlying type, resolve the base class to an actual instantiated type (e.g., PtrStep<unsigned char> instead of PtrStep<T>).
-
#resolve_base_specifier_spelling(base_specifier, substitutions: {}) ⇒ Object
Resolve a template base specifier by rewriting the written source with semantic names and any current template-parameter substitutions.
-
#ruby_name_from_template(base_spelling, template_arguments) ⇒ Object
Generate Ruby class name from a C++ template instantiation spelling e.g., “Tests::Matx<unsigned char, 2, 1>” -> “MatxUnsignedChar21”.
-
#specialization_spelling(specialization_cursor, specialized_type, template_cursor) ⇒ Object
Build the C++ specialization spelling for a typedef/alias specialization using the semantic template cursor plus the written template arguments.
-
#template_argument_list_text(spelling) ⇒ Object
Extract only the outer template argument list from a fully resolved instantiation spelling.
-
#template_argument_texts(args_text) ⇒ Object
Split a comma-separated template argument list while keeping nested templates, function pointer signatures, and string literals intact.
- #template_parameter_argument(template_parameter) ⇒ Object
-
#template_parameter_signature(template_parameter) ⇒ Object
Render a class template parameter for the instantiate helper’s own template declaration.
- #template_parameters(template_cursor) ⇒ Object
- #usable_template_parameter_declaration?(template_parameter, declaration) ⇒ Boolean
Constructor Details
#initialize(reference_qualifier:, type_speller:, namer:) ⇒ TemplateResolver
Returns a new instance of TemplateResolver.
13 14 15 16 17 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 13 def initialize(reference_qualifier:, type_speller:, namer:) @reference_qualifier = reference_qualifier @type_speller = type_speller @namer = namer end |
Instance Method Details
#full_template_arguments(cursor, underlying_type, template_cursor) ⇒ Object
Get full template arguments including default values. When a typedef uses a template with default arguments, libclang reports only the written arguments. Type arguments come from the semantic type API, while non-type and template-template args fall back to source text so expressions like ‘1 + 2` and names like `Box` are preserved.
Examples:
ExprValue<1 + 2>
stays
ExprValue_instantiate<1 + 2>
Matrix<int, 2>
becomes
Matrix_instantiate<int, 2, 1>
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 33 def full_template_arguments(cursor, , template_cursor) actual_args = specialization_template_arguments(cursor, , template_cursor) return actual_args if template_cursor.nil? params = template_parameters(template_cursor) return actual_args if actual_args.length >= params.length argument_cursor = specialization_argument_cursor() missing_params = params.drop(actual_args.length) default_values = missing_params.each_with_index.map do |param, offset| template_param_default(param, argument_cursor: argument_cursor, argument_index: actual_args.length + offset) end.compact return actual_args if default_values.length != missing_params.length actual_args + default_values end |
#resolve_base_instantiation(cursor, underlying_type) ⇒ Object
Given a typedef cursor and its underlying type, resolve the base class to an actual instantiated type (e.g., PtrStep<unsigned char> instead of PtrStep<T>). Correctly handles cases where derived and base templates have different numbers of template parameters (e.g., Vec<_Tp, cn> : public Matx<_Tp, cn, 1>). Returns the resolved base class spelling or nil if no base class exists.
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 264 def resolve_base_instantiation(cursor, ) derived_template = specialized_template_cursor() if derived_template.nil? template_ref = cursor.find_first_by_kind(false, :cursor_template_ref) derived_template = template_ref&.referenced end return nil unless derived_template base_spec = derived_template.find_first_by_kind(false, :cursor_cxx_base_specifier) return nil unless base_spec base_template_ref = base_spec.find_first_by_kind(false, :cursor_template_ref) unless base_template_ref base_type_ref = base_spec.find_first_by_kind(false, :cursor_type_ref) return base_type_ref&.referenced&.qualified_name end template_params = template_parameters(derived_template).map(&:spelling) template_arg_values = full_template_arguments(cursor, , derived_template) substitutions = {} template_params.each_with_index do |param, index| substitutions[param] = template_arg_values[index] if template_arg_values[index] end resolve_base_specifier_spelling(base_spec, substitutions: substitutions) end |
#resolve_base_specifier_spelling(base_specifier, substitutions: {}) ⇒ Object
Resolve a template base specifier by rewriting the written source with semantic names and any current template-parameter substitutions.
Examples:
substitutions = { 'T' => 'unsigned char' }
source = 'BasePtr<T>'
=> 'Tests::BasePtr<unsigned char>'
substitutions = { 'Result' => 'void', 'Left' => 'int', 'Right' => 'int' }
source = 'CallbackBase<Result (*)(Left, Right)>'
=> 'Tests::CallbackBase<void (*)(int, int)>'
304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 304 def resolve_base_specifier_spelling(base_specifier, substitutions: {}) base_template_ref = base_specifier.find_first_by_kind(false, :cursor_template_ref) unless base_template_ref base_type_ref = base_specifier.find_first_by_kind(false, :cursor_type_ref) return base_type_ref&.referenced&.qualified_name end source = base_specifier_source(base_specifier, base_template_ref) return nil unless source source_text, source_offset = source @reference_qualifier.qualify_source_references(base_specifier, source_text, source_offset, substitutions: substitutions) end |
#ruby_name_from_template(base_spelling, template_arguments) ⇒ Object
Generate Ruby class name from a C++ template instantiation spelling e.g., “Tests::Matx<unsigned char, 2, 1>” -> “MatxUnsignedChar21”
252 253 254 255 256 257 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 252 def ruby_name_from_template(base_spelling, template_arguments) base_name = base_spelling.sub(/<.*>\z/, "").split("::").last.camelize argument_values = template_arguments.is_a?(Array) ? template_arguments : template_argument_texts(template_arguments) args_name = argument_values.map { |argument| ruby_name_from_template_argument(argument) }.join @namer.apply_rename_types(base_name + args_name) end |
#specialization_spelling(specialization_cursor, specialized_type, template_cursor) ⇒ Object
Build the C++ specialization spelling for a typedef/alias specialization using the semantic template cursor plus the written template arguments.
This is narrower than ‘full_template_arguments`: the Data_Type<T> side should preserve the number of arguments written at the use site rather than eagerly expanding omitted defaults. When libclang does not expose a complete argument list for a non-type specialization, fall back to the type speller’s direct output.
Examples:
typedef FunctionTemplate<callback_ints> FunctionTemplateCallback;
=> Tests::FunctionTemplate<&Tests::callback_ints>
typedef MultiDefault<int> MultiDefaultInt;
=> MultiDefault<int>
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 67 def specialization_spelling(specialization_cursor, specialized_type, template_cursor) return @type_speller.type_spelling(specialized_type) unless template_cursor actual_args = specialization_template_arguments(specialization_cursor, specialized_type, template_cursor) count = specialized_type.num_template_arguments return @type_speller.type_spelling(specialized_type) if count <= 0 return @type_speller.type_spelling(specialized_type) unless actual_args.length == count "#{template_cursor.qualified_name}<#{actual_args.join(', ')}>" end |
#template_argument_list_text(spelling) ⇒ Object
Extract only the outer template argument list from a fully resolved instantiation spelling.
Examples:
'Tests::Matx<unsigned char, 2, 1>'
=> 'unsigned char, 2, 1'
'Tests::CallbackBase<void (*)(int, int)>'
=> 'void (*)(int, int)'
240 241 242 243 244 245 246 247 248 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 240 def template_argument_list_text(spelling) start_index = spelling.index('<') return nil unless start_index suffix, = balanced_template_suffix(spelling, start_index) return nil if suffix.empty? suffix[1..-2] end |
#template_argument_texts(args_text) ⇒ Object
Split a comma-separated template argument list while keeping nested templates, function pointer signatures, and string literals intact.
Examples:
'unsigned char, 2, 1'
=> ['unsigned char', '2', '1']
'void (*)(int, int)'
=> ['void (*)(int, int)']
'Support::Box<Support::Tag>, callback_t'
=> ['Support::Box<Support::Tag>', 'callback_t']
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 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 171 def template_argument_texts(args_text) return [] if args_text.nil? || args_text.empty? result = [] current = String.new angle_depth = 0 paren_depth = 0 bracket_depth = 0 brace_depth = 0 quote = nil escaped = false args_text.each_char do |char| current << char if quote if escaped escaped = false elsif char == '\\' escaped = true elsif char == quote quote = nil end next end case char when '"', "'" quote = char when '<' angle_depth += 1 when '>' angle_depth -= 1 if angle_depth > 0 when '(' paren_depth += 1 when ')' paren_depth -= 1 if paren_depth > 0 when '[' bracket_depth += 1 when ']' bracket_depth -= 1 if bracket_depth > 0 when '{' brace_depth += 1 when '}' brace_depth -= 1 if brace_depth > 0 when ',' if angle_depth.zero? && paren_depth.zero? && bracket_depth.zero? && brace_depth.zero? current.chop! piece = current.strip result << piece unless piece.empty? current = String.new end end end piece = current.strip result << piece unless piece.empty? result end |
#template_parameter_argument(template_parameter) ⇒ Object
142 143 144 145 146 147 148 149 150 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 142 def template_parameter_argument(template_parameter) name = template_parameter.spelling return name if name.empty? declaration = template_parameter.extent.text return name unless declaration&.match?(/\.\.\.\s*#{Regexp.escape(name)}\b/) "#{name}..." end |
#template_parameter_signature(template_parameter) ⇒ Object
Render a class template parameter for the instantiate helper’s own template declaration.
Examples:
`typename T`
stays
`typename T`
`int N = 4`
becomes
`int N`
`void (*Fn)(int, int)`
stays
`void (*Fn)(int, int)`
`template<typename> class Container = Box`
becomes
`template<typename> class Container`
`template<typename U = int> class Container = Box`
becomes
`template<typename U = int> class Container`
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 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 108 def template_parameter_signature(template_parameter) case template_parameter.kind when :cursor_template_type_parameter declaration = template_parameter.extent.text declaration = nil unless usable_template_parameter_declaration?(template_parameter, declaration) return "typename #{template_parameter_argument(template_parameter)}" if declaration.nil? || declaration.empty? separator_offset = @reference_qualifier.top_level_default_separator_offset(declaration) signature = separator_offset ? declaration.byteslice(0, separator_offset).rstrip : declaration.rstrip signature.sub(/\A(\s*)class\b/, '\1typename') when :cursor_non_type_template_parameter declaration = template_parameter.extent.text declaration = nil unless usable_template_parameter_declaration?(template_parameter, declaration) return "int #{template_parameter.spelling}" if declaration.nil? || declaration.empty? separator_offset = @reference_qualifier.top_level_default_separator_offset(declaration) return declaration.rstrip unless separator_offset declaration.byteslice(0, separator_offset).rstrip when :cursor_template_template_parameter declaration = template_parameter.extent.text declaration = nil unless usable_template_parameter_declaration?(template_parameter, declaration) return "template<typename> class #{template_parameter.spelling}" if declaration.nil? || declaration.empty? separator_offset = @reference_qualifier.top_level_default_separator_offset(declaration) return declaration.rstrip unless separator_offset declaration.byteslice(0, separator_offset).rstrip else raise("Unsupported template parameter kind: #{template_parameter.kind}") end end |
#template_parameters(template_cursor) ⇒ Object
78 79 80 81 82 83 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 78 def template_parameters(template_cursor) template_parameter_kinds = [:cursor_template_type_parameter, :cursor_non_type_template_parameter, :cursor_template_template_parameter] template_cursor.find_by_kind(false, *template_parameter_kinds).to_a end |
#usable_template_parameter_declaration?(template_parameter, declaration) ⇒ Boolean
152 153 154 155 156 157 |
# File 'lib/ruby-bindgen/generators/rice/template_resolver.rb', line 152 def usable_template_parameter_declaration?(template_parameter, declaration) return false if declaration.nil? || declaration.empty? parent_declaration = template_parameter.semantic_parent&.extent&.text declaration != parent_declaration end |