Class: FFI::Clang::Types::Type

Inherits:
Object
  • Object
show all
Defined in:
lib/ffi/clang/types/type.rb

Overview

Represents a type in the C/C++ type system. This class wraps libclang’s type representation and provides methods to query type properties.

Direct Known Subclasses

Array, Elaborated, Function, Pointer, Record, TypeDef, Vector

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type, translation_unit) ⇒ Type

Create a new type instance.



51
52
53
54
# File 'lib/ffi/clang/types/type.rb', line 51

def initialize(type, translation_unit)
	@type = type
	@translation_unit = translation_unit
end

Instance Attribute Details

#translation_unitObject (readonly)

Returns the value of attribute translation_unit.



21
# File 'lib/ffi/clang/types/type.rb', line 21

attr_reader :type, :translation_unit

#typeObject (readonly)



21
22
23
# File 'lib/ffi/clang/types/type.rb', line 21

def type
  @type
end

Class Method Details

.create(cxtype, translation_unit) ⇒ Object

Create a type instance of the appropriate subclass based on the type kind.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/ffi/clang/types/type.rb', line 27

def self.create(cxtype, translation_unit)
	case cxtype[:kind]
	when :type_pointer, :type_block_pointer, :type_obj_c_object_pointer, :type_member_pointer
		Pointer.new(cxtype, translation_unit)
	when :type_constant_array, :type_incomplete_array, :type_variable_array, :type_dependent_sized_array
		Array.new(cxtype, translation_unit)
	when :type_vector
		Vector.new(cxtype, translation_unit)
	when :type_function_no_proto, :type_function_proto
		Function.new(cxtype, translation_unit)
	when :type_elaborated
		Elaborated.new(cxtype, translation_unit)
	when :type_typedef
		TypeDef.new(cxtype, translation_unit)
	when :type_record
		Record.new(cxtype, translation_unit)
	else
		Type.new(cxtype, translation_unit)
	end
end

Instance Method Details

#==(other) ⇒ Object

Compare this type with another for equality.



552
553
554
# File 'lib/ffi/clang/types/type.rb', line 552

def ==(other)
	Lib.equal_types(@type, other.type) != 0
end

#address_spaceObject

Get the address space of this type.



222
223
224
# File 'lib/ffi/clang/types/type.rb', line 222

def address_space
	Lib.get_address_space(@type)
end

#alignofObject

Get the alignment of this type in bytes.



106
107
108
# File 'lib/ffi/clang/types/type.rb', line 106

def alignof
	Lib.type_get_align_of(@type)
end

#canonicalObject

Get the canonical type.



76
77
78
# File 'lib/ffi/clang/types/type.rb', line 76

def canonical
	Type.create Lib.get_canonical_type(@type), @translation_unit
end

#const_qualified?Boolean

Check if this type is const-qualified.

Returns:

  • (Boolean)


88
89
90
# File 'lib/ffi/clang/types/type.rb', line 88

def const_qualified?
	Lib.is_const_qualified_type(@type) != 0
end

#copy_assignable?Boolean

Check if this type’s declaration (after reference stripping) has an accessible copy assignment operator and copy-assignable bases. Returns true for non-class types (fundamentals, pointers, enums) and for types whose declaration is unavailable (:cursor_no_decl_found).

Returns:

  • (Boolean)


199
200
201
# File 'lib/ffi/clang/types/type.rb', line 199

def copy_assignable?
	self.non_reference_type.declaration.copy_assignable?
end

#copyable?Boolean

Check if this type’s declaration (after reference stripping) has an accessible copy constructor and copyable bases. Returns true for non-class types (fundamentals, pointers, enums) and for types whose declaration is unavailable (:cursor_no_decl_found).

Returns:

  • (Boolean)


188
189
190
# File 'lib/ffi/clang/types/type.rb', line 188

def copyable?
	self.non_reference_type.declaration.copyable?
end

#declarationObject

Get the cursor for the declaration of this type.



124
125
126
# File 'lib/ffi/clang/types/type.rb', line 124

def declaration
	Cursor.new Lib.get_type_declaration(@type), @translation_unit
end

#fqn_elaborated(policy) ⇒ Object

Spell an elaborated type (typedef / type alias / enum / class) preserving the alias name where appropriate and qualifying template arguments recursively.



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/ffi/clang/types/type.rb', line 347

def fqn_elaborated(policy)
	decl = self.declaration
	const_prefix = self.const_qualified? ? "const " : ""
	
	case decl.kind
	when :cursor_typedef_decl, :cursor_type_alias_decl
		# Preserve the typedef/alias name and qualify with namespace.
		spelling = self.unqualified_type.spelling
		qualified = decl.qualified_name
		
		if spelling.include?("::")
			# Already partially qualified. For nested typedefs in
			# template classes (e.g., std::vector<Pixel>::iterator),
			# qualify template args using the parent type's fqn.
			parent = decl.semantic_parent
			if parent.kind == :cursor_class_decl || parent.kind == :cursor_struct
				parent_type = parent.type
				parent_fqn = parent_type.fqn_impl(policy)
				member_name = decl.spelling
				"#{const_prefix}#{parent_fqn}::#{member_name}"
			else
				"#{const_prefix}#{spelling}"
			end
		elsif qualified
			"#{const_prefix}#{qualified}"
		else
			"#{const_prefix}#{spelling}"
		end
		
	when :cursor_enum_decl
		"#{const_prefix}#{decl.qualified_name}"
		
	else
		# Alias-template detection: e.g. `AliasOptional<int>` -> `Optional<int>`.
		# The elaborated spelling preserves the alias; fqn_record
		# would resolve to the underlying type. Use spelling when
		# it's already qualified.
		unqual = self.unqualified_type.spelling
		if unqual.include?("::") && decl.spelling != unqual.sub(/<.*/, "").split("::").last
			"#{const_prefix}#{unqual}"
		else
			base = fqn_record
			if self.const_qualified? && !base.start_with?("const ")
				"const #{base}"
			else
				base
			end
		end
	end
end

#fqn_impl(policy) ⇒ Object

Shim implementation of fully_qualified_name. Recursively walks the type tree, dispatching by kind. Public so it can be invoked across Type subclass boundaries during recursion.



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/ffi/clang/types/type.rb', line 292

def fqn_impl(policy)
	case self.kind
	when :type_lvalue_ref
		"#{self.non_reference_type.fqn_impl(policy)} &"
	when :type_rvalue_ref
		"#{self.non_reference_type.fqn_impl(policy)} &&"
	when :type_pointer
		fqn_pointer(policy)
	when :type_constant_array
		"#{self.element_type.fqn_impl(policy)}[#{self.size}]"
	when :type_incomplete_array
		"#{self.element_type.fqn_impl(policy)}[]"
	when :type_elaborated
		fqn_elaborated(policy)
	when :type_record
		fqn_record
	else
		self.spelling
	end
end

#fqn_pointer(policy) ⇒ Object

Spell a pointer type and its qualifier chain. Function pointers get a single rendering with parameter list; data pointers walk the chain collecting ‘*`/`*const` parts and qualify the leaf child once. Output matches native fqn: `int **`, `const char *const`, etc.



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/ffi/clang/types/type.rb', line 319

def fqn_pointer(policy)
	pointee = self.pointee
	
	if [:type_function_proto, :type_function_no_proto].include?(pointee.kind)
		ptr_const = self.const_qualified? ? " const" : ""
		result_type = pointee.result_type.fqn_impl(policy)
		arg_types = pointee.arg_types.map{|arg_type| arg_type.fqn_impl(policy)}.join(", ")
		return "#{result_type} (*#{ptr_const})(#{arg_types})"
	end
	
	parts = []
	current = self
	while current.kind == :type_pointer
		inner = current.pointee
		break if [:type_function_proto, :type_function_no_proto].include?(inner.kind)
		
		parts << (current.const_qualified? ? "*const" : "*")
		current = inner
	end
	
	"#{current.fqn_impl(policy)} #{parts.reverse.join}"
end

#fqn_recordObject

Spell a record type (class/struct) using its declaration’s type spelling, which suppresses inline namespaces and includes template args. Falls back to qualified_name + spelling args for dependent types.



403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'lib/ffi/clang/types/type.rb', line 403

def fqn_record
	decl = self.declaration
	return self.spelling if decl.kind == :cursor_no_decl_found
	
	const_prefix = self.const_qualified? ? "const " : ""
	
	# decl.type.spelling gives the right qualification (no inline
	# ns, with template args).
	decl_spelling = decl.type.spelling
	if decl_spelling && !decl_spelling.empty? && decl_spelling.include?("::")
		# For concrete template types, recursively qualify args.
		n = self.num_template_arguments
		if n > 0
			base = decl_spelling.sub(/<.*/, "")
			template_args = fqn_template_args(nil)
			"#{const_prefix}#{base}#{template_args}"
		else
			"#{const_prefix}#{decl_spelling}"
		end
	else
		# Fallback for types where decl.type.spelling is unqualified.
		qualified = decl.qualified_name
		bare_spelling = self.unqualified_type.spelling
		template_args = bare_spelling.include?("<") ? bare_spelling[/<.*/] : ""
		"#{const_prefix}#{qualified}#{template_args}"
	end
end

#fqn_template_args(policy) ⇒ Object

Build the qualified template argument list by recursing into each type argument. Non-type template parameters (e.g. integral values) are recovered from the type’s spelling.



436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/ffi/clang/types/type.rb', line 436

def fqn_template_args(policy)
	n = self.num_template_arguments
	return "" unless n > 0
	
	# Extract original args from spelling for non-type template params.
	spelling_args = parse_template_args_from_spelling
	
	args = (0...n).map do |i|
		arg_type = self.template_argument_type(i)
		if arg_type.kind == :type_invalid
			# Non-type template arg (e.g., int N=3) — use from spelling.
			spelling_args ? spelling_args[i] : nil
		else
			arg_type.fqn_impl(policy)
		end
	end.compact
	
	return "" if args.empty?
	"<#{args.join(", ")}>"
end

#fully_qualified_name(policy = nil, with_global_ns_prefix: false) ⇒ Object

Get the fully qualified name of this type.

On libclang 21+ this dispatches to clang_getFullyQualifiedName. On earlier libclang versions it falls back to a Ruby shim that composes existing libclang APIs (declaration, qualified_name, template arguments, pointer/array/reference unwrapping, etc.).

Known shim limitation: STL container typedefs that depend on default template arguments (e.g., ‘std::vector<T>::iterator`) don’t expand the defaults. Output is valid C++ and matches the native result for non-STL types.



278
279
280
281
282
283
284
285
# File 'lib/ffi/clang/types/type.rb', line 278

def fully_qualified_name(policy = nil, with_global_ns_prefix: false)
	if Lib.respond_to?(:get_fully_qualified_name)
		Lib.extract_string Lib.get_fully_qualified_name(@type, policy, with_global_ns_prefix ? 1 : 0)
	else
		result = fqn_impl(policy)
		with_global_ns_prefix ? "::#{result}" : result
	end
end

#intrinsic_typeObject

Get the intrinsic type — strip the reference, follow pointer indirection until reaching a non-pointer type, then drop cv-qualifiers. Named after Rice’s ‘intrinsic_type` metafunction of the same shape. Useful when asking “what does this type ultimately denote?” for skip-list and bindability checks.

Examples:

  • ‘T &` becomes `T`

  • ‘T *` becomes `T`

  • ‘T **&` becomes `T`

  • ‘const T &` becomes `T`



173
174
175
176
177
178
179
# File 'lib/ffi/clang/types/type.rb', line 173

def intrinsic_type
	type = self.non_reference_type
	while type.kind == :type_pointer
		type = type.pointee
	end
	type.unqualified_type
end

#kindObject

Get the kind of this type.



58
59
60
# File 'lib/ffi/clang/types/type.rb', line 58

def kind
	@type[:kind]
end

#kind_spellingObject

Get the spelling of this type’s kind.



64
65
66
# File 'lib/ffi/clang/types/type.rb', line 64

def kind_spelling
	Lib.extract_string Lib.get_type_kind_spelling @type[:kind]
end

#modified_typeObject

Get the type modified by an attributed type.



246
247
248
# File 'lib/ffi/clang/types/type.rb', line 246

def modified_type
	Type.create Lib.type_get_modified_type(@type), @translation_unit
end

#non_reference_typeObject

Get the non-reference type. For reference types, returns the type that is being referenced. Guards against :type_invalid input: clang_getNonReferenceType dereferences the underlying QualType without a null check and segfaults on invalid types. Returning self preserves the invalid kind without entering libclang.



155
156
157
158
# File 'lib/ffi/clang/types/type.rb', line 155

def non_reference_type
	return self if self.kind == :type_invalid
	Type.create Lib.get_non_reference_type(@type), @translation_unit
end

#nullabilityObject

Get the nullability kind of a pointer type.



240
241
242
# File 'lib/ffi/clang/types/type.rb', line 240

def nullability
	Lib.type_get_nullability(@type)
end

#num_template_argumentsObject

Get the number of template arguments for this type. For template specializations (e.g., ‘std::map<int, std::string>`), this returns the number of template arguments. Returns -1 if this is not a template specialization.



216
217
218
# File 'lib/ffi/clang/types/type.rb', line 216

def num_template_arguments
	Lib.get_num_template_arguments(@type)
end

#parse_template_args_from_spellingObject

Parse template arguments from the type’s unqualified spelling, respecting nested angle brackets. Used to recover non-type template arguments that libclang surfaces only as text.



461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/ffi/clang/types/type.rb', line 461

def parse_template_args_from_spelling
	bare = self.unqualified_type.spelling
	start = bare.index("<")
	return nil unless start
	
	depth = 0
	args = []
	current = +""
	bare[start + 1..].each_char do |c|
		case c
		when "<"
			depth += 1
			current << c
		when ">"
			if depth == 0
				args << current.strip unless current.strip.empty?
				break
			else
				depth -= 1
				current << c
			end
		when ","
			if depth == 0
				args << current.strip
				current = +""
			else
				current << c
			end
		else
			current << c
		end
	end
	args
end

#pod?Boolean

Check if this is a Plain Old Data (POD) type.

Returns:

  • (Boolean)


82
83
84
# File 'lib/ffi/clang/types/type.rb', line 82

def pod?
	Lib.is_pod_type(@type) != 0
end

#pretty_printed(policy) ⇒ Object

Pretty-print this type using a printing policy.



259
260
261
# File 'lib/ffi/clang/types/type.rb', line 259

def pretty_printed(policy)
	Lib.extract_string Lib.get_type_pretty_printed(@type, policy)
end

#ref_qualifierObject

Get the ref-qualifier for this type (C++ only).



118
119
120
# File 'lib/ffi/clang/types/type.rb', line 118

def ref_qualifier
	Lib.type_get_cxx_ref_qualifier(@type)
end

#reference?Boolean

True if this type is an lvalue or rvalue reference.

Returns:

  • (Boolean)


143
144
145
# File 'lib/ffi/clang/types/type.rb', line 143

def reference?
	self.kind == :type_lvalue_ref || self.kind == :type_rvalue_ref
end

#restrict_qualified?Boolean

Check if this type is restrict-qualified.

Returns:

  • (Boolean)


100
101
102
# File 'lib/ffi/clang/types/type.rb', line 100

def restrict_qualified?
	Lib.is_restrict_qualified_type(@type) != 0
end

#sizeofObject

Get the size of this type in bytes.



112
113
114
# File 'lib/ffi/clang/types/type.rb', line 112

def sizeof
	Lib.type_get_size_of(@type)
end

#spellingObject

Get the spelling of this type.



70
71
72
# File 'lib/ffi/clang/types/type.rb', line 70

def spelling
	Lib.extract_string Lib.get_type_spelling(@type)
end

#template_argument_type(index) ⇒ Object

Get the type of a template argument at the given index. For template specializations (e.g., ‘std::vector<int>`), this returns the type of the template argument at the specified position.



208
209
210
# File 'lib/ffi/clang/types/type.rb', line 208

def template_argument_type(index)
	Type.create Lib.get_template_argument_as_type(@type, index), @translation_unit
end

#to_sObject

Get a string representation of this type.



558
559
560
# File 'lib/ffi/clang/types/type.rb', line 558

def to_s
	"#{self.class.name} <#{self.kind}: #{self.spelling}>"
end

#transparent_tag_typedef?Boolean

Check if this typedef is transparent.

Returns:

  • (Boolean)


234
235
236
# File 'lib/ffi/clang/types/type.rb', line 234

def transparent_tag_typedef?
	Lib.type_is_transparent_tag_typedef(@type) != 0
end

#typedef_nameObject

Get the typedef name of this type.



228
229
230
# File 'lib/ffi/clang/types/type.rb', line 228

def typedef_name
	Lib.extract_string Lib.get_typedef_name(@type)
end

#unqualified_typeObject

Get the type with all qualifiers (const, volatile, restrict) removed. Guards against :type_invalid input: clang_getUnqualifiedType has no null check on the underlying QualType (unlike its siblings clang_getNonReferenceType / clang_getCanonicalType / etc.) and segfaults on invalid types. Returning self preserves the invalid kind without entering libclang.



136
137
138
139
# File 'lib/ffi/clang/types/type.rb', line 136

def unqualified_type
	return self if self.kind == :type_invalid
	Type.create Lib.get_unqualified_type(@type), @translation_unit
end

#value_typeObject

Get the value type of an atomic type.



252
253
254
# File 'lib/ffi/clang/types/type.rb', line 252

def value_type
	Type.create Lib.type_get_value_type(@type), @translation_unit
end

#visit_base_classes(&block) ⇒ Object

Visit all base classes of a C++ record type.



501
502
503
504
505
# File 'lib/ffi/clang/types/type.rb', line 501

def visit_base_classes(&block)
	return to_enum(__method__) unless block_given?
	
	visit_type(:visit_cxx_base_classes, &block)
end

#visit_fields(&block) ⇒ Object

Visit all fields of a record type.



523
524
525
526
527
# File 'lib/ffi/clang/types/type.rb', line 523

def visit_fields(&block)
	return to_enum(__method__) unless block_given?
	
	visit_type(:type_visit_fields, &block)
end

#visit_methods(&block) ⇒ Object

Visit all methods of a C++ record type.



512
513
514
515
516
# File 'lib/ffi/clang/types/type.rb', line 512

def visit_methods(&block)
	return to_enum(__method__) unless block_given?
	
	visit_type(:visit_cxx_methods, &block)
end

#volatile_qualified?Boolean

Check if this type is volatile-qualified.

Returns:

  • (Boolean)


94
95
96
# File 'lib/ffi/clang/types/type.rb', line 94

def volatile_qualified?
	Lib.is_volatile_qualified_type(@type) != 0
end