I'm writing a C++ library with all classes making use of the factory pattern, i.e. having a private empty constructor and a private void init()
function, which does the actual initialization of an instance. The classes that can be instantiated by the library user have a public static create()
function which does the following:
static Class* create(int arg1, int arg2) {
Class* ret = new Class();
ret->init(arg1, arg2);
return ret;
}
There are also several classes which shall not be instantiated from outside the library, so they don't provide a public create()
.
These "internal classes" do have some friend
relationships between each other, so they can instantiate each other. Let's assume I want to write as little code as possible, so I would like to avoid declaring and defining private create()
functions for all the internal classes, but I would also like to avoid writing
InternalClass* foo = new InternalClass();
foo->init();
all the time an internal class gets instantiated. Instead it would be nice to have a single (template) function or macro that can create any internal class with just one line of code, e.g.
template<class T, typename ...Args> inline T* create(Args... args) {
T* ret = new T();
ret->init(args...);
return ret;
}
The problem with the above function (which would be defined as a standalone function in a global shared header, thus the inline
keyword) is that it bypasses the friend
declarations between the classes and thus wouldn't compile.
The problem with a macro on the other hand is that it can't "return" a pointer to the instance as long as there is the statement ret->init()
inside the macro, so using the macro like the following wouldn't be possible:
InternalClass* foo = CREATE(InternalClass, arg1, arg2);
Finally, my question is:
Is it somehow possible to create a macro that can be called in the above way (and without changing the return type of all the init()
functions to a class type pointer, which would indeed allow the following: #define CREATE(T, ...) (new T())->init(__VA_ARGS__)
)?
(Please note that this is more an academic than a practical question as I've already implemented the solution with the private create()
functions, which works perfectly fine and seems to be the "cleanest" solution. I was just wondering if there could have been a solution with macros, too...)
You can mix both template and macro:
template <typename T, typename InitF, typename... Args>
T* CreateImpl(T* t, InitF init, Args&&...args)
{
(t->*init)(std::forward<Args>(args)...);
return t;
}
and the MACRO (to forward private stuff)
// I assume no overloads of T::init
#define CREATE(T, ...) CreateImpl(new T(), &T::init, __VA_ARGS__)