TGViewer
this->notes. this->notes. @thisnotes · 4.52K subscribers
Post #463 1.89K
#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.
  • 🤯 27
  • ❤ 5
  • 👎 2
  • 🤪 2
More from @thisnotes
  1. Sep 17, 2026#common Сидите вы себе спокойно, разрабатываете поиск каких-нибудь объектов. Может это тов…
  2. Sep 9, 2026#cpp #books Да, книга 2001ого года. Мы ровесники. И да, в ней в основном обсуждаются какие…
  3. Sep 2, 2026#perf Попробовал собрать в кучку (кажется, немного сумбурно всё же) мысли по двум моментам…
  4. Aug 31, 2026Давайте новый тег заведём: #perf Во-первых, надо понять, что я вообще понимаю под перфом,…
  5. Aug 27, 2026#common Мы часто делаем системы, которые обладают какими-то ограничениями. Ограничения наш…
  6. Aug 24, 2026#list 0. [talk] Achieving Peak Performance for Matrix Multiplication in C++. Aliaksei Sala…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →