The operator Is (Run-Time Type Information) uses it to check if an object is compatible to a type derived from its compile-time type, because RTTI provides not only the runtime typename of the object but also all typenames of its base types, up to the Object built-in type.
The stored typenames by RTTI are mangled names. Some C++ implementations produce a human-readable typename. Others, most notably gcc, return the mangled name (convertible to human-readable form using implementation-specific API). But FreeBASIC returns nothing.
The aim of this article is to provide a description of the RTTI structure, and to propose user procedures to extract the typenames (mangled and also demangled).
1) How are chained the entities: object instance, vptr, vtbl (vtable), and RTTI info
- Instance -> Vptr -> Vtbl(Vtable) -> RTTI info chaining:
- For any type derived (directly or indirectly) from the Object built-in type, a hidden pointer 'vptr' is added at beginning (located at offset 0) of its data fields (own or inherited). This vptr points to the virtual table (vtbl or vtable) of the considered type.
- The vtbl contains the list of the addresses of all abstract/virtual procedures (from the offset 0). The vtbl also contains (located at offset -1) a pointer to the RunTime Type Information (RTTI) info block of the considered type.
- The RTTI info block contains (located at offset +1) a pointer to the mangled-typename of the considered type (ascii characters). The RTTI info block also contains (located at offset +2) a pointer to the RTTI info block of its Base. All RTTI info blocks for up-hierarchy are so chained.
- Instance -> Vptr -> Vtbl(Vtable) -> RTTI info diagram:
[/b]Code: Select all
vtbl (vtable) .-------------------. [-2]| reserved (0) | RTTI info Mangled Typename |-------------------| .-----------------------. .---------------. Instance of UDT [-1]| Ptr to RTTI info |--->[0]| reserved (0) | |Typename string| .-------------------. |-------------------| |-----------------------| | with | [0]| vptr: Ptr to vtbl |--->[0]|Ptr to virt proc #1| [+1]|Ptr to Mangled Typename|--->[0]| length (ASCII)| |-------------------| |-------------------| |-----------------------| | & | |UDT member field #a| [+1]|Ptr to virt proc #2| [+2]| Ptr to Base RTTI info |---. | name (ASCII) | |-------------------| |-------------------| |_______________________| | | for | |UDT member field #b| [+2]|Ptr to virt proc #3| ________________________________| |each component | |-------------------| :- - - - - - - - - -: | |_______________| |UDT member field #c| : : | Base RTTI info :- - - - - - - - - -: : : | .----------------------------. : : |___________________| '--->[0]| reserved (0) | : : |----------------------------| |___________________| [+1]|Ptr to Mangled Base Typename|---> |----------------------------| [+2]| Ptr to Base.Base RTTI info |---. |____________________________| | | V
- Extraction of the mangled typename from the RTTI info:
- From the instance address, the RTTI info pointer of the type of the instance is accessed through a double indirection (with offsets: [0][-1]).
- The RTTI info pointer chaining described above allows to access RTTI info of the selected type in the inheritance hierarchy (up to the Object built-in type). This is done by means of an iteration on the pointer indirection (with offset: [+2]).
- Then the selected mangled typename is accessed (final indirection with offset: [+1])
- Function 'mangledTypeNameFromRTTI()' to extract the mangled typenames:
Code: Select all
Function mangledTypeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String ' Function to get any mangled-typename in the inheritance up hierarchy ' of the type of an instance (address: 'po') compatible with the built-in 'Object' ' ' ('baseIndex = 0' to get the mangled-typename of the instance) ' ('baseIndex = -1' to get the base mangled-typename of the instance, or "" if not existing) ' ('baseIndex = -2' to get the base.base mangled-typename of the instance, or "" if not existing) ' (.....) ' Dim As String s Dim As Zstring Ptr pz Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1] ' Ptr to RTTI info For I As Integer = baseIndex To -1 p = Cptr(Any Ptr Ptr, p)[2] ' Ptr to Base RTTI info of previous RTTI info If p = 0 Then Return s Next I pz = Cptr(Any Ptr Ptr, p)[1] ' Ptr to mangled-typename s = *pz Return s End Function
- Example of mangled typenames extraction from RTTI info, for an inheritance structure (three derived level) declared inside a namespace block:
Code: Select all
Namespace oop Type parent Extends Object End Type Type child Extends parent End Type Type grandchild Extends child End Type End namespace Function mangledTypeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String ' Function to get any mangled-typename in the inheritance up hierarchy ' of the type of an instance (address: 'po') compatible with the built-in 'Object' ' ' ('baseIndex = 0' to get the mangled-typename of the instance) ' ('baseIndex = -1' to get the base mangled-typename of the instance, or "" if not existing) ' ('baseIndex = -2' to get the base.base mangled-typename of the instance, or "" if not existing) ' (.....) ' Dim As String s Dim As Zstring Ptr pz Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1] ' Ptr to RTTI info For I As Integer = baseIndex To -1 p = Cptr(Any Ptr Ptr, p)[2] ' Ptr to Base RTTI info of previous RTTI info If p = 0 Then Return s Next I pz = Cptr(Any Ptr Ptr, p)[1] ' Ptr to mangled-typename s = *pz Return s End Function Dim As Object Ptr p = New oop.grandchild Print "Mangled typenames list, from RTTI info:" Print " " & mangledTypeNameFromRTTI(p, 0) Print " " & mangledTypeNameFromRTTI(p, -1) Print " " & mangledTypeNameFromRTTI(p, -2) Print " " & mangledTypeNameFromRTTI(p, -3) Delete p Sleep
[/b]Code: Select all
Mangled typenames list, from RTTI info: N3OOP10GRANDCHILDE N3OOP5CHILDE N3OOP6PARENTE 6OBJECT
- From the above outputs, the mangling process on typenames can be highlighted with the following formatting:
- N3OOP10GRANDCHILDE
(for 'oop.grandchild')
N3OOP5CHILDE
(for 'oop.child')
N3OOP6PARENTE
(for 'oop.parent')
6OBJECT
(for 'Object') - Details on the the mangling process on typenames in the RTTI info:
- The mangled typename is a Zstring (ended by the null character).
- Each component (one dot as separator) of the full typename (converted to uppercase) is preceded by its number of characters encoded in ASCII itself (based on length-prefixed strings).
- When the type is inside at least one namespace, the mangled typename string begins with an additional "N" and ends with an additional "E".- (prefix "N" and suffix "E" from nested-name ... ending)
- N3OOP10GRANDCHILDE
- The previous function ('mangledTypeNameFromRTTI()') can be now completed with a demangling process.
- Function 'typeNameFromRTTI()' to extract the demangled typenames:
Code: Select all
Function typeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String ' Function to get any typename in the inheritance up hierarchy ' of the type of an instance (address: 'po') compatible with the built-in 'Object' ' ' ('baseIndex = 0' to get the typename of the instance) ' ('baseIndex = -1' to get the base.typename of the instance, or "" if not existing) ' ('baseIndex = -2' to get the base.base.typename of the instance, or "" if not existing) ' (.....) ' Dim As String s Dim As Zstring Ptr pz Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1] ' Ptr to RTTI info For I As Integer = baseIndex To -1 p = Cptr(Any Ptr Ptr, p)[2] ' Ptr to Base RTTI info of previous RTTI info If p = 0 Then Return s Next I pz = Cptr(Any Ptr Ptr, p)[1] ' Ptr to mangled-typename Do Do While (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If (*pz)[0] = 0 Then Return s pz += 1 Loop Dim As Integer N = Val(*pz) Do pz += 1 Loop Until (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If s <> "" Then s &= "." s &= Left(*pz, N) pz += N Loop End Function
- Previous example completed with the above function:
Code: Select all
Namespace oop Type parent Extends Object End Type Type child Extends parent End Type Type grandchild Extends child End Type End namespace Function mangledTypeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String ' Function to get any mangled-typename in the inheritance up hierarchy ' of the type of an instance (address: 'po') compatible with the built-in 'Object' ' ' ('baseIndex = 0' to get the mangled-typename of the instance) ' ('baseIndex = -1' to get the base mangled-typename of the instance, or "" if not existing) ' ('baseIndex = -2' to get the base.base mangled-typename of the instance, or "" if not existing) ' (.....) ' Dim As String s Dim As Zstring Ptr pz Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1] ' Ptr to RTTI info For I As Integer = baseIndex To -1 p = Cptr(Any Ptr Ptr, p)[2] ' Ptr to Base RTTI info of previous RTTI info If p = 0 Then Return s Next I pz = Cptr(Any Ptr Ptr, p)[1] ' Ptr to mangled-typename s = *pz Return s End Function Function typeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String ' Function to get any typename in the inheritance up hierarchy ' of the type of an instance (address: 'po') compatible with the built-in 'Object' ' ' ('baseIndex = 0' to get the typename of the instance) ' ('baseIndex = -1' to get the base.typename of the instance, or "" if not existing) ' ('baseIndex = -2' to get the base.base.typename of the instance, or "" if not existing) ' (.....) ' Dim As String s Dim As Zstring Ptr pz Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1] ' Ptr to RTTI info For I As Integer = baseIndex To -1 p = Cptr(Any Ptr Ptr, p)[2] ' Ptr to Base RTTI info of previous RTTI info If p = 0 Then Return s Next I pz = Cptr(Any Ptr Ptr, p)[1] ' Ptr to mangled-typename Do Do While (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If (*pz)[0] = 0 Then Return s pz += 1 Loop Dim As Integer N = Val(*pz) Do pz += 1 Loop Until (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If s <> "" Then s &= "." s &= Left(*pz, N) pz += N Loop End Function Dim As Object Ptr p = New oop.grandchild Print "Mangled typenames list, from RTTI info:" Print " " & mangledTypeNameFromRTTI(p, 0) Print " " & mangledTypeNameFromRTTI(p, -1) Print " " & mangledTypeNameFromRTTI(p, -2) Print " " & mangledTypeNameFromRTTI(p, -3) Print Print "Typenames (demangled) list, from RTTI info:" Print " " & typeNameFromRTTI(p, 0) Print " " & typeNameFromRTTI(p, -1) Print " " & typeNameFromRTTI(p, -2) Print " " & typeNameFromRTTI(p, -3) Delete p Sleep
[/b]Code: Select all
Mangled typenames list, from RTTI info: N3OOP10GRANDCHILDE N3OOP5CHILDE N3OOP6PARENTE 6OBJECT Typenames (demangled) list, from RTTI info: OOP.GRANDCHILD OOP.CHILD OOP.PARENT OBJECT
- Simply by calling the previous function in a loop with a decreasing parameter 'baseIndex' (from the value 0) and to stop it as soon as an empty string is returned. Finaly by returning a string containing the different typenames with a hierarchic separator between each.
- Function 'typeNameHierarchyFromRTTI()' to extract the Typename (demangled) and all those of its base-types hierarchy:
Code: Select all
Function typeNameHierarchyFromRTTI (Byval po As Object Ptr) As String ' Function to get the typename inheritance up hierarchy ' of the type of an instance (address: po) compatible with the built-in 'Object' ' Dim As String s = TypeNameFromRTTI(po) Dim As Integer i = -1 Do Dim As String s0 = typeNameFromRTTI(po, i) If s0 = "" Then Exit Do s &= "->" & s0 i -= 1 Loop Return s End Function
- Previous example again completed with the above function:
Code: Select all
Namespace oop Type parent Extends Object End Type Type child Extends parent End Type Type grandchild Extends child End Type End namespace Function mangledTypeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String ' Function to get any mangled-typename in the inheritance up hierarchy ' of the type of an instance (address: 'po') compatible with the built-in 'Object' ' ' ('baseIndex = 0' to get the mangled-typename of the instance) ' ('baseIndex = -1' to get the base mangled-typename of the instance, or "" if not existing) ' ('baseIndex = -2' to get the base.base mangled-typename of the instance, or "" if not existing) ' (.....) ' Dim As String s Dim As Zstring Ptr pz Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1] ' Ptr to RTTI info For I As Integer = baseIndex To -1 p = Cptr(Any Ptr Ptr, p)[2] ' Ptr to Base RTTI info of previous RTTI info If p = 0 Then Return s Next I pz = Cptr(Any Ptr Ptr, p)[1] ' Ptr to mangled-typename s = *pz Return s End Function Function typeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String ' Function to get any typename in the inheritance up hierarchy ' of the type of an instance (address: 'po') compatible with the built-in 'Object' ' ' ('baseIndex = 0' to get the typename of the instance) ' ('baseIndex = -1' to get the base.typename of the instance, or "" if not existing) ' ('baseIndex = -2' to get the base.base.typename of the instance, or "" if not existing) ' (.....) ' Dim As String s Dim As Zstring Ptr pz Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1] ' Ptr to RTTI info For I As Integer = baseIndex To -1 p = Cptr(Any Ptr Ptr, p)[2] ' Ptr to Base RTTI info of previous RTTI info If p = 0 Then Return s Next I pz = Cptr(Any Ptr Ptr, p)[1] ' Ptr to mangled-typename Do Do While (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If (*pz)[0] = 0 Then Return s pz += 1 Loop Dim As Integer N = Val(*pz) Do pz += 1 Loop Until (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If s <> "" Then s &= "." s &= Left(*pz, N) pz += N Loop End Function Function typeNameHierarchyFromRTTI (Byval po As Object Ptr) As String ' Function to get the typename inheritance up hierarchy ' of the type of an instance (address: po) compatible with the built-in 'Object' ' Dim As String s = TypeNameFromRTTI(po) Dim As Integer i = -1 Do Dim As String s0 = typeNameFromRTTI(po, i) If s0 = "" Then Exit Do s &= "->" & s0 i -= 1 Loop Return s End Function Dim As Object Ptr p = New oop.grandchild Print "Mangled typenames list, from RTTI info:" Print " " & mangledTypeNameFromRTTI(p, 0) Print " " & mangledTypeNameFromRTTI(p, -1) Print " " & mangledTypeNameFromRTTI(p, -2) Print " " & mangledTypeNameFromRTTI(p, -3) Print Print "Typenames (demangled) list, from RTTI info:" Print " " & typeNameFromRTTI(p, 0) Print " " & typeNameFromRTTI(p, -1) Print " " & typeNameFromRTTI(p, -2) Print " " & typeNameFromRTTI(p, -3) Print Print "Typename (demangled) and all those of its base-types hierarchy, from RTTI info:" Print " " & typeNameHierarchyFromRTTI(p) Delete p Sleep
[/b]Code: Select all
Mangled typenames list, from RTTI info: N3OOP10GRANDCHILDE N3OOP5CHILDE N3OOP6PARENTE 6OBJECT Typenames (demangled) list, from RTTI info: OOP.GRANDCHILD OOP.CHILD OOP.PARENT OBJECT Typename (demangled) and all those of its base-types hierarchy, from RTTI info: OOP.GRANDCHILD->OOP.CHILD->OOP.PARENT->OBJECT
- As the various steps of demangling, the successive elements of the typname extracted from the RTTI info are compared with those of the chain provided (as soon as an element is different, "false" is returned immediately).
- Function 'typeNameEqualFromRTTI()' to compared the typename (demangled) extracted from RTTI info to a string variable:
Code: Select all
Function typeNameEqualFromRTTI (Byval po As Object Ptr, Byref typeName As String) As Boolean ' Function to get true if the instance typename (address: po) is the same than the passed string ' Dim As String t = Ucase(typeName) Dim As ZString Ptr pz = Cptr(Any Ptr Ptr Ptr Ptr, po)[0][-1][1] ' Ptr to Mangled Typename Dim As Integer i = 1 Do Do While (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If (*pz)[0] = 0 Then Return True pz += 1 Loop Dim As Integer N = Val(*pz) Do pz += 1 Loop Until (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If i > 1 Then If Mid(t, i, 1) <> "." Then Return False Else i += 1 End If If Mid(t, i, N) <> Left(*pz, N) Then Return False Else pz += N : i += N Loop End Function
- Previous example finally completed with the above function:
Code: Select all
Namespace oop Type parent Extends Object End Type Type child Extends parent End Type Type grandchild Extends child End Type End namespace Function mangledTypeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String ' Function to get any mangled-typename in the inheritance up hierarchy ' of the type of an instance (address: 'po') compatible with the built-in 'Object' ' ' ('baseIndex = 0' to get the mangled-typename of the instance) ' ('baseIndex = -1' to get the base mangled-typename of the instance, or "" if not existing) ' ('baseIndex = -2' to get the base.base mangled-typename of the instance, or "" if not existing) ' (.....) ' Dim As String s Dim As Zstring Ptr pz Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1] ' Ptr to RTTI info For I As Integer = baseIndex To -1 p = Cptr(Any Ptr Ptr, p)[2] ' Ptr to Base RTTI info of previous RTTI info If p = 0 Then Return s Next I pz = Cptr(Any Ptr Ptr, p)[1] ' Ptr to mangled-typename s = *pz Return s End Function Function typeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String ' Function to get any typename in the inheritance up hierarchy ' of the type of an instance (address: 'po') compatible with the built-in 'Object' ' ' ('baseIndex = 0' to get the typename of the instance) ' ('baseIndex = -1' to get the base.typename of the instance, or "" if not existing) ' ('baseIndex = -2' to get the base.base.typename of the instance, or "" if not existing) ' (.....) ' Dim As String s Dim As Zstring Ptr pz Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1] ' Ptr to RTTI info For I As Integer = baseIndex To -1 p = Cptr(Any Ptr Ptr, p)[2] ' Ptr to Base RTTI info of previous RTTI info If p = 0 Then Return s Next I pz = Cptr(Any Ptr Ptr, p)[1] ' Ptr to mangled-typename Do Do While (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If (*pz)[0] = 0 Then Return s pz += 1 Loop Dim As Integer N = Val(*pz) Do pz += 1 Loop Until (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If s <> "" Then s &= "." s &= Left(*pz, N) pz += N Loop End Function Function typeNameHierarchyFromRTTI (Byval po As Object Ptr) As String ' Function to get the typename inheritance up hierarchy ' of the type of an instance (address: po) compatible with the built-in 'Object' ' Dim As String s = TypeNameFromRTTI(po) Dim As Integer i = -1 Do Dim As String s0 = typeNameFromRTTI(po, i) If s0 = "" Then Exit Do s &= "->" & s0 i -= 1 Loop Return s End Function Function typeNameEqualFromRTTI (Byval po As Object Ptr, Byref typeName As String) As Boolean ' Function to get true if the instance typename (address: po) is the same than the passed string ' Dim As String t = Ucase(typeName) Dim As ZString Ptr pz = Cptr(Any Ptr Ptr Ptr Ptr, po)[0][-1][1] ' Ptr to Mangled Typename Dim As Integer i = 1 Do Do While (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If (*pz)[0] = 0 Then Return True pz += 1 Loop Dim As Integer N = Val(*pz) Do pz += 1 Loop Until (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0") If i > 1 Then If Mid(t, i, 1) <> "." Then Return False Else i += 1 End If If Mid(t, i, N) <> Left(*pz, N) Then Return False Else pz += N : i += N Loop End Function Dim As Object Ptr p = New oop.grandchild Print "Mangled typenames list, from RTTI info:" Print " " & mangledTypeNameFromRTTI(p, 0) Print " " & mangledTypeNameFromRTTI(p, -1) Print " " & mangledTypeNameFromRTTI(p, -2) Print " " & mangledTypeNameFromRTTI(p, -3) Print Print "Typenames (demangled) list, from RTTI info:" Print " " & typeNameFromRTTI(p, 0) Print " " & typeNameFromRTTI(p, -1) Print " " & typeNameFromRTTI(p, -2) Print " " & typeNameFromRTTI(p, -3) Print Print "Typename (demangled) and all those of its base-types hierarchy, from RTTI info:" Print " " & typeNameHierarchyFromRTTI(p) Delete p Print p = New oop.child Print "Is the typename of an oop.child instance the same as ""child""?" Print " " & typeNameEqualFromRTTI(p, "child") Print "Is the typename of an oop.child instance the same as ""oop.child""?" Print " " & typeNameEqualFromRTTI(p, "oop.child") Print "Is the typename of an oop.child instance the same as ""oop.grandchild""?" Print " " & typeNameEqualFromRTTI(p, "oop.grandchild") Print "Is the typename of an oop.child instance the same as ""oop.parent""?" Print " " & typeNameEqualFromRTTI(p, "oop.parent") Delete p Sleep
[/b]Code: Select all
Mangled typenames list, from RTTI info: N3OOP10GRANDCHILDE N3OOP5CHILDE N3OOP6PARENTE 6OBJECT Typenames (demangled) list, from RTTI info: OOP.GRANDCHILD OOP.CHILD OOP.PARENT OBJECT Typename (demangled) and all those of its base-types hierarchy, from RTTI info: OOP.GRANDCHILD->OOP.CHILD->OOP.PARENT->OBJECT Is the typename of an oop.child instance the same as "child"? false Is the typename of an oop.child instance the same as "oop.child"? true Is the typename of an oop.child instance the same as "oop.grandchild"? false Is the typename of an oop.child instance the same as "oop.parent"? false