The current project that I am working on is called the Archetype Reflection System. It consists of an automated reflection creation tool and an sdk for run-time reflection for C++. My want to create this system began from reading forum posts where users asked for a system such as this.
I began studying different implementations of reflection systems, including; C#,Helium,Unreal Engine, and white papers over the matter. The main two implementations for a reflection system are ; Macro based, and pre-processor header parsing based, Reflection Patterns . Both of these implementations have their strengths and weaknesses. The hand written macro based process is easy to implement, but can be tedious therefore making clean coding practices difficult to abide by. The pre-processor header based implementation differs in that it allows the user to keep clean code practices while reducing the effort on the end of the user. The main difficulty with the pre-processor method is that because of the native ambiguity of C++, it is hard to parse. More details pertaining to this issue may be found here : Parsing Difficulty in C++ . With these two options in mind, and my want to dust off my parsing skills, I chose the latter.
I chose to focus on a mark-up scheme, in combination with a subset of C++, that would both help reduce the ambiguity of the language, and allow the user to choose which properties/functions that would be reflected. As an added benefit, the mark up strategy allows the user to adjust the quantity of reflected types based on the memory constraints of their application. The mark ups consist of ; FCLASS( ) , FSTRUCT( ) , FENUM( ) , FFUNCTION( ) , and FPROPERTY( )
Shows the Archetype FClass implementation. The FCLASS( ) macro is used to mark c++ classes that will be reflected by the archetype system. The FFUNCTION( ) macro marks functions that will be reflected in the class, and the FPROPERTY macro marks properties, c++ member variables, for reflection. The USER_REFLECTION_INFO, which is a combination of the class name and the 'REFLECTION_INFO' keyword, macro is used to add any class specific functions, such as GetArchetype( ), which returns the reflected version of the class, GetPropertyByName( ), GetFunctionByName( ), and SetPropertyValue( ).
Shows the Archetype FStruct implementation, using the ACCOUNT_REFLECTION_INFO , which is a combination of the struct name and the 'REFLECTION_INFO' keyword, to add struct specific functions, such as; GetArchetype(),GetPropertyByName,etc..
Shows the Archetype FEnum implementation.
Differences between Structs and Classes
In C++ , the sole difference between structs and classes , is that struct member functions and variables begin with public access, while classes start as private. To add a more meaningful difference between the two, influenced by the google coding standards, I chose to only reflect functions that are found in FClasses, leaving FStructs to be used as aggregate containers for data types. An added restriction is that all FClasses must inherit from FObject, while FStructs do not have an inheritance restriction.
Accessing generated reflection data
For the sake of cleanliness, The Archetype Reflection Standalone generates the reflected class, struct, and enums in a separate .h and .cpp file which may be accessed through : filename.reflection.h , where filename is the name of the file being reflected.
To allow for efficient and simplistic setting and getting of values, The system supplies 8 functions in the REFLECTION_INFO macros generated.
SetPropertyValue : template function, sets the value of the property as the template argument given. It returns true if the property exists in the class/struct and if the value was correctly set, else returns false.
GetPropertyValue : template function that returns the value of the property in the class/struct, only if it exists.
GetPropertyByName : Returns the ArchetypeProperty that represents the c++ property.
GetPropertyValuePtr : Returns the pointer to the c++ property.
GetStringProperty : a template function that returns the char/wchar_t array that the given property name represents. This function differs from GetPropertyValue<char/wchar_t> in that it does not allocate memory to copy the contents of the property char/wchar_t array.
In light of the IEEE standards for floating point precision, and casting between floats and integers, I have supplied type safe functions for setting and getting numerical values. GetNumericPropertyAs## returns the value of the class/struct property , if it exists. SetNumericPropertyAs## sets the value of the class/struct property, if it exists. The template argument, in the cases above, of "float", is the data type of the variable in the class or struct, i.e float m_variable, this coding scheme helps insure type safety.
Archetype functions may be called one of two ways; through the generated "CallFunctionByName" method, or by retrieving the archetype function through the "GetFunctionByName" method, and doing a manual call. Archetype functions support ; optional , pass by reference, pass by value, const, pointers, and basic parameters.
Calling functions manually:
ArchetypeFunctions rely on the alloca macro to allocate data dynamically, for parameters, on the stack. Calling functions manually consists of 4 steps.
1) Retrieve the Archetype Function through the GetFunctionByName method.
2) Create an FCall structure by passing the Class that the function will be called on and the bytes, allocated through the alloca macro. The amount of bytes required by the function parameters may be obtained through the GetParameterSize() function.
3) Set the parameters of the FCall structure through the overloaded () operators
4) Call Invoke on the ArchetypeFunction, passing the FCall structure and the optional return value reference.
Retrieving parameters that were passed by reference:
To retrieve parameters that were passed by reference, the user will use the GetParamAs function from the created FCall structure. The user passes a template FunctionArguments# structure where # is in between 1 and MAXFUNCTIONARGUMENTS. The user can then retrieve the argument through the FunctionArgument structure's member variables.
The archetype sdk supplies two types of iterators, ArchetypeIterator, and TArchetypeIterator. An ArchetypeIterator contiguously traverse through properties and function of an FClass/FStruct, while TArchetypeIterators only traverse through the given template, i.e TArchetypeIterator<ArchetypeProperty> traverses only through properties, while TArchetypeIterator<ArchetypeFunction> traverses functions.
To mirror c++ style casting in a user friendly way, the ArchetypeSDK supplies casting support in the form of NumericalCast and StringCast.
As requested, the system now supports if/def statements.
Importing/Exporting Reflection Data
As of now, the Archetype Reflection Standalone currently supports serialization of reflection data to windows ini and json formats. I will soon integrate XML.
As said previously, the Archetype Reflection System only understands a subset of the C++ language. The library supports a fair majority of the new c++11 library, including the integrated smart pointers, stl, and enum classes. It does not currently support functions that share the same name, but have different signatures. Templated functions, structs, and classes are not supported, and macros requiring expansion are yet to be included. Archetype Classes/Structs currently only support single inheritance
> Add Multi-Inheritance support
> Add more c++11 features
> Create a Visual Studios plugin implementation for better pipeline of project building.
> Implement XML serialization
> Create multi threaded implementation