How to implement a generic Factory in C++14 that supports template covariance?
I want to achieve something like this:
std::shared_ptr<Factory<BaseClass>> factory =
std::make_shared<Factory<DerivedClass>>();
auto x = factory->create(arg1, arg2, arg3);
Note that in factory->create
, you can pass any arguments to DerivedClass
constructor. It is okay to assume that the BaseClass
constructor and the DerivedClass
are identical.
To avoid the XY Problem, the reason I need this is because I want to use dependency injection (boost::di) to achieve maximum testability.
For example, if there's a class A
that creates Socket
instances, I want it to depend on a Factory<ISocket>
service. In the real code, I'd inject Factory<Socket>
, and in the testing code, I'd inject Factory<Mock<ISocket>>
, so I can test the A
class without actually creating a real socket.
This is my current attempt:
template <typename T>
struct BaseFactory {
virtual std::unique_ptr<T> create() = 0;
};
template <typename TInterface, typename TImplementation>
struct Factory : public BaseFactory<TInterface> {
virtual std::unique_ptr<TInterface> create() override {
return std::make_unique<TImplementation>();
}
};
The current usage is something like:
std::shared_ptr<BaseFactory<ISocket>> factory =
std::make_shared<Factory<ISocket, Socket>>();
auto x = factory->create();
Although not ideal (you need to specify the base class in Factory
), this usage is fine for me and it works.
The next thing I need to add is support for constructor arguments. I've tried to add variadic template to create
:
template <typename ...TArgs>
virtual std::unique_ptr<T> create() = 0;
... but it looks like you can't have virtual methods with templates.
Thank you!
OK, I found one solution, but it's not pretty:
template <typename T, typename ...TArgs>
struct BaseFactory {
virtual std::unique_ptr<T> create(TArgs&&... args) = 0;
};
template <typename TInterface, typename TImplementation, typename ...TArgs>
struct Factory : public BaseFactory<TInterface, TArgs...> {
virtual std::unique_ptr<TInterface> create(TArgs&&... args) override {
return std::make_unique<TImplementation>(std::forward<TArgs>(args)...);
}
};
using ISocketFactory = BaseFactory<ISocket, int>;
using SocketFactory = Factory<ISocket, Socket, int>;
int main() {
std::shared_ptr<ISocketFactory> socket_factory =
std::make_shared<SocketFactory>();
std::unique_ptr<ISocket> socket = socket_factory->create(1234);
socket->read();
socket->write();
}
The idea is to pass the implementation class' constructor arguments in the BaseFactory
and Factory
templates. In this case, the Socket
constructor should look something like:
Socket(int n);
Do you have any idea how to optimize this? (less boilerplate code)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With