C ++ Factory using template variables

I just tried something - I wanted to create a shared factory that will return shared_ptr to the type.

I have a derived class that uses static methods to return shared_ptr to the base class. The idea is that I would like to be able to register these methods using a common factory, but it cannot determine which method is being registered at compile time. There may be a way to achieve this using SFINAE, but I'm just starting to understand its complexities.

Sorry for the rather long sample code also available at http://coliru.stacked-crooked.com/a/331e08de86004592

Including more than one factory method in DerivedA will result in a compilation error.

 #include <iostream> #include <string> #include <vector> #include <unordered_map> #include <memory> // Factory which returns a shared_ptr of type T. template<class T, class Tag, class... Args> class NameFactory { public: typedef std::function<std::shared_ptr<T>(Args...)> Function; static NameFactory& instance(); void registerType(const std::string& type, const Function& createFunction); std::shared_ptr<T> createObject(const std::string& type, Args&&... arguments); private: NameFactory() {} std::unordered_map<std::string, Function> m_functionMap; }; template<class T, class Tag, class... Args> NameFactory<T, Tag, Args...>& NameFactory<T, Tag, Args...>::instance() { static NameFactory<T, Tag, Args...> m_instance; return m_instance; } template<class T, class Tag, class... Args> void NameFactory<T, Tag, Args...>::registerType(const std::string& type, const Function& createFunction) { m_functionMap[type] = createFunction; } template<class T, class Tag, class... Args> std::shared_ptr<T> NameFactory<T, Tag, Args...>::createObject(const std::string& type, Args&&... arguments) { auto iter(m_functionMap.find(type)); if (iter != m_functionMap.end()) { return (iter->second)(std::forward<Args>(arguments)...); } throw std::logic_error("Cannot find constructor for type '" + type + "'"); } template<class T, class Tag, class... Args> class NameFactoryRegistration { public: typedef NameFactory<T, Tag, Args...> Factory; NameFactoryRegistration(const std::string& type, const typename Factory::Function& createFunction) { Factory::instance().registerType(type, createFunction); } private: }; class MyBase { public: typedef std::shared_ptr<MyBase> SPtr; }; class DerivedA : public MyBase { public: static SPtr create() { return SPtr(new DerivedA); } // Enabling this factory method (and/or the two args method below causes an 'unresolved overloaded function type' error //static SPtr create(const std::string& s) //{ // return SPtr(new DerivedA(s)); //} //static SPtr create(const std::string& s, double d) //{ // return SPtr(new DerivedA(s,d)); //} private: DerivedA() { std::cout << "DerivedA - no args" << std::endl; } DerivedA(const std::string& s) { std::cout << "DerivedA - one arg: " << s << std::endl; } DerivedA(const std::string& s, double d) { std::cout << "DerivedA - two args: " << s << " : " << d << std::endl; } }; // Tags to help differentiate the factories struct NoArgsReg; struct SingleArgReg; struct TwoArgReg; typedef NameFactory<MyBase, NoArgsReg> NoArgsFactory; typedef NameFactoryRegistration<MyBase, NoArgsReg> NoArgsRegistration; typedef NameFactory<MyBase, SingleArgReg, const std::string&> SingleArgFactory; typedef NameFactoryRegistration<MyBase, SingleArgReg, const std::string&> SingleArgRegistration; typedef NameFactory<MyBase, TwoArgReg, const std::string&, double> TwoArgsFactory; typedef NameFactoryRegistration<MyBase, TwoArgReg, const std::string&, double> TwoArgsRegistration; // Register the factory methods into the NameFactory NoArgsRegistration dAReg0("A", DerivedA::create); //SingleArgRegistration dAReg1("A", DerivedA::create); //TwoArgsRegistration dAReg2("A", DerivedA::create); int main() { auto object0(NoArgsFactory::instance().createObject("A")); // Not registered, //auto object1(SingleArgFactory::instance().createObject("A","testString")); //auto object2(TwoArgsFactory::instance().createObject("A","testString",3.142)); return 0; } 
+6
source share
2 answers

The problem is that you cannot infer a type in an overload set. Even if we simplify the example to what we could try using SFINAE, we are stuck:

 #include <functional> struct A { static void create() { } static void create(int ) { } }; template <typename F, typename = decltype(std::declval<F>()(std::declval<int>()))> void foo(F ) { } int main() { foo(&A::create); // error, even in this case } 

You will need to add explicit overloads for function pointers to handle this case, since there is an exception in the standard:

 void foo(void (*)(int)) { } // (1) template <typename F, typename = decltype(std::declval<F>()(std::declval<int>()))> void foo(F ) { } // (2) int main() { foo(&A::create); // OK, calls (1) } 

In your specific example, this means adding two constructors:

 // in Factory using Function = std::function<std::shared_ptr<T>(Args...)>; using FunctionPtr = std::shared_ptr<T>(*)(Args...); // in Registration using Function = typename Factory::Function; using FunctionPtr = typename Factory::FunctionPtr; NameFactoryRegistration(const std::string& type, const Function& createFunction) { /* same as before */ } NameFactoryRegistration(const std::string& type, FunctionPtr createFunction) : NameFactoryRegistration(type, Function(createFunction)) { } 
+2
source

The problem is that (before C ++ 14) std::function<R(A...)> can be built from anything, and not just from what supports calling R(A...) . This should help if you add registerType overload, which will take the parameter R (&)(Args&&...) .

+3
source

All Articles