C ++: a general factory that any constructor can call?

I am trying to write a factory class that will have a standard interface that looks like this:

Register<MyBase, MyDerived> g_regDerived("myderived"); // register to factory 

now called:

 auto* d = Factory<MyBase>::instance().create("myderived", 1, 2, 3); 

will call the constructor MyDerived(1,2,3) and return a pointer to the created object

This is similar to what should be possible with C ++ 11, but I could not figure out how to do this.
Starting from the standard erasure factory type:

 template<typename BaseT> class Factory { public: static Factory* instance() { static Factory inst; return &inst; } template<typename T> void reg(const string& name) { m_stock[name].reset(new Creator<T>); } BaseT* create(const string& name) { return m_stock[name]->create(); } private: struct ICreator { virtual BaseT* create() = 0; }; template<typename T> struct Creator : public ICreator { virtual BaseT* create() { return new T; } }; std::map<string, std::unique_ptr<ICreator>> m_stock; }; template<typename BaseT, typename T> class Register { public: Register(const QString& name) { Factory<BaseT>::instance()->reg<T>(name); } }; 

The problem is that as soon as you delete the type of the created object, you can no longer pass arbitrary template arguments, since you need to pass them through a virtual function.

The answer to this question is:
How to pass a function pointer pointing to a constructor?
says something similar, but the answer is to go through a function that is specific to each derived class. I want to use the class constructor directly and not write the create() function.

+6
source share
1 answer

I don't know why your aversion to writing the create() function. So here is what I implemented.

 #include <iostream> #include <utility> using namespace std; class C { public: virtual char const* whoAmI() const = 0; }; class A : public C { public: A(int a1) { cout << "A(" << a1 << ")" << endl; } A(float a1) { cout << "A(" << a1 << ")" << endl; } virtual char const* whoAmI() const override { return "A"; } }; class B : public C { public: B(int a1) { cout << "B(" << a1 << ")" << endl; } B(float a1) { cout << "B(" << a1 << ")" << endl; } virtual char const* whoAmI() const override { return "B"; } }; template<typename BASET> class Factory { public: // could use a is_base type trait test here template <typename T, typename...ARGs> static BASET* create(ARGs&&...args) { return new T(forward<ARGs>(args)...); } }; int main() { Factory<C> factory; C* a = factory.create<A>(1); C* b = factory.create<B>(1.0f); cout << a->whoAmI() << endl; cout << b->whoAmI() << endl; return 0; } 

NOTE. I did not do everything you have, I just implemented the create function. I will leave the final implementation to you.

This uses perfect forwarding to allow the varidict template to pass any number of parameters to the constructor. The registration function may then store a pointer to a function for a particular instance of the template for a specific set of parameters.

EDIT

I forgot to use the appropriate call forward<ARGs>(args)... to implement perfect forwarding. He is now added.

Do you think this is not useful, here is a complete implementation of your factory using advanced redirection and varidict patterns that allow a certain number of parameters of specific types for a specific factory instance:

 #include <string> #include <map> #include <memory> #include <utility> #include <iostream> using namespace std; template<typename BaseT, typename...ARGs> class Factory { public: static Factory* instance() { static Factory inst; return &inst; } template<typename T> void reg(const string& name) { m_stock[name].reset(new Creator<T>); } BaseT* create(const string& name, ARGs&&...args) { return m_stock[name]->create(forward<ARGs>(args)...); } private: struct ICreator { virtual BaseT* create(ARGs&&...) = 0; }; template<typename T> struct Creator : public ICreator { virtual BaseT* create(ARGs&&...args) override { return new T(forward<ARGs>(args)...); } }; std::map<string, std::unique_ptr<ICreator>> m_stock; }; template<typename BaseT, typename T, typename...ARGs> class Register { public: Register(const string& name) { auto instance = Factory<BaseT, ARGs...>::instance(); instance->template reg<T>(name); } }; struct C { virtual char const * whoAmI() const = 0; }; struct A : public C { A(int a1, int a2) { cout << "Creating A(" << a1 << ", " << a2 << ")" << endl; } virtual char const * whoAmI() const override { return "A"; } }; struct B : public C { B(int b1, int b2) { cout << "Creating B(" << b1 << ", " << b2 << ")" << endl; } B(int b1, int b2, int b3) { cout << "Creating B(" << b1 << ", " << b2 << ", " << b3 << ")" << endl; } virtual char const * whoAmI() const override { return "B"; } }; typedef int I; Register<C, A, I, I> a("a"); Register<C, B, I, I> b("b"); Register<C, B, I, I, I> b3("b"); int main() { C* a = Factory<C, I, I>::instance()->create("a", 1, 2); C* b = Factory<C, I, I>::instance()->create("b", 3, 4); C* b3 = Factory<C, I, I, I>::instance()->create("b", 5, 6, 7); cout << "I am a " << a->whoAmI() << endl; cout << "I am a " << b->whoAmI() << endl; cout << "I am a " << b3->whoAmI() << endl; return 0; } 

Is this what you want? If you do not want to deal with function parameters, use the helper function of the template to display them for you like this:

 template <typename BaseT, typename...ARGs> BaseT* create(const string& name, ARGs&&...args) { return Factory<C, ARGs...>::instance()->create(name, forward<ARGs>(args)...); } int main() { C* a = create<C>("a", 1, 2); C* b = create<C>("b", 3, 4); C* b3 = create<C>("b", 3, 4, 5); cout << "I am a " << a->whoAmI() << endl; cout << "I am a " << b->whoAmI() << endl; cout << "I am a " << b3->whoAmI() << endl; return 0; } 

Which has an additional bonus that allows you to get multiple constructor signatures through the seemingly single-function API (it looks like only one, but actually N, where N is the number of different signatures that you allow). All of this can be viewed with this online demo .

You still have to use the same registration as before, but which can be shortened with a macro.

If this is not what you want, add additional details to your question.

+6
source

Source: https://habr.com/ru/post/984005/


All Articles