Skip to content

Instantly share code, notes, and snippets.

@RichardMarks
Created June 9, 2014 20:37
Show Gist options
  • Save RichardMarks/5d5c6a98ded6e81b0496 to your computer and use it in GitHub Desktop.
Save RichardMarks/5d5c6a98ded6e81b0496 to your computer and use it in GitHub Desktop.
C++ Reflection System by Mattias Gustavsson - Used with Permission.
#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;
}
//*** 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;
}
#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