this->notes.
4.51K subscribers
54 photos
1 file
426 links
О разработке, архитектуре и C++.

Tags: #common, #cpp, #highload и другие можно найти поиском.
Задачки: #poll.
Мои публикации: #pub.
Автор и предложка: @vanyakhodor.
GitHub: dasfex.
Download Telegram
#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.
🤯265👎2🤪2
#cpp

Day 36.

Иногда используют похожую структуру для реализации INC/DEC:

#define AT_0(a,b,c,d,e,f,g,h) a
#define AT_1(a,b,c,d,e,f,g,h) b
#define AT_2(a,b,c,d,e,f,g,h) c
#define AT_3(a,b,c,d,e,f,g,h) d
#define AT_4(a,b,c,d,e,f,g,h) e
#define AT_5(a,b,c,d,e,f,g,h) f
#define AT_6(a,b,c,d,e,f,g,h) g
#define AT_7(a,b,c,d,e,f,g,h) h

#define CAT(a,b) CAT_I(a,b)
#define CAT_I(a,b) a##b

#define INC_(n) CAT(AT_, n)(1,2,3,4,5,6,7,0)
#define DEC_(n) CAT(AT_, n)(7,0,1,2,3,4,5,6)

#define INC(n) INC_(n)
#define DEC(n) DEC_(n)

INC(6) // 7
INC(7) // 0
DEC(0) // 7
INC(DEC(INC(INC(1)))) // 3


AT_x равносильно взять элемент по индексу x, а INC/DEC просто передают сдвинутое на 1 в нужную сторону количество аргументов в нужном порядке.

Тут фактически мы сделали конечную группу \Z_8.

@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
👍11🌚6🤯21👎1🤔1