Решение:
#define Argument1(a,...) Argument2(a
#define Argument2(a, b,...) b
#define TestEmpty() ,
#define PRINT(...) Argument1(TestEmpty __VA_ARGS__ (),), printf(__VA_ARGS__);,)
Для этого исходного текста:
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.)
результат предварительной обработки:
Test 0:
Test 1: printf("Hello, world.\n");
Test 2: printf("Result is %d.\n", result);
Test 3: printf("%d = %g.\n", 3, 3.);
Для этого необходимо, чтобы аргументыPRINT
не начинать с элемента в скобках, что кажется приемлемым для этого конкретного вопроса.Код здесь не является общим решением для обнаружения пустого параметра, когда могут присутствовать скобки.
Объяснение:
- При подготовке к замене
PRINT
, __VA_ARGS__
заменяется.TestEmpty
в настоящее время не заменяется, так как за ним не следуют скобки. - Затем заменяется
PRINT
, и результат повторно сканируется. Argument1
идентифицируется для замены.Готовясь к этому, его аргументы обрабатываются. - На данный момент, если
__VA_ARGS__
пусто, у нас есть токены TestEmpty ()
, которые заменяются на ,
.В противном случае TestEmpty <some tokens> ()
остается.Обратите внимание, что если присутствует TestEmpty ()
, то ,
расширяется до и является первым аргументом до Argument1
.Это не разделитель аргументов, потому что аргументы для Argument1
уже определены. - Затем
Argument1
заменяется, производя либо Argument2(,
(если __VA_ARGS
было пусто), либо, возможно, Argument2(TestEmpty
с последующими дополнительными токенами (в противном случае). - Результат будет
Argument2(,, printf();,)
или Argument2(TestEmpty <some tokens>, printf(<some tokens>);,)
, в зависимости от того, был ли __VA_ARGS__
пуст или нет. - Теперь токены повторно сканируются для дальнейшегозамену, поэтому
,
теперь будет распознаваться как разделитель аргументов. - Наконец,
Argument2
заменяется пустым списком токенов или printf(<some tokens>);
.