khanat-opennel-code/code/nel/include/nel/misc/factory.h
2016-12-09 16:04:26 +01:00

255 lines
8.5 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef FACTORY_H
#define FACTORY_H
#include "types_nl.h"
#include "debug.h"
#include <map>
namespace NLMISC
{
/** Interface class for object registered in the factory.
* This is for factory internal use, you should not need to use this class directly.
*/
template <class BaseClass>
class IFactoryRegister
{
public:
virtual ~IFactoryRegister() {}
/// This method is called to create an instance of the factored object.
virtual BaseClass *createObject(const typename BaseClass::TCtorParam &ctorParam) = 0;
};
/** Factory implementation class.
* the class take 2 template argument :
* * BaseClass : the common base class for all factored object of one factory.
* * KeyType : the type of the key that identify the factorable object (string by default).
*
* The factory conforms to singleton design pattern.
*
* BaseClass must provide a typedef for TCTorParam corresponding to the parameter
* required by the constructor of factored object.
*/
template <class BaseClass, class KeyType = std::string>
class CFactory
{
typedef std::map<KeyType, IFactoryRegister<BaseClass>*> TRegisterCont;
public:
/// Get the singleton instance reference.
static CFactory &instance()
{
// Singleton instance pointer.
static CFactory *instance = NULL;
if (!instance)
{
instance = new CFactory();
}
return *instance;
}
/** Register a factorable object in the factory.
* The method receive the key for this factorable object and
* a pointer on the interface of a factory register object.
*/
void registerClass(const KeyType &key, IFactoryRegister<BaseClass> *factoryRegister)
{
nlassert(_FactoryRegisters.find(key) == _FactoryRegisters.end());
_FactoryRegisters.insert(std::make_pair(key, factoryRegister));
}
/** Create a new instance of a factorable object.
* \param key the identifier of the object to create.
* \param ctorParam the parameter for the constructor.
* \return a pointer on the newly created object.
*/
BaseClass *createObject(const KeyType &key, const typename BaseClass::TCtorParam &ctorParam)
{
typename TRegisterCont::iterator it (_FactoryRegisters.find(key));
if (it == _FactoryRegisters.end())
return NULL;
else
return it->second->createObject(ctorParam);
}
// Add some introspection
void fillFactoryList(std::vector<KeyType> &classList)
{
typename TRegisterCont::iterator first(_FactoryRegisters.begin()), last(_FactoryRegisters.end());
for (; first != last; ++first)
{
classList.push_back(first->first);
}
}
protected:
/// Singleton instance pointer.
// static CFactory *_Instance;
/// Container of all registered factorable object.
TRegisterCont _FactoryRegisters;
};
/** This class is responsible for creating the factorable object and to register
* them in the factory instance.
* You must declare an instance of this class for each factorable object.
**/
template <class FactoryClass, class BaseClass, class FactoredClass, class KeyType>
class CFactoryRegister : public IFactoryRegister<BaseClass>
{
public:
/** Constructor.
* Register the factorable object in the factory.
* /param key The identifier for this factorable object.
*/
CFactoryRegister(const KeyType &key)
{
FactoryClass::instance().registerClass(key, this);
}
/** Create an instance of the factorable class.
* Implements IFactoryRegister::createObject
*/
BaseClass *createObject(const typename BaseClass::TCtorParam &ctorParam)
{
return new FactoredClass(ctorParam);
}
};
/** Macro to declare a factory.
* Place this macro in an appropriate cpp file to declare a factory implementation.
* You just need to specify the base class type and key type.
*/
//#define NLMISC_IMPLEMENT_FACTORY(baseClass, keyType) NLMISC::CFactory<baseClass, keyType> *NLMISC::CFactory<baseClass, keyType>::_Instance = NULL
/** Macro to declare a factorable object.
* Place this macro in a cpp file after your factorable class declaration.
*/
#define NLMISC_REGISTER_OBJECT(baseClass, factoredClass, keyType, keyValue) NLMISC::CFactoryRegister<NLMISC::CFactory<baseClass, keyType>, baseClass, factoredClass, keyType> Register##factoredClass(keyValue)
#define NLMISC_GET_FACTORY(baseClass, keyType) NLMISC::CFactory<baseClass, keyType>::instance()
/** Interface class for object registered in the indirect factory.
* This is for indirect factory internal use, you should not need to use this class directly.
*/
template <class BaseFactoryClass>
class IFactoryIndirectRegister
{
public:
virtual ~IFactoryIndirectRegister() {}
/** Return the factory implementation.*/
virtual BaseFactoryClass *getFactory() = 0;
};
/** Indirect factory implementation class.
* the class take 2 template argument :
* * BaseFactoryClass : the common base class for all factory object of one indirect factory.
* * KeyType : the type of the key that identify the factorable object (string by default).
*
* The indirect factory conforms to singleton design pattern.
*
* In indirect factory, the object returned by the factory are not instance of factored object
* but instance of 'sub' factory that do the real job.
* This can be useful in some case, like adapting existing code into a factory
* or having a complex constructor (or more than one constructor).
*/
template <class BaseFactoryClass, class KeyType = std::string>
class CFactoryIndirect
{
typedef std::map<KeyType, IFactoryIndirectRegister<BaseFactoryClass>*> TRegisterCont;
public:
/// Get the singleton instance reference.
static CFactoryIndirect &instance()
{
static CFactoryIndirect *instance = NULL;
if (!instance)
{
instance = new CFactoryIndirect();
}
return *instance;
}
void registerClass(const KeyType &key, IFactoryIndirectRegister<BaseFactoryClass> *factoryRegister)
{
nlassert(_FactoryRegisters.find(key) == _FactoryRegisters.end());
_FactoryRegisters.insert(std::make_pair(key, factoryRegister));
}
BaseFactoryClass *getFactory(const KeyType &key)
{
typename TRegisterCont::const_iterator it (_FactoryRegisters.find(key));
if (it == _FactoryRegisters.end())
return NULL;
else
return it->second->getFactory();
}
// Add some introspection
void fillFactoryList(std::vector<KeyType> &classList)
{
typename TRegisterCont::iterator first(_FactoryRegisters.begin()), last(_FactoryRegisters.end());
for (; first != last; ++first)
{
classList.push_back(first->first);
}
}
protected:
// static CFactoryIndirect *_Instance;
TRegisterCont _FactoryRegisters;
};
template <class IndirectFactoryClass, class BaseFactoryClass, class SpecializedFactoryClass, class KeyType>
class CFactoryIndirectRegister : public IFactoryIndirectRegister<BaseFactoryClass>
{
SpecializedFactoryClass _FactoryClass;
public:
CFactoryIndirectRegister(const KeyType &key)
{
IndirectFactoryClass::instance().registerClass(key, this);
}
BaseFactoryClass *getFactory()
{
return &_FactoryClass;
}
};
//#define NLMISC_IMPLEMENT_FACTORY_INDIRECT(baseFactoryClass, keyType) NLMISC::CFactoryIndirect<baseFactoryClass, keyType> *NLMISC::CFactoryIndirect<baseFactoryClass, keyType>::_Instance = NULL
#define NLMISC_GET_FACTORY_INDIRECT_REGISTRY(baseFactoryClass, keyType) NLMISC::CFactoryIndirect<baseFactoryClass, keyType>::getInstance()
#define NLMISC_REGISTER_OBJECT_INDIRECT(baseFactoryClass, specializedFactoryClass, keyType, keyValue) NLMISC::CFactoryIndirectRegister<NLMISC::CFactoryIndirect<baseFactoryClass, keyType>, baseFactoryClass, specializedFactoryClass, keyType> RegisterIndirect##specializedFactoryClass(keyValue)
#define NLMISC_DECLARE_OBJECT_INDIRECT(baseFactoryClass, specializedFactoryClass, keyType) extern NLMISC::CFactoryIndirectRegister<NLMISC::CFactoryIndirect<baseFactoryClass, keyType>, baseFactoryClass, specializedFactoryClass, keyType> RegisterIndirect##specializedFactoryClass
#define NLMISC_GET_FACTORY_INDIRECT(specializedFactoryClass) RegisterIndirect##specializedFactoryClass.getFactory()
} // namespace NLMISC
#endif // FACTORY_H