/*
	Author: Marco Costalba (C) 2007


   This is a simple but powerful object factory.
*/
#ifndef SIMPLE_FACTORY_HPP
#define SIMPLE_FACTORY_HPP

#include <map> #include <string> #include <boost/type_traits.hpp> #include <boost/function.hpp> #include <boost/fusion/sequence/container/vector.hpp> #include <boost/fusion/functional/invocation/invoke_function_object.hpp> /* To get an object of a given class from the factory you need first to register the class against the factory, passing a class identifier. Interface is defined by these static methods bool Factory<Base>:: register_class<Derived* (arg1, ... ,argN)>(KeyRef key, FnPtr = NULL); void Factory<Base>::unregister<Derived* (arg1, ... , argN)>(str_ref ident); Base* Factory<Base>::object(str_ref ident, arg1 a1, ... , argN aN) Where: - 'Base' is the base class, there is one factory each base class - 'Derived' is a class that inherits 'Base' - 'arg1', ...'argN' are the types of the argument passed to object constructor, if not supplied then default object constructor will be called. - 'KeyRef' is a const string reference used to query the factory for objects, a common choice for 'key' value is the name of the class. - 'FnPtr' it's a pointer to an user custom function or a functor (a function object) that, when supplied, will be used instead of the default one to actually create the object. Default one is defined as: Derived* def_ctor(Arg1 arg1, Arg2 arg2, ..., argN) { return new Derived(arg1, arg2,..., argN); } And now the implementation... */ namespace simple_factory { namespace detail { typedef std::string Key; // using std::string as key type typedef const Key& KeyRef; template<typename D, typename Signature, int n> struct ArgNumTraits; /********************* ARGUMENT NUMBER DEPENDANT *********************/ template<typename D, typename Signature> struct ArgNumTraits<D, Signature, 0> { typedef boost::fusion::vector<> Args; typedef D*(*DefFunType)(); typedef typename boost::function<D*()> Functor; static D* defFun() { return new D(); } }; template<typename D, typename Signature> struct ArgNumTraits<D, Signature, 1> { typedef typename boost::function_traits<Signature>::arg1_type P1; typedef boost::fusion::vector<P1> Args; typedef D*(*DefFunType)(P1); typedef typename boost::function<D*(P1)> Functor; static D* defFun(P1 a1) { return new D(a1); } }; template<typename D, typename Signature> struct ArgNumTraits<D, Signature, 2> { typedef typename boost::function_traits<Signature>::arg1_type P1; typedef typename boost::function_traits<Signature>::arg2_type P2; typedef boost::fusion::vector<P1, P2> Args; typedef D*(*DefFunType)(P1, P2); typedef typename boost::function<D*(P1, P2)> Functor; static D* defFun(P1 a1, P2 a2) { return new D(a1, a2); } }; /********************* END OF ARGUMENT NUMBER DEPENDANT *********************/ template<typename Signature> struct CtorTraits { typedef boost::function_traits<Signature> FT; typedef typename boost::remove_pointer<typename FT::result_type>::type Derived; typedef ArgNumTraits<Derived, Signature, FT::arity> AT; typedef typename AT::DefFunType DefFunType; typedef typename AT::Functor Functor; typedef typename AT::Args Args; }; template<class Base> class Factory_p { // singleton class, one each base class Factory_p() {} Factory_p(const Factory_p&); Factory_p& operator=(const Factory_p&); ~Factory_p() { for(ConstIter it = creators.begin(); it != creators.end(); ++it) delete (*it).second; } /* 'StorableType' is the common base class to store pointers * to different type of creators, dynamic_cast<> at runtime * will be used to check the (hidden) derived type */ struct StorableClass { virtual ~StorableClass(){} }; typedef std::multimap<Key, const StorableClass*> Container; typedef typename Container::iterator Iter; typedef typename Container::const_iterator ConstIter; Container creators; /* 'SignatureClass' is subclassed from StorableType and identifies * an unique argument list type (without return value). Used by * runtime argument checking */ template<typename Args> struct SignatureClass : public StorableClass { virtual Base* operator()(Args&) const = 0; }; /* 'CreatorClass' is subclassed from 'SignatureClass' and stores the * functor object used to create the requested object, * will be called due to dynamic polymorphism */ template<typename Args, typename FnType> struct CreatorClass: public SignatureClass<Args> { typedef typename FnType::result_type result_type; CreatorClass(const FnType& fn) : functor(fn) {} virtual result_type operator()(Args& v) const { return boost::fusion::invoke_function_object(functor, v); } FnType functor; }; // Some helpers that deal with 'SignatureClass' objects template<typename Args> const SignatureClass<Args>* checkType(const StorableClass* p) const { // check c'tor argument type at runtime return dynamic_cast<const SignatureClass<Args>*>(p); } template<typename Args> const SignatureClass<Args>* find(KeyRef nm) const { std::pair<ConstIter, ConstIter> r = creators.equal_range(nm); for (ConstIter it = r.first; it != r.second; ++it) { const SignatureClass<Args>* p = this->checkType<Args>((*it).second); if (p) return p; } return NULL; } protected: // only Factory can use Factory_p static Factory_p& instance() { static Factory_p p_instance; return p_instance; } public: /* These methods can be called only from Factory_p::instance(), * no problem in declaring 'public' because only Factory * can access Factory_p<Base>::instance() */ template<typename Values> Base* doObject(KeyRef nm, Values& v) const { const SignatureClass<Values>* p = this->find<Values>(nm); return (p ? (*p)(v) : NULL); } template<typename Signature, typename FnType> bool doRegister(KeyRef nm, const FnType& fn) { typedef typename CtorTraits<Signature>::Args Args; if (this->find<Args>(nm)) return false; creators.insert(std::make_pair(nm, new CreatorClass<Args, FnType>(fn))); return true; } template<typename Signature> bool doUnregister(KeyRef nm) { typedef typename CtorTraits<Signature>::Args Args; std::pair<Iter, Iter> r = creators.equal_range(nm); for (Iter it = r.first; it != r.second; ++it) { if (this->checkType<Args>((*it).second)) { delete (*it).second; creators.erase(it); return true; // can be only one } } return false; } }; } // namespace detail template<class Base> class Factory : private detail::Factory_p<Base> { Factory(); // prevent instantation public: template<typename Signature> static bool register_class(detail::KeyRef nm) { typedef typename detail::CtorTraits<Signature>::DefFunType FnType; return Factory<Base>:: template register_class<Signature, FnType>(nm, NULL); } template<typename Signature, typename FnType> static bool register_class(detail::KeyRef nm, FnType fn) { typedef detail::CtorTraits<Signature> CT; typedef typename CT::Functor Fun; Fun fun(*fn); if (!fun) fun = CT::AT::defFun; return detail::Factory_p<Base>::instance(). template doRegister<Signature, Fun>(nm, fun); } static bool unregister(detail::KeyRef nm) { return Factory<Base>::template unregister<Base*()>(nm); } template<typename Signature> static bool unregister(detail::KeyRef nm) { return detail::Factory_p<Base>::instance(). template doUnregister<Signature>(nm); } /********************* ARGUMENT NUMBER DEPENDANT *********************/ /* overloads to get the object, these are argument dependent because * function templates does not support template default arguments */ static Base* object(detail::KeyRef nm) { typedef boost::fusion::vector<> values; values v; return detail::Factory_p<Base>::instance(). template doObject<values>(nm, v); } template<typename T1> static Base* object(detail::KeyRef nm, T1 a1) { typedef boost::fusion::vector<T1> values; values v(a1); return detail::Factory_p<Base>::instance(). template doObject<values>(nm, v); } template<typename T1, typename T2> static Base* object(detail::KeyRef nm, T1 a1, T2 a2) { typedef boost::fusion::vector<T1, T2> values; values v(a1, a2); return detail::Factory_p<Base>::instance(). template doObject<values>(nm, v); } template<typename T1, typename T2, typename T3> static Base* object(detail::KeyRef nm, T1 a1, T2 a2, T3 a3) { typedef boost::fusion::vector<T1, T2, T3> values; values v(a1, a2, a3); return detail::Factory_p<Base>::instance(). template doObject<values>(nm, v); } /********************* END OF ARGUMENT NUMBER DEPENDANT *********************/ }; } // namespace simple_factory #endif