C++ Perfect Generic Abstract Factory with arbitrary Constructor Arguments

  abstract-factory, c++

for unit testing, I’m trying to create a Factory which meets the following requirements:

  • (1) It can create arbitrary objects (e.g. Timer and TimerMock)
  • (2) It returns unique_ptrs to a Base class to these objects (e.g. unique_ptr<TimerInterface>)
  • (3) The Factory itself can be passed around as a base class pointer too
  • (4) It shall be possible to call any constructor to create the Object

The purpose is to do dependency injection with this factories to be able to exchange objects that are not part of the test with mock objects.

Here’s what I have so far:

#include <memory>
#include <iostream>

//"perfect factory method"
//usage: std::unique_ptr<Type> pt(create<Type>(Type-constructor-arguments));
template <typename Type, typename ... ConstructorArgs>
auto create(ConstructorArgs&& ... args){
    return std::make_unique<Type>(std::forward<ConstructorArgs>(args)...);
}

//Abstract Factory Base class
template<typename BaseType, typename ... ConstructorArgs>
class IFactory {
    public:
        virtual ~IFactory() = default;
        virtual std::unique_ptr<BaseType> create(ConstructorArgs&& ... args) const = 0;
};

//Abstract Factory class
template <typename BaseType, typename DerivedType, typename ... ConstructorArgs>
class CFactory : public IFactory<BaseType, ConstructorArgs...>
{
    public:
        using Base = IFactory<BaseType, ConstructorArgs...>;
        std::unique_ptr<BaseType> create(ConstructorArgs&& ... args) const override
        {
            return ::create<DerivedType>(std::forward<ConstructorArgs>(args)...);
        }
};

How the real Factory classes are defined:

class TimerInterface {
    public:
        TimerInterface() = default;
        TimerInterface (const char* name);
        virtual void whoami() const = 0;
        /*...*/
};

class Timer: public TimerInterface {
    public:
        Timer() = default;
        Timer(const char* name) : TimerInterface (name) {}
        void whoami() const override { std::cerr << "I'm real!" << std::endl; }
        /*...*/
};


class TimerMock : public TimerInterface {
    public:
        TimerMock () = default;
        TimerMock (const char* name) : TimerInterface (name) {}
        void whoami() const override { std::cerr << "I fake it!" << std::endl; }
        /*...*/
};

using TimerFactory = CFactory<TimerInterface, Timer, const char*>;
using TimerMockFactory = CFactory<TimerInterface, TimerMock, const char*>;

using TimerFactoryInterface = TimerFactory::Base;

And how they are intended to be used:

class ClassUnderTest {
    public:
        std::unique_ptr<TimerInterface> timer {};
        std::unique_ptr<TimerInterface> timer2 {};
    
        ClassUnderTest(const TimerFactoryInterface& factory)
        : timer(factory.create("I got a name!"))
        //, timer2(factory.create())
        {}
};

class Production
{
    public:
        ClassUnderTest realUsage;
        
        Production() :
        realUsage(TimerFactory())
        {}
};

class Test
{
    public:
        ClassUnderTest tested;
        
        Test() :
        tested(TimerMockFactory())
        {}  
};

int main()
{
    Production p;
    p.realUsage.timer->whoami();
    
    Test t;
    t.tested.timer->whoami();
}

My big problem is requirement (4)
ClassUnderTest::timer2 can’t be created with the same factory that’s used for ClassUnderTest::timer, beause the constructor signatur already needs to be known when defining the CFactory class.

Anyone got an idea?

P.S.: "It can’t be done" with an explanation is also an acceptable answer, but not my favourite 😉

Source: Windows Questions C++

LEAVE A COMMENT