Это не красиво ... но этот подход может работать для вас. Предполагается, что макрос определен с использованием #define FOO
, #define FOO 1
или -DFOO
(предполагается, что, как правило, это создает нечто, эквивалентное #define FOO 1
).
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B
#define GLUE3(A,B,C) GLUE3_I(A,B,C)
#define GLUE3_I(A,B,C) A##B##C
#define AGLUE3(A,B,C) AGLUE3_I(A,B,C)
#define AGLUE3_I(A,B,C) A##B##C
#define TEST_ASSERT_KEY GLUE3(NO_ASSERT_PROBE,0_,NO_ASSERT_KEY)
#define NO_ASSERT_PROBE0_NO_ASSERT_KEY AGLUE3(NO_ASSERT_PROBE,0_,NO_ASSERT2)
#define NO_ASSERT_PROBE0_ ,1
#define NO_ASSERT_PROBE0_1 ,1
#define NO_ASSERT_TEST SECOND(TEST_ASSERT_KEY,0)
При этом ваше использование будет:
#if NO_ASSERT_TEST
#define assert2(x)
#else
#define assert2(x) assert2_handler(x);
#endif
Вот демоверсия на с накоплением кривоватых .
Используется сопоставление с образцом в препроцессоре через косвенный макрос SECOND
. Идея состоит в том, что он расширяется до второго аргумента, но только косвенно ... это позволяет вам построить в качестве первого аргумента шаблон. Обычно этот первый аргумент игнорируется, но если вы хотите сопоставить что-либо, вы можете установить его так, чтобы первым аргументом был макрос, который расширяется запятой; это сдвигает новый второй аргумент, заменяя значение по умолчанию.
Отсюда легче объяснить задом наперед. NO_ASSERT_TEST
использует TEST_ASSERT_KEY
для построения шаблона со значением по умолчанию 0
. TEST_ASSERT_KEY
сборок NO_ASSERT_PROBE0_
, связанных с NO_ASSERT_KEY
. Когда определено NO_ASSERT_KEY
, это создаст NO_ASSERT_PROBE0_
, объединенный с расширением того, что он определил. В противном случае он перестраивает тестовый токен, используя NO_ASSERT_PROBE0_
, объединенный с NO_ASSERT2
.
В любом случае это косвенная вставка, поэтому NO_ASSERT_KEY
в первом случае или NO_ASSERT2
во втором расширяется первым. В первом случае, если, скажем, NO_ASSERT_KEY
равно NO_ASSERT_CUSTOM
и NO_ASSERT_CUSTOM
не определено, это создает NO_ASSERT_PROBE0_NO_ASSERT_CUSTOM
, который является обычным идентификатором, который будет игнорироваться, что приведет к 0
из-за SECOND
в NO_ASSERT_TEST
. Но если NO_ASSERT_CUSTOM
определено для #define NO_ASSERT_CUSTOM
, то получается NO_ASSERT_PROBE0_
, который расширяется до ,1
, что переводит 1
в SECOND
вызов в NO_ASSERT_TEST
. Аналогично, если NO_ASSERT_CUSTOM
определено для -DNO_ASSERT_CUSTOM
в командной строке, это (как правило) сделает его определение эквивалентным #define NO_ASSERT_CUSTOM 1
, что приведет к NO_ASSERT_PROBE0_1
, который расширится до ,1
.
Случаи, когда NO_ASSERT_KEY
не определен, аналогичны.
Вероятно, есть более красивый способ построить эту конструкцию, если кто-то хочет сделать выстрел.