LibeRTTI is a header only and dependency-free library which provides type data for your (and selected built-in) c++ types. The minimal requirement is C++14.
Feature | Description |
---|---|
Hierarchy based | Every registered type gets its unique nested type class, e.g if you have a class A then it's type class will be A::Type . These type classes create a hierarchy based on their original classes hierarchy. So for instance, if class A : public B , then class A::Type : class B::Type . Also, all type classes are virtual, even if their original classes are not. It allows you to take advantage of polymorphic features like having a function parameter of type const A::Type& and passing the children types of A as arguments. rtti:Type is the root class for all types classes. |
Namespaces and nested types friendly | Types within namespaces and other types are fully supported. |
"Super" keyword | A well known feature from other languages. The Super keyword refers to the parent class of your class. |
Instantiating/Destroying/Copying without knowing true type | Type classes are able to instantiate, destroy or copy raw memory which contains the types they represent. |
Move without knowing true type (Optional) | Same as above but with the move operation. It forces all registered types to be movable. It might be disabled by defining RTTI_REQUIRE_MOVE_CTOR 0 before including the LibeRTTI header. |
Recognizing object's true type | You can get the true type of your polymorphic class instance. |
Types register | LibeRTTI gives you access to all registered types. |
Properties and Methods | Types might keep data about the their member variables and methods to make them accessible in runtime. |
Metadata | Types and properties can store additional string-based metadata. |
Unique and persistent IDs | All registered types and their properties get unique IDs which persist between executions unless the name of the type/property changes. |
Primitive Types | All primitive types are registered out of the box. |
Enums | Custom enum classes can also be registered. |
Pointer Types | Pointer types are registered lazily at runtime when the need for them arrises. You're not limited by the amount of indirections (properties like float***** are allowed). Pointer types follow their original classes hierarchy. I.e PointerType<B> inherits from PointerType<A> if B also inherits from A . |
Runtime Types | You can compose completely new type with selected properties in runtime. Such types can still inherit from other types and preserve hierarchy of classes. |
std:: shared_ptr, unique_ptr, vector, unordered_set, unordered_map, pair, string Types (Optional) |
All these types are registered out of the box and might be disabled using config defines: RTTI_CFG_CREATE_STD_SHAREDPTR_TYPE 0 , RTTI_CFG_CREATE_STD_UNIQUEPTR_TYPE 0 , RTTI_CFG_CREATE_STD_VECTOR_TYPE 0 , RTTI_CFG_CREATE_STD_SET_TYPE 0 ,RTTI_CFG_CREATE_STD_MAP_TYPE 0 ,RTTI_CFG_CREATE_STD_PAIR_TYPE 0 , RTTI_CFG_CREATE_STD_STRING_TYPE 0 โ |
More examples can be found in Tests or Forge project source code.
// .h
struct BaseStruct
{
RTTI_DECLARE_STRUCT( BaseStruct );
};
struct MyStruct : public BaseStruct
{
RTTI_DECLARE_STRUCT( MyStruct, BaseStruct );
float myFloat;
MyStruct* myPtr;
BaseStruct myCustomInstance;
MyStruct* Foo( Float input )
{
myFloat = input;
return this;
}
}
// .cpp
RTTI_IMPLEMENT_TYPE( BaseStruct );
RTTI_IMPLEMENT_TYPE( MyStruct,
RTTI_REGISTER_PROPERTY( myFloat,
RTTI_ADD_METADATA( MyCustomMetadata, 321 );
);
RTTI_REGISTER_PROPERTY( myPtr );
RTTI_REGISTER_PROPERTY( myCustomInstance );
RTTI_REGISTER_METHOD( Foo );
RTTI_ADD_METADATA( MyCustomMetadata );
RTTI_ADD_METADATA( MyCustomMetadataWithValue, 123 );
);
static_assert( MyStruct::GetTypeStatic().InheritsFrom< BaseStruct::Type > );
void Func( void* rawInstance, const rtti::Type& type )
{
if( type.IsA< MyType >() )
{
const MyStruct::Type& myType = static_cast< const MyStruct::Type& >( type );
// Accessing properties
const rtti::Property* floatProperty = myType.FindProperty( "myFloat" );
if ( floatProperty->GetType().IsA< float >() )
{
floatProperty->GetValue< float >( rawInstance ) = 3.14f;
}
myType.FindProperty( "myCustomInstance" )->GetValue< BaseStruct >( rawInstance ) = BaseStruct();
// Calling methods
MyType* fooResult = nullptr;
float arg = 3.14f;
myType.FindMethod( "Foo" )->Call( /*instance*/ rawInstance, /*args*/ &arg, /*outcome*/ &fooResult );
// Accessing metadata
if ( myType.HasMetadata( "MyCustomMetadata ) )
{
// type contains provided metadata
}
if (const std::string* metadataValue = myType.GetMetadataValue( "MyCustomMetadataWithValue" )
{
*metadataValue == "123 // true
}
if( myType.FindProperty( "myFloat" )->HasMetadata( "MyCustomMetadata ") )
{
// property contains provided metadata
}
}
}
Registering your type requires 2 steps.
Put one of the following macros into your type's body.
Type | Macro |
---|---|
Regular, non virtual class | RTTI_DECLARE_CLASS( <class_name>, <parent_name_with_namespace> (optional) ) |
Virtual class | RTTI_DECLARE_POLYMORPHIC_CLASS( <class_name>, <parent_name_with_namespace> (optional) ) |
Abstract class | RTTI_DECLARE_ABSTRACT_CLASS( <class_name>, <parent_name_with_namespace> (optional) ) |
Struct | RTTI_DECLARE_STRUCT( <struct_name>, <parent_name_with_namespace> (optional) ) |
Put the following macro in .cpp file.
RTTI_IMPLEMENT_TYPE( <class_name_with_namespace>, <properties/methods/metadata_registration_macros... (optional)> );
To register property of your type use:
RTTI_REGISTER_PROPERTY( <property_name>, <metadata_macros... (optional)...> );
For methods use:
RTTI_REGISTER_METHOD( <method_name> );
To add type or property metadata:
RTTI_ADD_METADATA( <key>, <value (optional)> );
and put it in RTTI_IMPLEMENT_TYPE
(for types) or RTTI_REGISTER_PROPERTY
(for properties) macro.
To register your custom enum put the following macro in .cpp file.
RTTI_DECLARE_AND_IMPLEMENT_ENUM( <enum_name_with_namespace>, <enum_members...> )
To register enum's members use:
RTTI_REGISTER_ENUM_MEMBER( <member_name> );
and put it in RTTI_DECLARE_AND_IMPLEMENT_ENUM
macro.