Skip to main content
GameDev.net gamedev.net
🔒 Locked

Pluggable Factories - initialisation problems

Started by Nanoprobe Mar 17, 2004 at 7:06 AM 6 replies 1.5k views
Original Post
Nanoprobe
Nanoprobe
I''m in the process of writing some code which can represent any value by using a Value object, and can do casts between arbitrary values by using Cast objects (if they exist for two different types of values). The whole thing is using pluggable factories to allow easy adding of more value types (for instance, classes) later on. The problem is that I''m getting linker errors. I''m using VS.NET 2003 as my compiler - unmanaged C++. First, some code (apologies for the horrid formatting in advance - i''m using 4 space tabs):
System.h

#pragma once

#include <string>
#include <map>
#include <vector>

#include <assert.h>

using namespace std;

namespace System {

	typedef signed __int8		sint8;
	typedef unsigned __int8		uint8;
	typedef signed __int16		sint16;
	typedef	unsigned __int16	uint16;
	typedef signed __int32		sint32;
	typedef unsigned __int32	uint32;
	typedef	signed __int64		sint64;
	typedef unsigned __int64	uint64;

	typedef	float				real32;
	typedef	double				real64;

	typedef basic_string<uint8>	binstring;

	typedef uint32				idtype;
	typedef uint16				hashtype;

	///////////////////////////////////////////////////////////////////////////////


	///////////////////////////////////////////////////////////////////////////////


	namespace Utility {
		class IRefCountable {
		public:
			virtual ~IRefCountable() {};

			virtual void	addRef() = 0;
			virtual void	subRef() = 0;
			virtual sint32	getRefCount() = 0;
		};

		class SimpleRefCount : IRefCountable {
		protected:
			sint32			refCount;
		public:
			SimpleRefCount() : refCount(0) {};
			virtual ~SimpleRefCount() {};
            
			virtual void	addRef()		{ refCount++; };
			virtual void	subRef()		{ refCount--; };
			virtual sint32	getRefCount()	{ return refCount; };
		};

		class AutoDelete : public IRefCountable {
		protected:
			sint32			refCount;
		public:
			AutoDelete() : refCount(0) {};
			virtual ~AutoDelete() {};		// FIXME: implement memory leak logging


			virtual void	addRef()		{ refCount++; };
			virtual void	subRef()		{ if ( --refCount < 1 ) delete this; };
			virtual sint32	getRefCount()	{ return refCount; };
		};
	}

}
Value.h

#pragma once

#include "System.h"

namespace System {

	class Value : public Utility::AutoDelete {
	public:
		class Type {
		private:
			static map< idtype, Type* >	_types;

		public:
			static Type	_VTsint8;
			static Type	_VTuint8;
			static Type	_VTsint16;
			static Type	_VTuint16;
			static Type	_VTsint32;
			static Type	_VTuint32;
			static Type	_VTsint64;
			static Type	_VTuint64;

			static Type	_VTreal32;
			static Type	_VTreal64;

		private:
			idtype						id;
			string						name;

		public:
			Type( idtype type, string humanName ) : id( type ), name( humanName ) {
				_types.insert( pair< idtype, Type* > ( type, this ) );
			};

			static Type*	get( idtype type ) {
				map< idtype, Type* >::iterator	i = _types.find( type );
				if ( i != _types.end() ) { return i->second; }
				else { return NULL; }
			};

			static vector< Type* >	getAll() {
				vector< Type* >	ret;

				map< idtype, Type* >::iterator	i = _types.begin();
				while ( i++ != _types.end() ) { ret.push_back( i->second ); }
				return ret;
			};
		};

		class Factory {
		private:
			static map< Type*, Factory* >	_factories;
		protected:
			virtual Value*	manufacture() = 0;
		public:
			Factory( Value::Type* type ) {
				_factories.insert( pair< Type*, Factory* > ( type, this ) );
			};

			static Value* create( Type* type ) {
				map< Type*, Factory* >::iterator	i = _factories.find( type );
				if ( i != _factories.end() ) { return i->second->manufacture(); }
				else { return NULL; }
			};
		};

		class Cast {
		private:
			static map< pair< Type*, Type* >, Cast* >	_casts;
		protected:
			virtual Value*	performCast( Value* in ) = 0;
		public:
			Cast( Type* from, Type* to ) {
				_casts.insert( pair< pair< Type*, Type* >, Cast* >
					( pair< Type*, Type* > ( from, to ), this ) );
			};

			static Value* typecast( Value* v, Type* type ) {
				assert( v ); assert( type );
				map< pair< Type*, Type* >, Cast* >::iterator	i
					= _casts.find( pair< Type*, Type* > ( v->getType(), type ) );
				if ( i != _casts.end() ) { return i->second->performCast( v ); }
				else { return NULL; }
			};
		};

	public:
		virtual Type*	getType() = 0;

		virtual void	set( Value* v ) = 0;
		virtual Value*	getCopy() = 0;
	};
	///////////////////////////////////////////////////////////////////////////////


	template< class valueType, Value::Type* valueTypeID >
	class simpleValue : public Value {
	public:
		class Factory : public Value::Factory {
		private:
			static const Factory	__f;
		protected:
			virtual Value*	manufacture() {
				return new simpleValue< valueType, valueTypeID >;
			};
		public:
			Factory() : Value::Factory( valueTypeID ) { };
		};

	private:
		valueType	value;
	public:
		simpleValue() { };
		simpleValue( valueType& v ) : value(v) { };

		virtual valueType	get()		{ return value; };
		virtual Value*		getCopy()	{ return new simpleValue< valueType, valueTypeID >( value ); };
		virtual void		set( valueType& v ) { value = v; };
		virtual void		set( Value* v ) {
			Value*	casted = Value::Cast::typecast( v, valueTypeID );
			if ( casted ) {
				simpleValue< valueType, valueTypeID >*	v2 =
					(simpleValue< valueType, valueTypeID >*) casted;
				value = v2->get();
				delete casted, v2;
			}
		};

		virtual Value::Type*	getType() { return valueTypeID; };
	};

///////////////////////////////////////////////////////////////////////////////


	typedef	simpleValue< sint8, &Value::Type::_VTsint8 >	Vsint8;
	typedef simpleValue< uint8, &Value::Type::_VTuint8 >	Vuint8;
	typedef	simpleValue< sint16, &Value::Type::_VTsint16 >	Vsint16;
	typedef simpleValue< uint16, &Value::Type::_VTuint16 >	Vuint16;
	typedef	simpleValue< sint32, &Value::Type::_VTsint32 >	Vsint32;
	typedef simpleValue< uint32, &Value::Type::_VTuint32 >	Vuint32;
	typedef	simpleValue< sint64, &Value::Type::_VTsint64 >	Vsint64;
	typedef simpleValue< uint64, &Value::Type::_VTuint64 >	Vuint64;

	typedef simpleValue< real32, &Value::Type::_VTreal32 >	Vreal32;
	typedef simpleValue< real64, &Value::Type::_VTreal64 >	Vreal64;

}
Value.cpp

#include "Value.h"

using namespace System;

// all this stuff is here for implementing serialisation later

Value::Type	Value::Type::_VTsint8	= Value::Type( ''SI8_'', "integer - signed 8-bit" );
Value::Type	Value::Type::_VTuint8	= Value::Type( ''UI8_'', "integer - unsigned 8-bit" );
Value::Type	Value::Type::_VTsint16	= Value::Type( ''SI16'', "integer - signed 16-bit" );
Value::Type	Value::Type::_VTuint16	= Value::Type( ''UI16'', "integer - unsigned 16-bit" );
Value::Type	Value::Type::_VTsint32	= Value::Type( ''SI32'', "integer - signed 32-bit" );
Value::Type	Value::Type::_VTuint32	= Value::Type( ''UI32'', "integer - unsigned 32-bit" );
Value::Type	Value::Type::_VTsint64	= Value::Type( ''SI64'', "integer - signed 64-bit" );
Value::Type	Value::Type::_VTuint64	= Value::Type( ''UI64'', "integer - unsigned 64-bit" );

Value::Type	Value::Type::_VTreal32	= Value::Type( ''FP32'', "real - 32-bit" );
Value::Type	Value::Type::_VTreal64	= Value::Type( ''FP64'', "real - 64-bit" );

The problem is when trying to use any of this code, I get linker errors. Specifically, adding this to main():
System::Vuint32 v; 
If I don''t include that code, I don''t get the linker errors. Now, I''ve figured that it has to be something to do with my static member initialisation. I''ve come to the conclusion that adding this code to initialise the maps in the Cast and Type classes to Value.cpp removes the linking errors:
map< pair< Value::Type*, Value::Type* >, Value::Cast* >	Value::Cast::_casts;
map< idtype, Value::Type* >	Value::Type::_types; 
Great, no errors. But that means that something is wrong with my factory code - the static members for Type and Cast aren''t initialising on their own. Which they should be - they''re pretty much clones of the Factory class (taken from the Pluggable Factories article on this site), which works fine and doesn''t give me problems. I know I have to be missing something obvious - ideas anyone? - Nanoprobe
Nanoprobe
amag
amag
Static class members always needs to be initialised separately or else they won''t be defined for the linker to use.
Example:

class A
{
static int i;
};
int A::i = 0;
Nanoprobe
Nanoprobe
But you see, using this line:

map< Value::Type*, Value::Factory* > Value::Factory::_factories;   


To initialise the _factories map in the Factory class appears to be optional - I get no errors if I leave that line out. But if I leave these lines out:

map< pair< Value::Type*, Value::Type* >, Value::Cast* > Value::Cast::_casts;
map< idtype, Value::Type* > Value::Type::_types;


I get errors.

Also, the Pluggable Factories article didn't initialise its static members separately - I don't see why I should have to do it for one class and not the other. [edit] Also, having to initialise stuff separately kinda defeats the purpose of 'pluggable' factories.

- Nanoprobe

[edited by - Nanoprobe on March 17, 2004 7:05:07 PM]
Nanoprobe
Nanoprobe
Nanoprobe
http://www.gamedev.net/reference/articles/article841.asp

Since I''m not getting many replies, I''ll change my question: how is it that in the article, this:

static net_message_maker_map m_registry; 


apparently doesn''t need to be initialised?

- Nanoprobe
Nanoprobe
Nanoprobe
Nanoprobe
Okay, thanks. I think I''m following a little better now.

I''m gonna go give this a shot.

- Nanoprobe
Nanoprobe
mentat
mentat
I am not the OP but I was trying to find a solution for the same problem, and the idea of having the static registry function saved the day.. just wanted to thank AP..
-----------my quote is under construction

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.