Disclaimer 1:
Honestly, I don't advise you do such machinery behind macros. They will become a massive maintenance burden, and the return is minimal. If you can do this with other more maintainable abstractions, it would be better for you in the long run.
Disclaimer 2:
I am writing this without a compiler at the moment. I may have a syntax error, but these are the generic building blocks to make this solution.
That disclaimer said, this can be done -- but not nicely.
You have several problems here that need to be tackled with a lot of macro trickery:
- You want the expansion to contain the size of
__VA_ARGS__ / 2
(at expansion time)
- You want the expansion of
__VA_ARGS__
to produce Variant(<arg 1>), Variant(<arg 3>), Variant(<arg 5>), Variant()
- You want the expansion of
__VA_ARGS__
to produce Variant[size]{args[0], args[1], args[2], ...}
To start with, I'm going to create a helper called JOIN
:
#define JOIN(a, b) JOIN_H(a, b)
#define JOIN_H(a, b) a ## b
This may seem silly, but what it actually does is ensures that macros being joined together will be evaluated before they get joined -- so that macro functions being called will properly instantiate a join with their result rather than the full name.
Getting the size of __VA_ARGS__ / 2
Getting the size of __VA_ARGS__
usually requires two macros:
- One that passes
__VA_ARGS__, N, N-1, N-2, ...
into a helper macro, and
- Another that extracts that
N
at the end.
Something like:
#define COUNT_VA_ARGS(...) \
COUNT_VA_ARGS_H(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define COUNT_VA_ARGS_H(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N,...) N
This works because the first one passes all the arguments of __VA_ARGS__
and counts backwards from the Nth number, and we then extract N
.
In your case, you want the __VA_ARGS__ / 2
, so you will need to double those arguments
#define COUNT_VA_ARGS(...) \
COUNT_VA_ARGS_H(__VA_ARGS__, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0)
#define COUNT_VA_ARGS_H(_1, _1, _2, _2, _3, _3, _4, _4, _5, _5, _6, _6, _7, _7, _8, _8, _9, _9, _10, _10, N,...) N
Making __VA_ARGS__
produce Wrap(<arg 1>), Wrap(<arg 3>), ...
Unlike C++ variadic templates, macros are not able to have expansion expressions where you can wrap each argument. To simulate this in Macros, you pretty much have to have N expansions explicitly defined, and then to call this, you will need to combine the result of one macro to call it.
#define WRAP_VA_ARGS_0(wrap)
#define WRAP_VA_ARGS_1(wrap,x0) wrap(x0)
...
#define WRAP_VA_ARGS_10(wrap,x0,x1, ..., x10) wrap(x0), wrap(x1), ..., wrap(x10)
// Call into one of the concrete ones above
#define WRAP_VA_ARGS(wrap, __VA_ARGS__) JOIN(WRAP_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS__))(__VA_ARGS__)
Since the expression actually wants every other argument in it, you will again need to double the arguments:
#define WRAP_VA_ARGS_0(wrap)
#define WRAP_VA_ARGS_1(wrap,x0type,x0) wrap(x0)
#define WRAP_VA_ARGS_2(wrap,x0type,x0,x1type,x1) wrap(x0), wrap(x1)
...
Calling WRAP_VA_ARGS(Variant, int, A, float, B)
will now create Variant(A), Variant(B)
Creating a list of index values
Similar to the above wrapping, you will need to find a way to generate a list of numbers, and wrap it. Again this must delegate to the counting wrapper
#define WRAP_COUNT_VA_ARGS_0(wrap)
#define WRAP_COUNT_VA_ARGS_1(wrap) wrap[0]
#define WRAP_COUNT_VA_ARGS_2(wrap) wrap[0], wrap[1]
...
#define WRAP_COUNT_VA_COUNT_ARGS(wrap, ...) JOIN(WRAP_COUNT_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS))(wrap)
Calling WRAP_COUNT_VA_COUNT_ARGS(args, int, A, float, B)
should generate args[0], args[1]
Putting it all together
Trigger warning: this is going to be ugly
#define DECLARE_FUNCTION(name, ...) \
void name(__VA_ARGS__) { \
JOIN(name, _PROXY)((Variant[COUNT_VA_ARGS(__VA_ARGS__)+1]) {WRAP_VA_ARGS(Variant,__VA_ARGS__), Variant()}); \
} \
void JOIN(name, _PROXY)(const Variant (&args)[COUNT_VA_ARGS(__VA_ARGS__) + 1]) { \
JOIN(name, _HANDLER)(WRAP_COUNT_VA_COUNT_ARGS(args, __VA_ARGS__)); \
} \
void JOIN(name, _HANDLER)(__VA_ARGS__) { \
\
}
With any luck, an example of DECLARE_FUNCTION(myFunction, int, A, int, B, char, C)
should produce:
void myFunction(int A, int B, char C) {
myFunction_PROXY((Variant[3+1]{Variant(A), Variant(B), Variant(C), Variant()});
}
void myFunction_PROXY(const Variant (&args)[3+1]) {
myFunction_HANDLER(args[0], args[1], args[2]);
}
void myFunction_HANDLER(int A, int B, char C) {
}
Note: the array is created by the constant expression 3 + 1
, since we need do this arithmetic to account for the Variant()
at the end of myFunction_PROXY
's call
Don't do macros. Macros are bad, mmmm'kay?