Created
June 9, 2014 20:37
-
-
Save RichardMarks/5d5c6a98ded6e81b0496 to your computer and use it in GitHub Desktop.
C++ Reflection System by Mattias Gustavsson - Used with Permission.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Reflection.h" | |
#include <stdio.h> | |
struct vec3 | |
{ | |
float x; | |
float y; | |
float z; | |
}; | |
class Entity | |
{ | |
public: | |
Entity() | |
{ | |
Reflection::Expose( this, &health_, "health_" ); | |
Reflection::Expose( this, &position_, "position_" ); | |
} | |
~Entity() | |
{ | |
Reflection::Revoke( this ); | |
} | |
private: | |
int health_; | |
vec3 position_; | |
}; | |
void main() | |
{ | |
// Call this once per class you want to be able to instantiate by name | |
Reflection::Expose<Entity>( "Entity" ); | |
// Create a new instance of Entity using its exposed name | |
void* entity = Reflection::New( "Entity" ); | |
// Set an exposed value | |
Reflection::Set( entity, "health_", 42 ); | |
// Get exposed values | |
int health; | |
Reflection::Get( entity, "health_", &health ); | |
vec3 pos; | |
Reflection::Get( entity, "position_", &pos); | |
// Destroy the instance - note that this will properly call the destructor | |
Reflection::Delete( entity ); | |
// The system also works with instances created on the stack | |
Entity test; | |
Reflection::Set( &test, "health_", 4711 ); | |
Reflection::Get( &test, "health_", &health ); | |
// And we can enumerate exposed variables | |
unsigned count = Reflection::GetVariableCount( &test ); | |
for( unsigned i = 0; i < count; ++i ) | |
{ | |
const char* name = Reflection::GetVariableName( &test, i ); | |
printf( "Name: %s\n", name ); | |
} | |
return; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//*** Reflection.cpp *** | |
#include "Reflection.h" | |
#define _CRT_SECURE_NO_WARNINGS | |
#include <assert.h> | |
#include <string.h> | |
const unsigned MAX_IDENTIFIER_LENGTH = 32; | |
const unsigned MAX_CLASS_COUNT = 256; | |
const unsigned MAX_INSTANCE_COUNT = 4096; | |
const unsigned MAX_VARIABLE_COUNT = 16384; | |
typedef char Identifier[ MAX_IDENTIFIER_LENGTH ]; | |
struct ClassInfo | |
{ | |
const void* type; | |
Identifier name; | |
Reflection::internal::NewFunction newFunction; | |
Reflection::internal::DeleteFunction deleteFunction; | |
}; | |
ClassInfo classes[ MAX_CLASS_COUNT ]; | |
unsigned classCount = 0; | |
struct InstanceInfo | |
{ | |
const void* instance; | |
ClassInfo* classInfo; | |
}; | |
InstanceInfo instances[ MAX_INSTANCE_COUNT ]; | |
unsigned instanceCount = 0; | |
struct VariableInfo | |
{ | |
const void* instance; | |
ClassInfo* classInfo; | |
Identifier name; | |
const void* type; | |
void* address; | |
}; | |
VariableInfo variables[ MAX_VARIABLE_COUNT ]; | |
unsigned variableCount = 0; | |
static void CopyIdentifier( Identifier identifier, const char* name ) | |
{ | |
assert( name && strlen( name ) < MAX_IDENTIFIER_LENGTH && "Identifier name too long." ); | |
strcpy( identifier, name ); | |
} | |
static ClassInfo* FindClassInfoByName( const char* name ) | |
{ | |
for( unsigned i = 0; i < classCount; ++i ) | |
{ | |
if( strcmp( name, classes[ i ].name ) == 0 ) | |
{ | |
return &classes[ i ]; | |
} | |
} | |
return 0; | |
} | |
static ClassInfo* FindClassInfoByType( const void* type ) | |
{ | |
for( unsigned i = 0; i < classCount; ++i ) | |
{ | |
if( type == classes[ i ].type ) | |
{ | |
return &classes[ i ]; | |
} | |
} | |
return 0; | |
} | |
static void AddInstance( ClassInfo* classInfo, void* instance ) | |
{ | |
assert( instanceCount < MAX_INSTANCE_COUNT && "Maximum number of instances exceeded." ); | |
InstanceInfo* info = &instances[ instanceCount ]; | |
++instanceCount; | |
info->instance = instance; | |
info->classInfo = classInfo; | |
} | |
static void RemoveInstance( InstanceInfo* info ) | |
{ | |
for( unsigned i = 0; i < instanceCount; ++i ) | |
{ | |
if( info->instance == instances[ i ].instance ) | |
{ | |
// Move last element into the instance slot we are removing | |
instances[ i ] = instances[ instanceCount - 1 ]; | |
--instanceCount; | |
return; | |
} | |
} | |
} | |
static InstanceInfo* FindInstanceInfo( const void* instance ) | |
{ | |
for( unsigned i = 0; i < instanceCount; ++i ) | |
{ | |
if( instance == instances[ i ].instance ) | |
{ | |
return &instances[ i ]; | |
} | |
} | |
return 0; | |
} | |
static VariableInfo* FindVariableByName( const void* instance, const char* name ) | |
{ | |
for( unsigned i = 0; i < variableCount; ++i ) | |
{ | |
if( instance == variables[ i ].instance && strcmp( variables[ i ].name, name ) == 0 ) | |
{ | |
return &variables[ i ]; | |
} | |
} | |
return 0; | |
} | |
static VariableInfo* FindVariableByAddress( const void* variable ) | |
{ | |
for( unsigned i = 0; i < variableCount; ++i ) | |
{ | |
if( variable == variables[ i ].address ) | |
{ | |
return &variables[ i ]; | |
} | |
} | |
return 0; | |
} | |
void* Reflection::internal::New( const char* name, const void* type ) | |
{ | |
if( name == 0 && type != 0 ) | |
{ | |
ClassInfo* info = FindClassInfoByType( type ); | |
assert( info && "No class of the specified type have been exposed." ); | |
assert( info->type == type && "Type id mismatch." ); | |
void* instance = info->newFunction(); | |
AddInstance( info, instance ); | |
return instance; | |
} | |
else if( name != 0 && type == 0 ) | |
{ | |
ClassInfo* info = FindClassInfoByName( name ); | |
assert( info && "No class with the specified name have been exposed." ); | |
void* instance = info->newFunction(); | |
AddInstance( info, instance ); | |
return instance; | |
} | |
assert( false && "New called with invalid parameters."); | |
return 0; | |
} | |
void Reflection::internal::Delete( void* instance, const void* type ) | |
{ | |
InstanceInfo* info = FindInstanceInfo( instance ); | |
assert( info && "Attempting to delete an instance which does not exist, or which wasn't created through the reflection system." ); | |
assert( ( type == Reflection::internal::TypeId<void>::id() || type == info->classInfo->type ) && "Type id mismatch." ); | |
info->classInfo->deleteFunction( instance ); | |
RemoveInstance( info ); | |
} | |
void Reflection::internal::Expose( NewFunction newFunction, DeleteFunction deleteFunction, const void* type, const char* name ) | |
{ | |
assert( classCount < MAX_CLASS_COUNT && "Maximum number of exposed classes exceeded." ); | |
assert( FindClassInfoByName( name ) == 0 && "A class with this identifier is already exposed." ); | |
assert( FindClassInfoByType( type ) == 0 && "A class of this type is already exposed." ); | |
ClassInfo* info = &classes[ classCount ]; | |
++classCount; | |
info->type = type; | |
CopyIdentifier( info->name, name ); | |
info->newFunction = newFunction; | |
info->deleteFunction = deleteFunction; | |
} | |
void Reflection::internal::Expose( const void* instance, const void* instance_type, void* variable, const void* variable_type, const char* name ) | |
{ | |
ClassInfo* classInfo = FindClassInfoByType( instance_type ); | |
assert( classInfo && "No class of the specified type have been exposed." ); | |
assert( ( FindInstanceInfo( instance ) == 0 || classInfo == FindInstanceInfo( instance )->classInfo ) && "Type id mismatch." ); | |
assert( FindVariableByName( instance, name ) == 0 && "A variable by that name is already exposed for this instance." ); | |
assert( FindVariableByAddress( variable ) == 0 && "The variable is already exposed." ); | |
assert( variableCount < MAX_VARIABLE_COUNT && "Maximum number of exposed variables exceeded." ); | |
VariableInfo* info = &variables[ variableCount ]; | |
++variableCount; | |
info->instance = instance; | |
info->classInfo = classInfo; | |
CopyIdentifier( info->name, name ); | |
info->type = variable_type; | |
info->address = variable; | |
} | |
void Reflection::internal::Revoke( const void* instance, const void* type ) | |
{ | |
for( unsigned i = 0; i < variableCount; ++i ) | |
{ | |
if( variables[ i ].instance == instance ) | |
{ | |
assert( variables[ i ].classInfo->type == type && "Type id mismatch." ); | |
// Move last element into the instance slot we are removing | |
variables[ i ] = variables[ variableCount - 1 ]; | |
--variableCount; | |
--i; | |
} | |
} | |
} | |
void* Reflection::internal::ExposedVariable( const void* instance, const void* variable_type, const char* name ) | |
{ | |
VariableInfo* info = FindVariableByName( instance, name ); | |
assert( info && "Variable not exposed" ); | |
assert( info->type == variable_type && "The exposed variable does not match the requested type." ); | |
return info->address; | |
} | |
unsigned Reflection::internal::ExposedVariableCount( const void* instance ) | |
{ | |
unsigned count = 0; | |
for( unsigned i = 0; i < variableCount; ++i ) | |
{ | |
if( instance == variables[ i ].instance ) | |
{ | |
++count; | |
} | |
} | |
return count; | |
} | |
const char* Reflection::internal::ExposedVariableName( const void* instance, unsigned index ) | |
{ | |
unsigned count = 0; | |
for( unsigned i = 0; i < variableCount; ++i ) | |
{ | |
if( instance == variables[ i ].instance ) | |
{ | |
if( count == index) | |
{ | |
return variables[ i ].name; | |
} | |
++count; | |
} | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef Reflection_h | |
#define Reflection_h | |
namespace Reflection | |
{ | |
namespace internal | |
{ | |
// Type info, a unique id to be used in comparisons (eg. TypeId<T1>::id()==TypeId<T2>::id() ) | |
template< typename T > struct TypeId | |
{ | |
static const void* id() { return (const void*) &id; } // Use pointer to function as unique id | |
}; | |
typedef void* (*NewFunction)(); | |
typedef void (*DeleteFunction)( void* ); | |
template< typename T > void* FactoryNew() { return new T(); } | |
template< typename T > void FactoryDelete( void* instance ) { delete (T*) instance; } | |
void* New( const char* name, const void* type ); | |
void Delete( void* instance, const void* type ); | |
void Expose( NewFunction newFunction, DeleteFunction deleteFunction, const void* type, const char* name ); | |
void Expose( const void* instance, const void* instance_type, void* variable, const void* variable_type, const char* name ); | |
void Revoke( const void* instance, const void* type ); | |
void* ExposedVariable( const void* instance, const void* variable_type, const char* name ); | |
unsigned ExposedVariableCount( const void* instance ); | |
const char* ExposedVariableName( const void* instance, unsigned index ); | |
} | |
// Expose a class - allows calling Reflection::New and Reflection::Delete for the exposed class. | |
template< typename T > void Expose( const char* name ) | |
{ | |
internal::Expose( internal::FactoryNew<T>, internal::FactoryDelete<T>, internal::TypeId<T>::id(), name ); | |
} | |
// Expose a member variable for a specific instance | |
template< typename T, typename U > void Expose( const T* instance, U* variable, const char* name ) | |
{ | |
internal::Expose( (void*) instance, internal::TypeId<T>::id(), (void*) variable, internal::TypeId<U>::id(), name ); | |
} | |
// Remove all exposed variables for a specific instance | |
template< typename T > void Revoke( const T* instance ) | |
{ | |
internal::Revoke( (void*) instance, internal::TypeId<T>::id() ); | |
} | |
// Instantiate a new class based on its type ( eg. MyClass* myObj = Reflection::New<MyClass>(); ) | |
// Basically equivalent to MyClass* myObj = new MyClass(), except objects created using this way | |
// can be deleted by using Reflection::Delete | |
template< typename T > T* New() | |
{ | |
return (T*) internal::New( 0, internal::TypeId<T>::id() ); | |
} | |
// Instantiate an exposed class from its name. | |
inline void* New( const char* name ) | |
{ | |
return internal::New( name, 0 ); | |
} | |
// Delete an object instance which was created through a call to Reflection::New. The type of | |
// the instance object may be a void* | |
template< typename T > void Delete( T* instance ) | |
{ | |
internal::Delete( (void*) instance, internal::TypeId<T>::id() ); | |
} | |
// Set the value of an exposed variable | |
template< typename T, typename U > void Set( const T* instance, const char* name, const U& value ) | |
{ | |
U* variable = (U*) internal::ExposedVariable( (const void*) instance, internal::TypeId<U>::id(), name ); | |
if( variable ) | |
{ | |
*variable = value; | |
} | |
} | |
// Get the value of an exposed variable | |
template< typename T, typename U > void Get( const T* instance, const char* name, U* value ) | |
{ | |
U* variable = (U*) internal::ExposedVariable( (const void*) instance, internal::TypeId<U>::id(), name ); | |
if( variable ) | |
{ | |
*value = *variable; | |
} | |
} | |
// Get the number of exposed variables for a specific instance | |
template< typename T> unsigned GetVariableCount( const T* instance ) | |
{ | |
return internal::ExposedVariableCount( (const void*) instance ); | |
} | |
// Get the name of one of the exposed variables for a specific instance | |
template< typename T> const char* GetVariableName( const T* instance, unsigned index ) | |
{ | |
return internal::ExposedVariableName( (const void*) instance, index ); | |
} | |
} | |
#endif /* Reflection_h */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment