#cpp
Day 35. Как мы уже поняли, препроцессор умеет только несколько базовых операций:
• раскрывать макросы
• склеивать токены (
##)
• работать с запятыми в списках аргументов (то бишь разделять по запятым аргументы).
Но иногда хочется делать что-то совсем человеческое. Например, написать if:
IF(1)(a, b)
IF(0)(a, b)
Так нельзя. Потому приходится крутиться.
Сегодня придётся напрячь думалку.
Зайдём издалека.
Давайте определим вот такой странный макрос:
#define PROBE ,found
Обратите внимание, что он раскрывается в 2 аргумента для другого макроса: "" и "found".
И сделаем макрос
CHECK:
#define TUPLE_AT_1(b,a,...) a // just return 2nd argument
#define CHECK(...) TUPLE_AT_1(__VA_ARGS__,)
Теперь мы можем сделать конструкцию, которая позволит нам узнать, определён ли макрос:
#define FOO PROBE
#define IS_FOO_IMPL(x) IS_FOO_##x
#define IS_FOO(x) CHECK(IS_FOO_IMPL(x), 0)
#define IS_FOO_FOO PROBE
Теперь можем проверять:
IS_FOO(FOO)
// CHECK(IS_FOO_IMPL(FOO), 0)
// TUPLE_AT_1(IS_FOO_IMPL(FOO), 0, )
// TUPLE_AT_1(IS_FOO_FOO, 0, )
// TUPLE_AT_1(PROBE, 0, )
// TUPLE_AT_1(, found, 0, )
// found
или иначе:
IS_FOO(BAR)
// ...
// 0
Вроде схема работы понятна, но зачем это нужно, не совсем. Давайте вернёмся к изначальному примеру.
IF(1)(A, B ) // A
IF(0)(A, B ) // B
Сделаем базу:
#define PROBE ~, 1
#define SECOND(a, b, ...) b
#define CHECK(...) SECOND(__VA_ARGS__, 0)
Тогда
CHECK(PROBE) // 1
CHECK(x) // 0
Теперь построим макрос
NOT:
#define NOT_0 PROBE
#define NOT(x) CHECK(NOT_##x)
И когда используем:
NOT(0) // CHECK(NOT_0) -> CHECK(PROBE) -> 1
NOT(1) // CHECK(NOT_1) -> NOT_1 doesn't exist -> 0
Вот мы уже научились инвертировать значение.
Осталось сделать
BOOL:
BOOL(0) = 0
BOOL(1) = 1
Реализация тривиальная:
#define BOOL(x) NOT(NOT(x))
Последнее, что нам нужно, это сделать
IF:
#define IF_0(t, f) f
#define IF_1(t, f) t
IF_0(A, B) // B
IF_1(A, B) // A
Его мы можем собрать через знакомый нам
CAT:
#define CAT(a, b) CAT_I(a, b)
#define CAT_I(a, b) a##b
#define IF(c) CAT(IF_, BOOL(c))
Конечно, в коде не обязательно писать
IF(0)/
IF(1). Вместо значения может быть какой-то другой макрос, который раскроется в конкретное число. Сам макрос может прийти откуда-то снаружи.
Жоска мне кажется. Чего только не придумают.
@thisnotes.
Patreon,
Boosty.
Спасибо Artyom Garkavy и niki4smirn.