A very templated solution, use like this:
class A {virtual ~A() = 0;};
inline A::~A() = default;
class B : A {};
class C : A {};
class D : B {};
// Define the factory
DEFINE_FACTORY(A, B, C, D)
// Want the factory?
factory<A> a_factory;
A* ap = a_factory(1, 0, 1, 2, 3); // Creates a C, passing 0,1,2,3 to the ctor
delete ap;
// Or directly?
make<std::unique_ptr>(1,0,1,2,3); // Creates a C, passing 0,1,2,3 to the ctor
// Immediately destroyed because the `std::unique_ptr` was not saved.
And here the template-code making it possible:
#include <utility>
#include <memory>
#include <exception>
template<class I, class... X> struct factory_base {
template<class... ARGS> inline I* operator()(int i, ARGS&&... args) {
return make(i, std::forward<ARGS>(args)...);
}
template<class... ARGS> using maker_t = I*(*)(ARGS...);
template<class... ARGS> static I* make(int i, ARGS&&... args) {
constexpr maker_t<ARGS...> maker[] = {make_helper<X, ARGS...>()...};
if(i < sizeof...(X) && maker[i])
return maker[i](std::forward<ARGS>(args)...);
throw std::invalid_argument("The selected class cannot be constructed thus.");
}
template<class Y, class... ARGS> inline static constexpr maker_t<ARGS...>
make_helper(...) {return nullptr;}
template<class Y, class... ARGS> inline static constexpr auto make_helper()
-> decltype(new Y(std::declval<ARGS>()...), maker_t<ARGS...>(0))
{return static_cast<maker_t<ARGS...>>(
[](ARGS&&... args){return new Y(std::forward<ARGS>(args)...);});}
};
template<class I> class factory;
#define DEFINE_FACTORY(A, ...) template<> struct factory<A> : \
factory_base<A, __VA_ARG__> {}
template<class I, class... ARGS> std::unique_ptr<I> make(ARGS&&... args) {
return factory<I>()(std::forward<ARGS>(args)...);
}