// Copyright (c) 2000 Stuart McConnell
//
// stuart_mcconnell@mail.com
//

#if defined ( WIN32 )
#pragma warning ( disable : 4786 )
#endif

#include <iostream>
#include <string>
#include <map>
using namespace std;

// template based factory
template <typename T>
class factory
{
public:
	typedef typename T base_type;

	class creator_base
	{
	public:
		base_type* operator()() { return create(); }
		virtual base_type* create() const = 0;
	};

	template <typename D>
	class creator : public creator_base
	{
	public:
		typedef typename D derived_type;
		virtual base_type* create() const { return new derived_type(); }
	};

	typedef std::map<std::string, creator_base*> string_creator_map;

	void register_object( const std::string& classname, creator_base* creator )
	{
		string_creator_map::iterator iter = _classes.find( classname );
		
		if ( iter == _classes.end() )
		{
			_classes[classname] = creator;
		}
	}

	base_type* create_object( const std::string& classname )
	{
		base_type* obj = 0;
	
		string_creator_map::iterator iter = _classes.find( classname );

		if ( iter != _classes.end() )
		{
			obj = (*(*iter).second)();
		}

		return obj;
	}

protected:
	string_creator_map _classes;
};

// sample test program with an example object hierarchy
class object
{};

class game_object : public object
{};

class light_object : public object
{};

// create an instance of a factory for 'object' derived classes
static factory<object> f;

// simple wrapper for creating an object
object* create( const std::string& classname )
{
	object* obj = f.create_object( classname );
	cout << "create_object(" << classname << ")\t:\t" << ( (obj) ? "successful" : "failed") << endl << endl;	
	return obj;
}

int main( int argc, char* argv[] )
{
	// register the sample derived objects
	f.register_object( "game_object", new factory<object>::creator<game_object>() );
	f.register_object( "light_object", new factory<object>::creator<light_object>() );

	// try to create instances of the objects
	object* game = create( "game_object" );
	object* light = create( "light_object" );
	object* other = create( "other_object" );

	// cleanup
	delete game;
	delete light;
	delete other;

	return 0;
}
