Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with the universal factory method and the variadic templates

I want to create something like an universal factory method - look at this one:

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

Where the DerivedType is some other type derived from BaseType and defined in a different place.

The problem is with storing DerivedType. I want to do it, for example, like this:

void f() {
  // Derived type may have more than one constructor,
  // that's 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 all of them are known at compile-time. I can't think of an appropriate technique to do this.

Question: Can you advise one?


The rationale of doing this (thus, the original problem, if someone suggests the question is it-self the XY problem) - is an ability to unit-test some tricky parts of 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 construct the shuttle each 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, that's why I need an universal factory for all of them:

class Car: public Factory<Car> { ... }
class Driver: public Factory<Driver> { ... }
...
like image 570
abyss.7 Avatar asked Jan 28 '14 07:01

abyss.7


1 Answers

Not a complete answer, but your class template's Create static function 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 does a second template argument not solve your problem?

For example:

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

Instead of

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

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

you may write:

Factory<MyBase, MyDerived1> factory1;
Factory<MyBase, MyDerived2> factory2;

auto object1 = factory1::Create(1, "a");
auto object2 = factory2::Create(1.2, "abc");
like image 170
CouchDeveloper Avatar answered Oct 23 '22 19:10

CouchDeveloper