Немножко macros tricks.
Макросы в целом скорее bad practice. Они могут приводить к нечитаемому коду и неожиданному поведению. Но они всё же позволяют делать интересные вещи.
1. При использовании макросов часто совсем непонятно, во что же он раскрывается. Как вам такой код?
int x = 1;
SOME_MACROS(x)На первый взгляд он кажется неестественным: а почему это компилится? Почему нет точки с запятой? Потому что автор макроса засунул её внутрь. Вы её не поставите и всё скомпилируется. Но кого-то позже введёт это в ступор, кто-то решит поменять ваш макрос и код перестанет компилиться из-за нехватки этой самой
; во всех местах использования. Конечно, можно договориться всегда писать её после макросов, но тогда это лишь рекомендация, которую можно и не выполнять. Лучше будет заставить пользователя макроса писать аккуратно. Например обернув всё тело макроса в do-while:#define SOME_MACROS(x) \
do { \
++x; \
} while (false)Ещё вы решаете проблему пересечения имён, т.к. создали новую область видимости. Короч немного накостылили и стало получше.
2. Об этом уже упоминалось, но повторим.
Думаю, вы тоже видели какой-то подобный код:
std::string very_long_string = "This is first part of long string." "And this is second";Никаких плюсов и функций конкатенации: компилятор сделает это одной строкой сам. Используя такое поведение можно реализовать интересный макрос:
#define LOG(exp) std::cout << "Result of " #exp "=" << (exp);
int x = 12;
LOG(x*5);Получаем:
Result of x*5=603. Есть популярные макросы, которые позволяют добавлять немного информации например при отладке (помните, что не все есть везде и их результаты иногда implementation defined).
Первые это
__FILE__, __LINE__:
#define assert(expr) \
(static_cast<bool>(expr) \
? void(0) \
: assert_fail(#expr, __FILE__, \
__LINE__, __ASSERT_FUNCTION))
Или __PRETTY_FUNCTION__:
void f(int) {
std::cout << __PRETTY_FUNCTION__;
}
Получим: void f(int)
Ещё есть __FUNCTION__ и __func__.
И __COUNTER__: по мере вызова в рамках программы он выдаёт натуральные числа от нуля и выше:std::cout << __COUNTER__ << __COUNTER__ << __COUNTER__;
Результат: 012.
Последний можно использовать для создание макроса для анонимных переменных:
#define CONCATENATE_IMPL(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#ifdef __COUNTER__
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, COUNTER)
#else
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
#endif
Теперь вы можете спокойно юзать этот макрос для создания анонимных переменных:auto ANONYMOUS_VARIABLE(var) = gsl::finally([] {});
Понятно, что это не анонимная в полном смысле этого слова переменная, т.к. её можно использовать, но теперь не нужно думать, какие имена использовать для нескольких переменных, которые нужно лишь создать.Тут рассказывают, как это сделать чуть более юзабельным.
Кстати бонусом вопрос: почему нам нужен промежуточный макрос
CONCATENATE_IMPL?