// factory.h
#ifndef FACTORY_H
#define FACTORY_H

#include <map>
#include <utility>

// pluggable factories

template <class Object, class Param, class ID>
class Factory
{
public:
	virtual ~Factory();

	static Object* newObject(Param param);

protected:
	Factory(ID id);

	static ID GetID(Param param); // must be implemented separately

	virtual Object* makeObject(Param param) const = 0;

private:
	typedef Factory<Object, Param, ID>* MakerPtr;
	typedef std::map<ID, MakerPtr> MakerMap;

	static MakerMap st_registry;
};

template <class Object, class Param, class ID>
Object* Factory<Object, Param, ID>::newObject(Param param)
{
	ID id = GetID(param);
	typename MakerMap::const_iterator it = st_registry.find(id);
	return it != st_registry.end()? it->second->makeObject(param) : 0;
};

template <class Object, class Param, class ID>
Factory<Object, Param, ID>::Factory(ID id)
{
	std::pair<typename MakerMap::iterator, bool> p =
	    st_registry.insert(std::make_pair(id, this));
}

template <class Object, class Param, class ID>
Factory<Object, Param, ID>::~Factory()
{
}

template <class Object, class Param, class ID>
typename Factory<Object, Param, ID>::MakerMap Factory<Object, Param, ID>::st_registry;

// maker

template <class Object, class Base, class Param, class ID, ID id>
class Maker : public Factory<Base, Param, ID>
{
public:
	Maker() : Factory<Base, Param, ID>(id)
	{}

protected:
	Object* makeObject(Param param) const
	{
		return new Object(param);
	}

private:
	static const Maker registerThis;
};

template <class Object, class Base, class Param, class ID, ID id>
const Maker<Object, Base, Param, ID, id> Maker<Object, Base, Param, ID, id>::registerThis;

#endif

// FactoryTest.cpp
//#include "factory.h"
#include <iostream>

class TestBase
{
public:
	virtual void load() = 0;
};

class Test1 : public TestBase
{
public:
	Test1(std::istream& is)
	{}
	
	void load()
	{
		std::cout << "loading Test1 data" << std::endl;
	}
};

class Test2 : public TestBase
{
public:
	Test2(std::istream& is)
	{}
	
	void load()
	{
		std::cout << "loading Test2 data" << std::endl;
	}
};

typedef Factory<TestBase, std::istream&, int> TestFactory;

template<>
int TestFactory::GetID(std::istream& is)
{
	int id = 0;
	is >> id;
	return id;
}

void FactoryTest()
{
	// instantiate maker classes
	static Maker<Test1, TestBase, std::istream&, int, 1> maker1;
	static Maker<Test2, TestBase, std::istream&, int, 2> maker2;

	TestBase *base = TestFactory::newObject(std::cin);
	if (base)
		base->load();
}

int main()
{
	while (std::cin)
	  FactoryTest();
	  
	return 0;
}