You're not gonna like this:
#define COMMA ,
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(x, y) class y : public x {};
#else
# define DOCUMENTED_TYPEDEF(x, y) typedef x y;
#endif
DOCUMENTED_TYPEDEF(Foo<R COMMA S>,Bar)
Test:
$ gcc -E comma-macro.c
# 1 "comma-macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "comma-macro.c"
# 9 "comma-macro.c"
typedef Foo<R , S> Bar;
Macro argument lists are parsed for parentheses and commas before any substitution takes place. Then COMMA
is replaced in the x
argument, and x
is substituted into the macro body. AT that time, the argument breaking is done; it is not relevant that COMMA
got replaced with a comma punctuator token. However, that comma will separate arguments which occur in any macro calls generated by that macro, so if those have to be protected, you need something more crazy.
You can hide the COMMA
behind a function-like macro, say PAIR
:
#define COMMA ,
#define PAIR(A, B) A COMMA B
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(x, y) class y : public x {};
#else
# define DOCUMENTED_TYPEDEF(x, y) typedef x y;
#endif
DOCUMENTED_TYPEDEF(PAIR(Foo<R, S>), Bar)
At first glance it is more appealing, but there are probably downsides. It's more obfuscated. The reader wonders, is there semantics behind PAIR
? Whereas COMMA
looks too obtuse to have semantics, and its purpose is likely instantly obvious to anyone who has battle scars from fighting with the preprocessor.
About PAIR
, we may be able to hide it, and end up with a syntax like in Zwol's answer. But then we need multiple variants of the DOCUMENTED_TYPEDEF
.
Also, by the way, let's drop the useless COMMA
which is not needed on the right hand side of a macro:
#define PAIR(A, B) A, B
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF_2(x2, y) class y : public PAIR x2 {};
#else
# define DOCUMENTED_TYPEDEF_2(x2, y) typedef PAIR x2 y;
#endif
DOCUMENTED_TYPEDEF_2((<R, S>), Bar)
$ gcc -std=c90 -E -Wall -pedantic comma-macro.c
# 1 "comma-macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "comma-macro.c"
# 11 "comma-macro.c"
typedef <R, S> Bar;
This looks like it may be doable with C99 style variadic macros. However, that may violate the portability requirement discussed in the comments, not to mention that this is C++. For the sake of future visitors:
#define PNEUMATIC_COMMA_GUN(A, ...) A, ## __VA_ARGS__
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(xv, y) class y : public PNEUMATIC_COMMA_GUN xv {};
#else
# define DOCUMENTED_TYPEDEF(xv, y) typedef PNEUMATIC_COMMA_GUN xv y;
#endif
DOCUMENTED_TYPEDEF((<R, S, T, L, N, E>), Bar)
$ gcc -std=c99 -E -Wall -pedantic comma-macro.c
# 1 "comma-macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "comma-macro.c"
# 9 "comma-macro.c"
typedef <R, S, T, L, N, E> Bar;
int x;
andint (x);
are the same thing.(int) x;
looks like a cast expression. – Drippy