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

Tags: #common, #cpp, #highload и другие можно найти поиском.
Задачки: #poll.
Мои публикации: #pub.
Автор и предложка: @vanyakhodor.
GitHub: dasfex.
Download Telegram
#cpp

Day 4.

Макросы просто подменяют текст. Вот прям втупую. Но мы часто про это забываем, потому часто пишем их неправильно.

Паша @cppdurka Сухов говорил, что когда-то видел какой-то гайд на 18 страниц, как правильно писать макросы. Видел и потерял. А теперь жалеет об этом.

Я бы тоже хотел почитать. Скиньте, если знаете про такой!

@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
👍15💩6😁3🫡1
#cpp

Day 5.

#ifndef в include guards на самом деле #if !defined(...). Есть ещё #ifdef (#if defined(...)). Сам #if тоже есть. Можете намутить себе условной компиляции по самые колени. Например, может у вас есть какая-то дебажная сборка с большим кол-вом логов:


#ifdef DEBUG
printf("debug log");
#endif

И код можно скомпилировать с -DDEBUG.

Или вы хотите написать разную логику для 32-битной и 64-битных систем:


#if !(defined __LP64__ || defined __LLP64__) || defined _WIN32 && !defined _WIN64
// code for a 32-bit system
#else
// code for a 64-bit system
#endif


Или вы хотите использовать новый стандарт, если он доступен, и не использовать, если есть своя поделка:


#if __cplusplus >= 202002L
#include <span>
#else
#include "my_span.hpp"
#endif


Хотя правильнее было бы проверять не на стандарт, а на доступность фичи/инклуда/атрибута, так как стандарт может быть новым, а STL старой. А некоторые фичи могут бекпортить.


#if __cpp_lib_span

#if __has_include(<format>)

#if __has_cpp_attribute(likely)
#define LIKELY [[likely]]
#else
#define LIKELY
#endif


Значения __cpp_* макросов это кстати дата принятия фичи WG21. Например, для __cpp_constexpr это 202211L (ноябрь 2022). Для того же constexpr можно узнавать его версию и соответственно набор возможностей, которые вы можете использовать, так как от стандарта к стандарту он сильно умощнялся.

Кто-то мне рассказывал байку, что MSVC много лет врал про значение __cplusplus (всегда возвращал 199711L, даже для C++17), потому писали вот так:


#if defined(_MSVC_LANG)
#define CPP_VER _MSVC_LANG
#else
#define CPP_VER __cplusplus
#endif


@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
👍26🔥2💩21
#cpp

Day 6.

Предположим, мы хотим написать макрос для возведения значения или переменной в квадрат:

#define SQR(x) x * x

В зависимости от способа использования, вы можете не получить или получить проблемы. В таком коде:

int a = SQR(1 + 2);

мы на самом деле получим

int a = 1 + 2 * 1 + 2;

Что не совсем то, что вы ожидали.

Для надёжности лучше завернуть аргументы в скобки:

#define SQR(x) (x) * (x)


Но этого тоже может иногда не хватать:

#define INC(x) (x) + 1

int a = 10 / INC(1 + 1);

Получим:

int a = 10 / (1 + 1) + 1;

Что тоже не то, что мы ожидали. Так что адекватный макрос должен как минимум завернуть в скобки каждый отдельный аргумент + завернуть всё выражение:

#define SQR(x) ((x) * (x))


Как максимум, ваш x может быть вообще-то функцией с сайд-эффектом:

int x = SQR(GetValueFromDbAndPostToKafka());

Так что прям совсем идеально было бы сохранить результат внутри макроса и переиспользовать его. Но я не знаю, как это написать, чтобы было полностью эквивалентно функции.

Мб пора начать сворачивать с макродорожки на что-то более современное......

@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
🔥226
#cpp

Day 7.

Вчера мы писали макросы, которые заменяли собой один statement. А что, если я хочу что-то более сложное?

Обопрусь на пример от Паши (https://xn--r1a.website/cpp_durka/23): напишем макрос для инкремента двух переменных.

#define INCREMENT_BOTH(x, y) (x)++; (y)++

Тут мы умные. Сразу взяли выражения в скобки. Не поставили в конце ; , чтобы обязать пользователя её поставить самому (для консистентности кода).

Но если мы чуть-чуть отступим от глупого использования:

if (condition)
INCREMENT_BOTH(a, b);

мы получим

if (condition)
(a)++; (b)++;

b инкрементится вне зависимости от условия.

Или ещё пример:

#define MACRO(condition, x) if (condition) std::cout << (x)

if (flag)
MACRO(flag2, 5);
else
std::cout << 10;

Тут else вдруг начинает относиться к if из макроса, а не изначальному, что очевидно баг.

Канонический способ такое исправить:

#define INCREMENT_BOTH(x, y) \
do { \
(x)++; \
(y)++; \
} while (0)

#define MACRO(condition, x) \
do { \
if (condition) { \
std::cout << (x);\
} \
} while (0)


@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
👍321
#cpp

Day 8.

В C тоже есть массивы. И работягам тоже хочется знать, сколько в этих массивах элементов. Стандартного решения у ребят там нет, но есть общий подход, перетекающий из кодовой базы в кодовую базу. Зовётся ARRAY_LENGTH/ARRAY_LEN/ARRAY_SIZE/COUNTOF/...
Выглядит так:

#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))


Тут мы полагаемся на sizeof, который, согласно стандарту C99 (моя вольная интерпретация):

sizeof возвращает размер операнда (в байтах). Размер зависит от типа операнда. Результат — int. Обычно результат не evaluated и является integer constant.


Another use of the sizeof operator is to compute the number of elements in an array:

sizeof array / sizeof array[0]


Так что sizeof(x) вернёт кол-во байт типа массива (если x — массив int[3], то можем получить (в зависимости от системы) 12). sizeof((x)[0]) вернёт размер типа одного элемента (в нашем случае 4). Вот и получаем 3.

На собесах могут спрашивать вопросы с подвохом вида:

int x = 10;
sizeof(x++);
std::cout << x; // result?

Конечно, вы на такое не попадётесь и скажете 10, ведь sizeof интересует тип. Он не evaluatит свой аргумент. Но всегда ли это так?

Конечно нет!
Если ваш аргумент — Variable Length Array, то sizeof придётся вычислить аргумент. Мы можем запруфать это через наличие сайдэффекта:


int f() {
printf("called\n");
return 10;
}

int main() {
sizeof(int[f()]);
}



Увидим called в output.
https://godbolt.org/z/8G1soa8T1

Теперь срочно требуйте оффер х3 от вашего текущего дохода, ведь собеседующий почти наверняка этого не знает.

@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
👍20🔥96🤯3😭2
#cpp

Day 9.

До сегодняшнего дня мы были где-то на уровне 1. Сегодня делаем шаг на следующую ступеньку (вниз или вверх, это как посмотреть).

Подстановка макросов (expansion) не являются рекурсивной.


#define A(x) A(x x)
A(x) // A(x x)

#define B(x) C(x x)
#define C(x) B(x x)
B(x) // B(x x x x)


Выстрелить себе в ногу становиться чуть сложнее. Или проще. Это опять как посмотреть.

@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.
11👍7🔥1