A solution is:
#define Argument1(a,...) Argument2(a
#define Argument2(a, b,...) b
#define TestEmpty() ,
#define PRINT(...) Argument1(TestEmpty __VA_ARGS__ (),), printf(__VA_ARGS__);,)
For this source text:
Test 0: PRINT()
Test 1: PRINT("Hello, world.\n")
Test 2: PRINT("Result is %d.\n", result)
Test 3: PRINT("%d = %g.\n", 3, 3.)
the result of preprocessing is:
Test 0:
Test 1: printf("Hello, world.\n");
Test 2: printf("Result is %d.\n", result);
Test 3: printf("%d = %g.\n", 3, 3.);
This requires that the arguments to PRINT
not start with a parenthesized item, which seems acceptable for this particular question. The code here is not a general solution for detecting an empty parameter when parentheses may be present.
Explanation:
- In preparing to replace
PRINT
, __VA_ARGS__
is replaced. TestEmpty
is not replaced at this time because it is not followed by parentheses.
- Then
PRINT
is replaced, and the result is rescanned.
Argument1
is identified for replacement. In preparation for that, its arguments are processed.
- At this point, if
__VA_ARGS__
is empty, we have have the tokens TestEmpty ()
, which are replaced by ,
. Otherwise, TestEmpty <some tokens> ()
remains. Note that, if TestEmpty ()
is present, the ,
it expands to is the first argument to Argument1
. It is not an argument separator, because the arguments to Argument1
have already been identified.
- Then
Argument1
is replaced, producing either Argument2(,
(if __VA_ARGS
was empty) or Argument2(TestEmpty
possibly followed by additional tokens (otherwise).
- The result is either
Argument2(,, printf();,)
or Argument2(TestEmpty <some tokens>, printf(<some tokens>);,)
, according to whether __VA_ARGS__
was empty or not.
- The tokens are now rescanned for further replacement, so the
,
will now be recognized as an argument separator.
- Finally,
Argument2
is replaced with an empty token list or with printf(<some tokens>);
.