Jump to content
  • Advertisement
Sign in to follow this  
Gluber2007

C++ Reflection

This topic is 3777 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi there ! I am currently working on adding Reflection/Introspection features to C++. (without requiring PDB files, a GCCXML backend or so, everything should be in the code itself ) I have to admit that I am far from being a C++ guru, and thus I am looking for some advice/critcism on what I could do to improve my code for implementing reflection. First of, i decided to emulate the .NET/JAVA Type system. There are value types, and reference types in my system. Reference types derive from a custom base class called "Object", value types are simple builtin types or POD. Reference types are always allocated on the heap and managed via boost::intrusive_ptr ( used because it helps with passing pointers from member functions using the this pointer, and DLL boundaries ) Valuetypes are used normally and when I need to pass them around in Object form they are boxed/unboxed into objects. All types are described using some template classes and registered into a global type registry, so one can retrieve a TypeDescription for each type. So far its basically what .NET and JAVA do. Of course this has some holes in it since nothing is to prevent someone from for example allocating an object derived/reference type on the stack, but i choose to ignore that for now. Now the area where i am on shaky ground is when describing methods/properties of reference types. For example I use a method: template<typename TProperty> AbstractReferenceTypeDescription & Property( const std::string & name,const TProperty & (TReference::*getter)() const,void (TReference::*setter)(const TProperty &) ) in order to use automatic type inference and be able to use calls like: Property( "HitPoints", &MyClass::GetHitpoints,&MyClass::SetHitpoints ); Now for methods i planned to do the same but run into some nasty code duplication issues... since i would have to duplicate the registration method and method description class for each number of parameters e.g template<typename TReturn> class M1 template<typename TReturn,Ttypename TP1> class M2 template<typename TReturn,typename TP1,typename TP2> class M3 I tried to use boost::preprocessor to auto generate these duplicates but have failed to get it working until now.. So all in all i would like some of you who have a far greater understanding of C++ etc to take a look at my code and provide some advice/criticism on how to do it better. Here is the code: ScaleTypeDescription.hpp

#pragma once
#ifndef __SCALE_FOUNDATION_TYPEDESCRIPTION_HPP__
#define __SCALE_FOUNDATION_TYPEDeSCRIPTION_HPP__

#include <map>

#include <boost/preprocessor/facilities/expand.hpp>

#include <ScaleTypeInfo.hpp>
#include <ScaleObject.hpp>

namespace Scale
{
	namespace Foundation
	{
		// TODO: Enum values, methods

		typedef boost::intrusive_ptr<class PropertyDescription> PropertyDescriptionPtr;
		typedef std::vector<PropertyDescriptionPtr>			    PropertyDescriptionList;
		typedef boost::intrusive_ptr<class MethodDescription>	MethodDescriptionPtr;
		typedef std::vector<MethodDescriptionPtr>				MethodDescriptionList;

		// The base type object:
		class SCALE_FOUNDATION_API TypeDescription : public Object
		{
			public:

			virtual bool				            IsAbstract() const = 0;
			virtual bool							IsReference() const = 0;
			virtual bool							IsValue() const = 0;
			virtual bool							IsEnum() const = 0;

			virtual int								GetId() const;
			virtual const std::string &             GetName() const;
			virtual const std::string &             GetFullName() const;
			virtual const TypeDescriptionList    &  GetBases() const = 0;

			virtual const TypeInfo    &             GetRaw() const;
			virtual int					            GetSize() const = 0;

			virtual ObjectPtr			            CreateInstance() const = 0;
		
			virtual const PropertyDescriptionList & GetProperties() const = 0;
			virtual const PropertyDescriptionPtr  & GetProperty( const std::string & name ) const = 0;

			protected:

			TypeDescription( const TypeInfo & rawType,
							 int id );
			virtual ~TypeDescription();
			
			private:

			int										mId;
			std::string								mName;
			std::string								mFullName;

			TypeInfo								mRawType;
		};
		
		namespace Private
		{
			template< typename TType >
			class TypeDescriptionBase : public TypeDescription
			{
				public:

				virtual int                        GetSize() const
				{
					return sizeof(TType);
				}

				protected:

				TypeDescriptionBase( int id )
					: TypeDescription( typeid(TType),id ) 
				{}

				virtual ~TypeDescriptionBase() {}
			};
		}

		template< typename TReference >
		class AbstractReferenceTypeDescription : public Private::TypeDescriptionBase<TReference>
		{
			public:

			virtual bool				          IsAbstract() const
			{
				return true;
			}

			virtual bool						  IsReference() const
			{
				return true;
			}

			virtual bool						  IsValue() const
			{
				return false;
			}

			virtual bool						  IsEnum() const
			{
				return false;
			}

			virtual const TypeDescriptionList &	  GetBases() const
			{
				if ( !mBasesAreResolved )
				{
					for( std::vector<TypeInfo>::const_iterator iter = mBasesRaw.begin(); iter != mBasesRaw.end(); ++ iter )
					{
						const TypeDescriptionPtr & baseType = Scale::Foundation::GetType( (*iter) );
						if ( baseType )
						{
							mBasesResolved.push_back( baseType );
						}
					}

					mBasesAreResolved = true;
				}

				return mBasesResolved;
			}

			virtual ObjectPtr                     CreateInstance() const
			{
				return ObjectPtr();
			}

			virtual const PropertyDescriptionPtr & GetProperty( const std::string & propertyName ) const
			{
				std::map<std::string,PropertyDescriptionPtr>::const_iterator iter = mPropertiesMap.find(propertyName);

				if ( iter != mPropertiesMap.end() )
				{
					return iter->second;
				}
				else
				{
					static PropertyDescriptionPtr result;
					return result;
				}
			}

			virtual const PropertyDescriptionList & GetProperties() const
			{
				if ( !mPropertiesAreResolved )
				{
					for( std::map<std::string,PropertyDescriptionPtr>::const_iterator iter = mPropertiesMap.begin(); iter != mPropertiesMap.end(); ++ iter )
					{
						mPropertiesList.push_back( iter->second );
					}

					mPropertiesAreResolved = true;
				}

				return mPropertiesList;
			}

			protected:

			template<typename TBase>
			AbstractReferenceTypeDescription &    Base()
			{
				mBasesRaw.push_back( typeid(TBase) );
				return *this;
			}

			template<typename TProperty>
			AbstractReferenceTypeDescription &	  Property( const std::string & name,const TProperty & (TReference::*getter)() const,void (TReference::*setter)(const TProperty &) )
			{
				PropertyDescriptionImpl<TReference,TProperty> * propertyDescription = new PropertyDescriptionImpl<TReference,TProperty>(name,getter,setter);
				mPropertiesMap.insert( std::pair<std::string,PropertyDescriptionPtr>( name,PropertyDescriptionPtr( propertyDescription ) ) );

				return *this;
			}

			template<typename TProperty>
			AbstractReferenceTypeDescription &	  Property( const std::string & name,void (TReference::*setter)(const TProperty &) )
			{
				PropertyDescriptionImpl<TReference,TProperty> * propertyDescription = new PropertyDescriptionImpl<TReference,TProperty>(name,0,setter);
				mPropertiesMap.insert( std::pair<std::string,PropertyDescriptionPtr>( name,PropertyDescriptionPtr( propertyDescription ) ) );

				return *this;
			}

			template<typename TProperty>
			AbstractReferenceTypeDescription &	  Property( const std::string & name,const TProperty & (TReference::*getter)() const )
			{
				PropertyDescriptionImpl<TReference,TProperty> * propertyDescription = new PropertyDescriptionImpl<TReference,TProperty>(name,getter,0);
				mPropertiesMap.insert( std::pair<std::string,PropertyDescriptionPtr>( name,PropertyDescriptionPtr( propertyDescription ) ) );

				return *this;
			}
		
			AbstractReferenceTypeDescription( int id ) 
				: TypeDescriptionBase( id ), mBasesAreResolved(false),mPropertiesAreResolved(false)
			{}

			virtual ~AbstractReferenceTypeDescription() {}

			private:
		
			std::vector<TypeInfo>		                 mBasesRaw;
			mutable TypeDescriptionList	                 mBasesResolved;
			mutable bool				                 mBasesAreResolved;

			std::map<std::string,PropertyDescriptionPtr> mPropertiesMap;
			mutable PropertyDescriptionList              mPropertiesList;
            mutable bool							     mPropertiesAreResolved;

		};

		template<typename TReference>
		class ReferenceTypeDescription : public AbstractReferenceTypeDescription<TReference>
		{
			public:

			virtual bool				         IsAbstract() const
			{
				return false;
			}

			virtual ObjectPtr			         CreateInstance() const
			{
				return ObjectPtr( new TReference() );
			}

			protected:
		
			ReferenceTypeDescription( int id )
				: AbstractReferenceTypeDescription(id)
			{}

			virtual ~ReferenceTypeDescription() {}
		};

		template<typename TValue>
		class ValueTypeDescription : public Private::TypeDescriptionBase<TValue>
		{
			public:
		
			virtual bool				        IsAbstract() const
			{
				return false;
			}

			virtual bool						IsReference() const
			{
				return false;
			}

			virtual bool				        IsValue() const
			{
				return true;
			}

			virtual bool						IsEnum() const
			{
				return false;
			}

			virtual ObjectPtr                   CreateInstance() const
			{
				return ObjectPtr( new ValueType<TValue>(TValue()));
			}

			virtual const TypeDescriptionList &	GetBases() const
			{
				static TypeDescriptionList types;
				return types;
			}

			virtual const PropertyDescriptionList & GetProperties() const
			{
				static PropertyDescriptionList result;
				return result;
			}

			virtual const PropertyDescriptionPtr  & GetProperty( const std::string & name ) const 
			{
				static PropertyDescriptionPtr result;
				return result;
			}

			protected:

			ValueTypeDescription( int id )
				: TypeDescriptionBase(id)
			{}

			virtual ~ValueTypeDescription() {}
		};

		template<typename TEnum>
		class EnumTypeDescription : public ValueTypeDescription<TEnum>
		{
			public:

			virtual bool					   IsEnum() const
			{
				return true;
			}

			const std::string &				   GetName( const TEnum & value ) const
			{
				return mValueToName[value];
			}

			const TEnum &					   GetValue( const std::string & name ) const
			{
				return mNameToValue[name];
			}

			protected:

			EnumTypeDescription &			   Value( const TEnum & value, const std::string & name )
			{
				mValueToName[value] = name;
				mNameToValue[name]  = value;
				return *this;
			}
		
			EnumTypeDescription( int id )
				: ValueTypeDescription(id)
			{}

			virtual ~EnumTypeDescription() {}

			private:

			std::map<TEnum,std::string>        mValueToName;
			std::map<std::string,TEnum>        mNameToValue;
		};

		SCALE_FOUNDATION_API void              RegisterType( const TypeDescriptionPtr & type );
	}
}

#define SCALE_TYPEDESCRIPTION_INTERNAL_BEGIN( ttypedescriptorfull,ttypedescriptor,tid,tname ) 	class TypeDescription##tid : public ttypedescriptorfull##<##tname##> 	{ 	  		public: 				class Registrator 		{				public: 						Registrator() 			{ 			Scale::Foundation::RegisterType( Scale::Foundation::TypeDescriptionPtr( new TypeDescription##tid##() ) ); 			} 		}; 				TypeDescription##tid##(); 	}; 		static TypeDescription##tid##::Registrator sTypeDescriptionRegistrator##tid##; 		TypeDescription##tid##::TypeDescription##tid##() : ttypedescriptor##( tid ) 	{

#define SCALE_TYPEDESCRIPTION_INTERNAL_END() }; 
#define SCALE_TYPEDESCRIPTION_INTERNAL_ARGS( ttypedescriptorfull,ttypedescriptor,tname ) ( ttypedescriptorfull,ttypedescriptor,__COUNTER__,tname )

#define SCALE_TYPEDESCRIPTION_ABSTRACT_BEGIN( tname ) BOOST_PP_EXPAND( SCALE_TYPEDESCRIPTION_INTERNAL_BEGIN SCALE_TYPEDESCRIPTION_INTERNAL_ARGS( Scale::Foundation::AbstractReferenceTypeDescription,AbstractReferenceTypeDescription,tname ) )
#define SCALE_TYPEDESCRIPTION_ABSTRACT_END() SCALE_TYPEDESCRIPTION_INTERNAL_END()

#define SCALE_TYPEDESCRIPTION_REFERENCE_BEGIN( tname ) BOOST_PP_EXPAND( SCALE_TYPEDESCRIPTION_INTERNAL_BEGIN SCALE_TYPEDESCRIPTION_INTERNAL_ARGS( Scale::Foundation::ReferenceTypeDescription,ReferenceTypeDescription,tname ) ) 
#define SCALE_TYPEDESCRIPTION_REFERENCE_END() SCALE_TYPEDESCRIPTION_INTERNAL_END()

#define SCALE_TYPEDESCRIPTION_VALUE_BEGIN( tname ) BOOST_PP_EXPAND( SCALE_TYPEDESCRIPTION_INTERNAL_BEGIN SCALE_TYPEDESCRIPTION_INTERNAL_ARGS( Scale::Foundation::ValueTypeDescription,ValueTypeDescription,tname ) )
#define SCALE_TYPEDESCRIPTION_VALUE_END() SCALE_TYPEDESCRIPTION_INTERNAL_END()

#define SCALE_TYPEDESCRIPTION_ENUM_BEGIN( tname ) BOOST_PP_EXPAND( SCALE_TYPEDESCRIPTION_INTERNAL_BEGIN SCALE_TYPEDESCRIPTION_INTERNAL_ARGS( Scale::Foundation::EnumTypeDescription,EnumTypeDescription,tname ) )
#define SCALE_TYPEDESCRIPTION_ENUM_END() SCALE_TYPEDESCRIPTION_INTERNAL_END()

#define SCALE_TYPEDESCRIPTION_BASE( tbase ) Base< tbase >();
#define SCALE_TYPEDESCRIPTION_ENUM_VALUE( tvalue ) Value( tvalue,#tvalue );
#define SCALE_TYPEDESCRIPTION_PROPERTY_READWRITE( tname,name ) Property(#name, &##tname##::Get##name , &##tname##::Set##name );
#define SCALE_TYPEDESCRIPTION_PROPERTY_READ( tname,name ) Property(#name, &##tname##::Get##name );
#define SCALE_TYPEDESCRIPTION_PROPERTY_WRITE( tname,name ) Property(#name, &##tname##::Set##name );
#define SCALE_TYPEDESCRIPTION_METHOD( tname,name ) Method(#name,&##tname##::##name );

#define SCALE_AUTO_PROPERTY( tvisibility,ttype,tname ) 	private: ttype m##tname##BackingField; 	tvisibility##: 		const ttype & Get##tname() const { return m##tname##BackingField; } 		void Set##tname( const ttype & value ) { m##tname##BackingField = value; }

#endif 

///////////////////////////////////////////////////////////////////////////////////////
// End of file - ScaleTypeDescription.hpp
///////////////////////////////////////////////////////////////////////////////////////





ScaleTypeDescription.cpp

#include <boost/algorithm/string.hpp>

#include <ScaleSingleton.hpp>
#include <ScaleTypeDescription.hpp>
#include <ScalePropertyDescription.hpp>

using namespace Scale::Foundation;

///////////////////////////////////////////////////////////////////////////////////////
class TypeManager : public Singleton<TypeManager>
{
	friend class Singleton<TypeManager>;

	public:

	const TypeDescriptionPtr & GetType( const std::string & name ) const
	{		
		std::map<std::string,TypeDescriptionPtr>::const_iterator iter = mNameToType.find(name);
		if ( iter != mNameToType.end() )
		{
			return iter->second;
		}

		return mNullPtr;
	}

	const TypeDescriptionPtr & GetType( const TypeInfo & type ) const
	{		
		std::map<TypeInfo,TypeDescriptionPtr>::const_iterator iter = mTypeInfoToType.find(type);
		if ( iter != mTypeInfoToType.end() )
		{
			return iter->second;
		}

		return mNullPtr;
	}

	const TypeDescriptionList & GetTypes() const
	{
		return mTypes;
	}

	void RegisterType( const TypeDescriptionPtr & type )
	{
		mTypeInfoToType.insert( std::pair<TypeInfo,TypeDescriptionPtr>(type->GetRaw(),type) );
		mNameToType.insert( std::pair<std::string,TypeDescriptionPtr>(type->GetName(),type) );
		mTypes.push_back(type);
	}

	private:

	TypeManager() {}
	virtual ~TypeManager() {}

	TypeDescriptionPtr							 mNullPtr;
	std::map<TypeInfo,TypeDescriptionPtr>        mTypeInfoToType;
	std::map<std::string,TypeDescriptionPtr>     mNameToType;
	std::vector<TypeDescriptionPtr>		         mTypes;
};

SCALE_IMPLEMENT_SINGLETON(TypeManager)

///////////////////////////////////////////////////////////////////////////////////////
TypeDescription::TypeDescription( const TypeInfo & rawType,int id )
	: mId(id),mRawType(rawType)
{
	std::string rawName( rawType.name() );
	mFullName = boost::algorithm::ireplace_all_copy(boost::algorithm::ireplace_first_copy(rawName,"class ",""),"::",".");
	
	if ( mFullName.find_last_of(".") != std::string::npos )
	{
		mName = mFullName.substr( mFullName.find_last_of("." ) + 1 );
	}
	else
	{
		mName = mFullName;
	}
}

///////////////////////////////////////////////////////////////////////////////////////
TypeDescription::~TypeDescription()
{
}

///////////////////////////////////////////////////////////////////////////////////////
int TypeDescription::GetId() const
{
	return mId;
}

///////////////////////////////////////////////////////////////////////////////////////
const std::string & TypeDescription::GetName() const
{
	return mName;
}

///////////////////////////////////////////////////////////////////////////////////////
const std::string & TypeDescription::GetFullName() const
{
	return mFullName;
}

///////////////////////////////////////////////////////////////////////////////////////
const TypeInfo & TypeDescription::GetRaw() const
{
	return mRawType;
}

///////////////////////////////////////////////////////////////////////////////////////
void Scale::Foundation::RegisterType( const TypeDescriptionPtr & type ) 
{
	TypeManager::GetInstance().RegisterType( type );
}

///////////////////////////////////////////////////////////////////////////////////////
const TypeDescriptionList & Scale::Foundation::GetTypes()
{
	return TypeManager::GetInstance().GetTypes();
}

///////////////////////////////////////////////////////////////////////////////////////
const TypeDescriptionPtr & Scale::Foundation::GetType( const std::string & name )
{
	return TypeManager::GetInstance().GetType(name);
}

///////////////////////////////////////////////////////////////////////////////////////
const TypeDescriptionPtr & Scale::Foundation::GetType( const TypeInfo & type )
{
	return TypeManager::GetInstance().GetType(type);
}

///////////////////////////////////////////////////////////////////////////////////////
// Standard type descriptions:
///////////////////////////////////////////////////////////////////////////////////////

SCALE_TYPEDESCRIPTION_VALUE_BEGIN(int)
SCALE_TYPEDESCRIPTION_VALUE_END()

SCALE_TYPEDESCRIPTION_VALUE_BEGIN(float)
SCALE_TYPEDESCRIPTION_VALUE_END()

SCALE_TYPEDESCRIPTION_VALUE_BEGIN(double)
SCALE_TYPEDESCRIPTION_VALUE_END()

SCALE_TYPEDESCRIPTION_VALUE_BEGIN(long)
SCALE_TYPEDESCRIPTION_VALUE_END()

SCALE_TYPEDESCRIPTION_VALUE_BEGIN(short)
SCALE_TYPEDESCRIPTION_VALUE_END()

SCALE_TYPEDESCRIPTION_VALUE_BEGIN(char)
SCALE_TYPEDESCRIPTION_VALUE_END()

///////////////////////////////////////////////////////////////////////////////////////
// End of file - ScaleTypeDescription.cpp
///////////////////////////////////////////////////////////////////////////////////////



ScaleObject.hpp

#pragma once
#ifndef __SCALE_FOUNDATION_OBJECT_HPP__
#define __SCALE_FOUNDATION_OBJECT_HPP__

#include <string>
#include <vector>
#include <stdexcept>

#include <boost/lexical_cast.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/noncopyable.hpp>

#include <ScaleFoundationConfig.hpp>

namespace boost
{
	template<typename TValue>
	void intrusive_ptr_add_ref( TValue * value )
	{
		value->IncRef();
	}

	template<typename TValue>
	void intrusive_ptr_release( TValue * value )
	{
		value->DecRef();
	}
}

namespace Scale
{
	namespace Foundation
	{
		typedef boost::intrusive_ptr<class Object>	        ObjectPtr;
		typedef std::vector<ObjectPtr>						ObjectList;
		typedef boost::intrusive_ptr<class TypeDescription> TypeDescriptionPtr;
		typedef std::vector<TypeDescriptionPtr>				TypeDescriptionList;

		SCALE_FOUNDATION_API const TypeDescriptionPtr &  GetType( const class TypeInfo & typeInfo );
		SCALE_FOUNDATION_API const TypeDescriptionPtr &  GetType( const std::string & name );
		SCALE_FOUNDATION_API const TypeDescriptionList & GetTypes();

		template<typename TType>
		const TypeDescriptionPtr &						 GetType()
		{
			return Scale::Foundation::GetType(typeid(TType) );
		}

		class SCALE_FOUNDATION_API Object : public boost::noncopyable
		{
			public:

			int  IncRef();
			void DecRef();
			int  GetRef() const;

			virtual std::string                ToString() const;
			virtual const TypeDescriptionPtr & GetType() const;
			
			Object();
			virtual ~Object();
			
			private:

			int mRefCount;
		};

		template<typename TValue>
		class ValueType : public Object
		{
			public:

			const TValue & GetValue() const { return mValue; }

			virtual const TypeDescriptionPtr & GetType() const
			{
				return Scale::Foundation::GetType( typeid(TValue) );
			}

			virtual std::string ToString() const
			{
				return boost::lexical_cast<std::string>(GetValue());
			}
	
			ValueType( const TValue & value ) : mValue(value)
			{}

			private:

			TValue mValue;
		};

		template<typename TValue>
		ObjectPtr Box( const TValue & value )
		{
			return ObjectPtr( new ValueType<TValue>(value) );
		}

		template<typename TValue>
		TValue Unbox( const ObjectPtr & boxedValue )
		{
			ValueType<TValue> * valuePtr = dynamic_cast< ValueType<TValue> * >( boxedValue.get() );

			if ( valuePtr )
			{
				return valuePtr->GetValue();
			}
			else
			{
				throw std::runtime_error("Unable to unbox value, incorrect type.");
			}
		}
			
		template< typename TType >
		struct CastFromObject
		{
			typedef TType ReturnType;

			TType operator()( const ObjectPtr & input ) const
			{
				return Unbox<TType>(input);
			}
		};

		template< typename TType >
		struct CastFromObject< boost::intrusive_ptr<TType> >
		{
			typedef boost::intrusive_ptr<TType> ReturnType;

			boost::intrusive_ptr<TType> operator()( const ObjectPtr & input ) const
			{
				return boost::intrusive_ptr<TType>( dynamic_cast<TType*>(input.get()));
			}
		};	

		template<>
		struct CastFromObject<void>
		{
			typedef void ReturnType;
		
			void operator()( const ObjectPtr & input ) const
			{
			}
		};

		template< typename TType >
		struct CastToObject
		{
			ObjectPtr operator()( const TType & value ) const
			{
				return Box<TType>(value);
			}
		};

		template< typename TType >
		struct CastToObject< boost::intrusive_ptr<TType> >
		{
			ObjectPtr operator()( const boost::intrusive_ptr<TType> & value )
			{
				return ObjectPtr( dynamic_cast<Object*>(value.get()) );
			}
		};	

		template<>
		struct CastToObject<void>
		{
			ObjectPtr operator()( void ) const
			{
				return ObjectPtr();
			}
		};
	}
}

#endif 

///////////////////////////////////////////////////////////////////////////////////////
// End of file - ScaleObject.hpp
///////////////////////////////////////////////////////////////////////////////////////


ScaleObject.cpp
#include <iostream>

#include <boost/format.hpp>

#include <ScaleObject.hpp>
#include <ScaleTypeDescription.hpp>

using namespace Scale::Foundation;

///////////////////////////////////////////////////////////////////////////////////////
Object::Object() : mRefCount(0)
{
}

///////////////////////////////////////////////////////////////////////////////////////
Object::~Object()
{
}

///////////////////////////////////////////////////////////////////////////////////////
int Object::IncRef()
{
	++mRefCount;
	return mRefCount;
}

///////////////////////////////////////////////////////////////////////////////////////
int Object::GetRef() const
{
	return mRefCount;
}

///////////////////////////////////////////////////////////////////////////////////////
void Object::DecRef()
{
	--mRefCount;
	if ( mRefCount < 1 )
	{
		delete this;
	}

	// Code is not allowed to touch the this pointer anymore, no data members, no member functions
	// after this point.
}

///////////////////////////////////////////////////////////////////////////////////////
const TypeDescriptionPtr & Object::GetType() const
{
	return Scale::Foundation::GetType( typeid(*this) );
}

///////////////////////////////////////////////////////////////////////////////////////
std::string Object::ToString() const
{
	const TypeDescriptionPtr & type = GetType();
	std::string typeName;

	if ( type )
	{
		typeName = type->GetFullName();
	}
	else
	{
		typeName = typeid(*this).name();
	}

	return (boost::format("Type: %1%, RefCount: %2%, Address: %3%") % typeName % GetRef() % (int)(this)).str();
}

///////////////////////////////////////////////////////////////////////////////////////
// End of file - ScaleObject.cpp
///////////////////////////////////////////////////////////////////////////////////////



ScalePropertyDescription.hpp

#pragma once
#ifndef __SCALE_FOUNDATION_PROPERTYDESCRIPTION_HPP__
#define __SCALE_FOUNDATION_PROPERTYDESCRIPTION_HPP__

#include <ScaleTypeInfo.hpp>
#include <ScaleObject.hpp>

namespace Scale
{
	namespace Foundation
	{
		typedef boost::intrusive_ptr<class TypeDescription> TypeDescriptionPtr;

		class SCALE_FOUNDATION_API PropertyDescription : public Object
		{
			public:

			virtual const std::string &		       GetName() const;

			virtual bool                           CanRead() const = 0;
			virtual bool	                       CanWrite() const = 0;

			virtual const TypeDescriptionPtr &	   GetPropertyType() const;

			virtual void                           SetValue( const ObjectPtr & targetObject, const ObjectPtr & value ) const = 0;
			virtual ObjectPtr                      GetValue( const ObjectPtr & sourceObject ) const = 0;

			protected:

			PropertyDescription( const std::string & propertyName,const TypeInfo & propertyType );
			virtual ~PropertyDescription();

			std::string                            mPropertyName;
			TypeInfo							   mPropertyType;
		};

		template<typename TType,typename TProperty>
		class PropertyDescriptionImpl : public PropertyDescription
		{
			public:

			typedef const TProperty & (TType::*GetterMethod)() const;
			typedef void (TType::*SetterMethod)(const TProperty &);

			virtual bool	 CanRead() const
			{
				return mGetter != 0;
			}

			virtual bool     CanWrite() const
			{
				return mSetter != 0;
			}
		
			virtual void	 SetValue( const ObjectPtr & targetObject,const ObjectPtr & value ) const
			{
				if ( CanWrite() )
				{
					TType * targetToCall = dynamic_cast<TType*>(targetObject.get());				
					(targetToCall->*mSetter)(CastFromObject<TProperty>()(value));
				}
			}

			virtual ObjectPtr GetValue( const ObjectPtr & sourceObject ) const 
			{
				if ( CanRead() )
				{
					TType * sourceToCall = dynamic_cast<TType*>(sourceObject.get());
					return CastToObject<TProperty>()( (sourceToCall->*mGetter)() );
				}

				return ObjectPtr();
			}

			PropertyDescriptionImpl( const std::string & propertyName,const GetterMethod & getter,const SetterMethod & setter )
				: PropertyDescription(propertyName,typeid(TProperty)),mGetter(getter),mSetter(setter)
			{
			}

			private:
		
			SetterMethod mSetter;
			GetterMethod mGetter;			
		};
	}
}

#endif 

///////////////////////////////////////////////////////////////////////////////////////
// End of file - ScalePropertyDescription.hpp
///////////////////////////////////////////////////////////////////////////////////////





ScalePropertyDescription.cpp
#include <boost/foreach.hpp>

#include <ScaleMethodDescription.hpp>
#include <ScaleTypeDescription.hpp>

using namespace Scale::Foundation;

///////////////////////////////////////////////////////////////////////////////////////
MethodDescription::MethodDescription( const std::string & methodName, const TypeInfo & returnType )
	: mMethodName(methodName),mReturnType(returnType),mParameterTypesAreResolved(false)
{
}

///////////////////////////////////////////////////////////////////////////////////////
MethodDescription::~MethodDescription()
{
}

///////////////////////////////////////////////////////////////////////////////////////
const std::string & MethodDescription::GetName() const
{
	return mMethodName;
}

///////////////////////////////////////////////////////////////////////////////////////
const TypeDescriptionPtr & MethodDescription::GetReturnType() const
{
	return Scale::Foundation::GetType(mReturnType);
}

///////////////////////////////////////////////////////////////////////////////////////
const TypeDescriptionList & MethodDescription::GetParameterTypes() const
{
	if ( !mParameterTypesAreResolved )
	{
		BOOST_FOREACH( const TypeInfo & rawType,mParameterTypes )
		{
			const TypeDescriptionPtr & resolvedType = Scale::Foundation::GetType(rawType);

			if ( resolvedType )
			{
				mParameterTypesResolved.push_back( resolvedType );
			}
		}

		mParameterTypesAreResolved;
	}

	return mParameterTypesResolved;
}

///////////////////////////////////////////////////////////////////////////////////////
void MethodDescription::AddParameterType( const TypeInfo & parameterType )
{
	mParameterTypes.push_back(parameterType);
}

///////////////////////////////////////////////////////////////////////////////////////
// End of file - ScaleMethodDescription.cpp
///////////////////////////////////////////////////////////////////////////////////////



Share this post


Link to post
Share on other sites
Advertisement
While this all seems quite interesting to me, it begs the question. Why? Why bother doing all this work when you can easily make use of another language, such as the two you already mentioned: C# and Java. I don't understand why people try to make C++ bend over backwards to be something it's not.

If you're just trying this out because it's cool and want to see if you can do it, well then carry on :)

Share this post


Link to post
Share on other sites
I know were you come from.

The thing is certain tasks are much easier to accomplish with such additions.
For example: Serialization/Configuration, Editing/Gui bindings, Dynamic composition etc.

Take a look at the .NET world for example. The Forms Designers, Data Binding, Serialization/Configuration are all based on the platforms Reflection support.
and certainly make the developers life easier and more productive.

But what if ( which is the case for me ) you have to develop such tools/code for a platform where
a) you don't have any other choice then to use C++ since .NET/JAVA are not available, and
b) you often need the little extra speed of C++

Share this post


Link to post
Share on other sites
Quote:
Original post by Gluber2007
But what if ( which is the case for me ) you have to develop such tools/code for a platform where
a) you don't have any other choice then to use C++ since .NET/JAVA are not available

You use any of a dozen other languages which have these features.
Quote:
b) you often need the little extra speed of C++

You write the bits that need the extra speed in C++.

Your efforts to turn C++ into a modern programming language are laudable, but the Boost guys have been going at it for a decade and haven't accomplished it yet.

Anyways. You mention trying to use boost.preprocessor, but I can't see any invocation of it there. Where/how are you using it?

Share this post


Link to post
Share on other sites
Q1: What is the memory impact of this solution?
Q2: What is the run-time penalty (virtual function calls, dereferences, heap allocations, casts, ...)
Q3: What do I, as a user, need to write?
Q4: I have a class Foo, provided by third-party, for which I only have a header. How do I make it introspectable and reflectable? That class uses proprietary_pointer<Bar> member.
Q5: Is the system thread-safe (I see singletons and auto-registration in there)? Can I make it context-dependant (allocate all this on per-thread basis).


Share this post


Link to post
Share on other sites
I recently implemented something based on this code:
http://www.garret.ru/~knizhnik/cppreflection/docs/reflect.html

It allows me to automatically load/save out classes to disk.
I'm going to use to to automatically send data over network.
I also use it for scripting, so can call any method using a script.



Share this post


Link to post
Share on other sites
Doesn’t one of the game gems books have an article about this?

<edit>
Book 5, 1.4 Using Templates for Reflection in C++. Don’t know if it’s any good but if you can get your hands on the book… can’t hurt to read it.

Share this post


Link to post
Share on other sites
Sneftel:

I would have to port the runtime of a given language to my target platform...
Considering i needed 1 day to write the code above, and taking into account the ammount of time i would need to do that, it does not seem like a good idea.

Also ( serious question ) is almost every toolkit vendor on the PC wrong ?
Take a look at wxWidgets,QT,VCL, etc heck even the oudated COM system on Windows... They all provide some reflection capabilities in one way or another.

Or another serious question, how would you go about writing an editor for your C++ game engine for example ? Which has probably 100eds of entity types all with some properties that need to exposed to an editor GUI. ? Do you write all of that by hand ? ( not to mention reading data from configuration files etc )

Antheus:

Q1: What is the memory impact of this solution?

Depends on the situation, you don't pay for it when you're not using it.
For reference types the overhead is one 32 bit integer for the reference counter, which is the same as almost any other smart pointer solution.

The type descriptions itself of course are resident in memory, I have to check
on its memory impact.

Q2: What is the run-time penalty (virtual function calls, dereferences, heap allocations, casts, ...)

This is of course a biggie, calls to methods/properties at runtime are costly.
But they are never made during time critical code, just for example during editing/serialization time for example at startup.

A call to a method entails one virtual method call, one pointer to member call,
and one normal function call per parameter/return value.
For value types the normal function call incurrs unboxing ( which is another function call and a cast ) for reference types this is another dynamic cast.

Q3: What do I, as a user, need to write?

I am currently experimenting with getting a good syntax for this, the above code is far from finished..

basically you have to describe every type you want to use reflection on.

For reference types this would look something like this:
SCALE_TYPEDESCRIPTION_REFERENCE_BEGIN( MyType )
SCALE_TYPEDESCRIPTION_PROPERTY_READWRITE( MyType,HitPoints )
SCALE_TYPEDESCRIPTION_METHOD( MyType,DoSomething )
SCALE_TYPEDESCRIPTION_REFERENCE_END()

Q4: I have a class Foo, provided by third-party, for which I only have a header. How do I make it introspectable and reflectable? That class uses proprietary_pointer<Bar> member.

The system does not handle this kind of situation, since i only wrote it for my special needs. It's not a general off the shelf reflection solution.
( Fields are not supported right now for example ).

However classes that are not available in source code form are fully supported already, no need to do anything just describe it like in the above sample.

Q5: Is the system thread-safe (I see singletons and auto-registration in there)? Can I make it context-dependant (allocate all this on per-thread basis).

Basically, not yet.

Share this post


Link to post
Share on other sites
A library already exists, it's called the Property Set Library (PSL) and is hosted on Sourceforge http://sourceforge.net/projects/psl/.

The library was written by Torsten Strobl and accompanies his book called 'Modern Concepts Applied to C++ - Object Persistence, Reflection, Events, Garbage Collection and Thread Safety in C++'. The source code comes with a mini-tutorial and is licensed under the Mozilla Public License (MPL 1.1).

Share this post


Link to post
Share on other sites
One hope for a good C++ platform is LLVM, which will also provide reflection. Then, C++ will get the modern platform it deserves.

Oh, and AGAIN: modern programming language != modern platform. Stop confusing these two, will you?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!