Problems with the generic factory method and variation patterns

I want to create something like a generic factory method - look at this:

template <class BaseType> class Factory { public: template <class ... Args> static BaseType* Create(const Args& ... args) { return new DerivedType(args ...); } }; 

Where DerivedType is another type derived from BaseType and defined elsewhere.

The problem is saving DerivedType . I want to do this, for example, as follows:

 void f() { // Derived type may have more than one constructor, // that why I suggest using of the variadic templates. BaseType* ptr1 = Factory<BaseType>::Create("abc", 5, 10.); BaseType* ptr2 = Factory<BaseType>::Create(); ... } ... Factory<BaseType>::SetType<MyDerivedType>(); f(); Factory<BaseType>::SetType<YourDerivedType>(); f(); 

I can set different derived types, but they are all known at compile time. I can’t come up with a suitable technique for this.

Question: Can you advise one?


The rationale for this (thus the original problem, if someone suggests the question of whether this is an XY problem) is the ability to unit test some complex parts of the code. For example, if I have a code:

 ... Shuttle* shuttle1 = new ShuttleImpl("Discovery", Destination::Moon); Shuttle* shuttle2 = new ShuttleImpl(); ... 

And I don’t want to really build the shuttle every time I run unit tests:

 class Shuttle: public Factory<Shuttle> { ... } ... Shuttle* shuttle1 = Shuttle::Create("Discovery", Destination::Moon); Shuttle* shuttle2 = Shuttle::Create(); ... 

So, in the unit test, I can just do: Shuttle::SetType<TestShuttle>(); .

There may be more “testable” classes, so for them I need a universal factory:

 class Car: public Factory<Car> { ... } class Driver: public Factory<Driver> { ... } ... 
+6
source share
2 answers

Not the complete answer, but your Create template template template should be:

 template <class BaseType> class Factory { public: template <class... Args> static BaseType* Create(Args&&... args) { return new DerivedType(std::forward<Args>(args)...); } }; 

See also When to use std :: forward to forward arguments?


Edit:

Why doesn't the second argument of the template solve your problem?

For instance:

 template <class Base, class Derived> class Factory { public: template <class... Args> static Base* Create(Args&&... args) { return new Derived(std::forward<Args>(args)...); } }; 

Instead

 Factory<BaseType>::SetType<MyDerivedType>(); f(); Factory<BaseType>::SetType<YourDerivedType>(); f(); 

You can write:

 Factory<MyBase, MyDerived1> factory1; Factory<MyBase, MyDerived2> factory2; auto object1 = factory1::Create(1, "a"); auto object2 = factory2::Create(1.2, "abc"); 
0
source

IF your factory knows all the possible derived classes, the following may help:

 // get_index<T, T1, .., TK-1, T, Ts...> is std::integral_constant<std::size_t, K> template <typename T, typename ... Ts> struct get_index; template <typename T, typename ... Ts> struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {}; template <typename T, typename Tail, typename ... Ts> struct get_index<T, Tail, Ts...> : std::integral_constant < std::size_t, 1 + get_index<T, Ts...>::value > {}; template <typename Base, typename...Deriveds> struct Factory { private: template <typename Derivated, typename...Ts> static constexpr Base* allocator(Ts&&...args) { return new Derivated(std::forward<Ts>(args)...); } template <typename...Ts> static constexpr std::array<Base*(*)(Ts&&...), sizeof...(Deriveds)> array_alloc() { return std::array<Base*(*)(Ts&&...), sizeof...(Deriveds)> {{ &allocator<Deriveds, Ts&&...>... }}; } public: template <typename...Ts> static Base* create(Ts&&...args) { return array_alloc<Ts...>()[active](std::forward<Ts>(args)...); } template <typename Derived> static void setType() { active = get_index<Derived, Deriveds...>::value; } private: static std::size_t active; }; template <typename Base, typename...Deriveds> std::size_t Factory<Base, Deriveds...>::active = 0; 

And use it like:

 class Base {}; struct D1 : Base { D1() {std::cout << "D1" << std::endl;} D1(int a, int b) {} }; struct D2 : Base { D2() {} D2(int a, int b) { std::cout << "D2(" << a << ", " << b << ")" << std::endl; } }; int main(int argc, char *argv[]) { typedef Factory<Base, D1, D2> BaseFactory; // default to D1 Base* b1 = BaseFactory::create(); // D1() BaseFactory::setType<D2>(); Base* b2 = BaseFactory::create(42, 53); // D2(42, 53) delete b2; delete b1; return 0; } 
0
source

All Articles