I’m trying to generate a function declaration using a macro
/* goal: generate int f(int a, float b) */
template<typename P>
struct ptype;
template<typename P>
struct ptype<void(P)> { typedef P type; };
#define NAMEe
#define COMMAe
#define COMMA ,
#define NAME(N) N PARAMS
#define PARAMS(P, ...) COMMA ## __VA_ARGS__ P NAME ## __VA_ARGS__
#define PARAM_ITER(P) P NAME
#define PROTO(R, N, P) \
ptype<void R>::type N (PARAM_ITER P (,e))
PROTO((int), f, (int)(a)(float)(b));
It will iteratively process the next (name) or (type) by NAME or PARAMS respectively, with the ... having an empty macro argument. But GCC complains with
prototype.hpp:20:35: warning: ISO C99 requires rest arguments to be used
And clang complains with
ptype<void (int)>::type f (int aprototype.hpp:20:1: warning: varargs argument missing, but tolerated as an extension [-pedantic]
I think this happens because of the following
#define FOO(X, ...)
FOO(A);
Because I’m not passing an argument for the ... or each of those (name) or (type). Is there any simple work around I can apply?
I’ve now used a technique similar to the technique used by @James to find the length of a parameter list. If as second argument, instead of O, ONT is passed, I will print the comma and NAME. The following is the final solution:
/* goal: generate void f(int a, float b) */
template<typename P>
struct ptype;
template<typename P>
struct ptype<void(P)> { typedef P type; };
#define TYPE_DO(X) X
#define TYPE_DONT(X)
#define TYPE_MAYBE(X, A, ...) TYPE_D ## A (X)
#define COMMA_DO ,
#define COMMA_DONT
#define COMMA_MAYBE(A, B, ...) COMMA_D ## B
#define NAME_DO NAME
#define NAME_DONT
#define NAME_MAYBE(A, B, ...) NAME_D ## B
#define NAME(N) N PARAMS
#define PARAMS(...) COMMA_MAYBE(__VA_ARGS__,O,O) TYPE_MAYBE(__VA_ARGS__,O,O) \
NAME_MAYBE(__VA_ARGS__,O,O)
#define PARAM_ITER(P) P NAME
#define PROTO(R, N, P) \
ptype<void R>::type N (PARAM_ITER P (D,ONT))
Test:
#define STR1(X) #X
#define STR(X) STR1(X)
int main() {
// prints correctly
std::cout << STR(PROTO((int), f, (int)(a)(float)(b)));
}
P99 has a macro that does exactly what you want, I think, namely
P99_PROTOTYPE. It has a “signature” ofwhere
RTis the return type (may bevoid) andATare the argument types. The list of argument types may be empty, in which case it is substituted byvoid.Beware that P99 is made for C99 and not for C++. You’ll encounter particular difficulties if your arguments contain commas. C++’s syntax abuse of tokens
<and>as bracketing expressions for templates is particularly bad for the preprocessor. C-preprocessor and C++ are basically incompatible languages on the syntax level.P99 gets away from the difficulties that you are facing by detecting the number of arguments that the macro receives on a call and reacts differently on the border cases.