Search code examples
c++api-design

Using a macro to replace "create()" functions in a factory pattern style API


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...)


Solution

  • 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__)