There are a couple of ways to solve the problem. First, you can use typed expression in the macro so you can parse the type. So the CREATE_OBJECT_IMPL
would be called like this:
CREATE_OBJECT_IMPL(Object, (Arg1) arg1, (Arg2) arg2)
Here are some macros that will retrieve the type and strip off the type:
#define EAT(x)
#define REM(x) x
#define STRIP(x) EAT x
#define PAIR(x) REM x
These macros work like this. When you write STRIP((Arg1) arg1)
it will expand to arg1
. And when you write PAIR((Arg1) arg1)
it will expand to Arg1 arg1
. Now next, you will want to do is to apply these macros to each argument that is passed in, so here is a simple APPLY
macro that will let you do that for up to 8 arguments:
/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT(x, y) PRIMITIVE_CAT(x, y)
/* This will call a macro on each argument passed in */
#define APPLY(macro, ...) CAT(APPLY_, NARGS(__VA_ARGS__))(macro, __VA_ARGS__)
#define APPLY_1(m, x1) m(x1)
#define APPLY_2(m, x1, x2) m(x1), m(x2)
#define APPLY_3(m, x1, x2, x3) m(x1), m(x2), m(x3)
#define APPLY_4(m, x1, x2, x3, x4) m(x1), m(x2), m(x3), m(x4)
#define APPLY_5(m, x1, x2, x3, x4, x5) m(x1), m(x2), m(x3), m(x4), m(x5)
#define APPLY_6(m, x1, x2, x3, x4, x5, x6) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6)
#define APPLY_7(m, x1, x2, x3, x4, x5, x6, x7) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7)
#define APPLY_8(m, x1, x2, x3, x4, x5, x6, x7, x8) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8)
Then you can define the CREATE_OBJECT_IMPL
like this:
#define CREATE_OBJECT_IMPL(ObjectType, ...)
ObjectType* Create##ObjectType(APPLY(PAIR, __VA_ARGS__))
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, APPLY(STRIP, __VA_ARGS__));
[...]
return object;
}
Of course, you might need some workarounds for these macros, if you use them on visual studio. Of course, a better solution is to write a templated function. So you would call your CreateObject
like this:
ObjectType* obj = CreateObject<ObjectType>(arg1, arg2, arg3);
In C++11, you can use varidiac templates like this:
template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args... args)
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, args...);
[...]
return object;
}
But if your compiler doesn't support varidiac templates, you can use the Boost.PP to generate overloads for up to 10 arguments(or more if you need to):
#define GENERATE_OBJS_EACH(z, n, data)
template<class ObjectType, BOOST_PP_ENUM_PARAMS_Z(z, n, class Arg)>
ObjectType* CreateObject(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, Arg, arg))
{
ObjectType* object = new ObjectType();
[...]
object->Init(this, BOOST_PP_ENUM_PARAMS_Z(z, n, arg));
[...]
return object;
}
/* Generate CreateObject template for up to 10 arguments */
BOOST_PP_REPEAT_FROM_TO_1(1, 10, GENERATE_OBJS_EACH, ~)
Edit: Heres are the workarounds you would need to get the above macros to work in msvc:
/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS_MSVC_WORKAROUND(x) NARGS_SEQ x
#define NARGS(...) NARGS_MSVC_WORKAROUND((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1))
/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT_MSVC_WORKAROUND(x) PRIMITIVE_CAT x
#define CAT(x, y) CAT_MSVC_WORKAROUND((x, y))
/* This will call a macro on each argument passed in */
#define APPLY(macro, ...) APPLY_MSVC_WORKAROUND(CAT(APPLY_, NARGS(__VA_ARGS__)), (macro, __VA_ARGS__))
#define APPLY_MSVC_WORKAROUND(m, x) m x
...