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>);.