Не используйте макросы для манипуляций с текстом программы
Причина
Макросы являются основным источником ошибок. Макросы не подчиняются обычным правилам области видимости и типов. Макросы гарантируют, что человек-читатель видит нечто иное, чем компилятор. Макросы усложняют создание инструментов.
Пример (плохой)
#define Case break; case /* ПЛОХО */
Этот безобидный с виду макрос превращает единственную строчную букву c вместо C в серьёзную ошибку управления потоком.
Примечание
Это правило не запрещает использование макросов для «управления конфигурацией» в #ifdef и т.п.
В будущем модули, вероятно, устранят необходимость в макросах для управления конфигурацией.
Примечание
Это правило также направлено против использования # для стрингификации и ## для конкатенации. Как и в случае с макросами в целом, существуют применения, которые «в основном безвредны», но даже они могут создавать проблемы для инструментов, таких как автодополнение, статические анализаторы и отладчики. Зачастую желание использовать сложные макросы — признак чрезмерно сложного проектирования. Кроме того, # и ## поощряют определение и использование макросов:
#define CAT(a, b) a ## b
#define STRINGIFY(a) #a
void f(int x, int y)
{
string CAT(x, y) = "asdf"; // ПЛОХО: сложно для инструментов (и некрасиво)
string sx2 = STRINGIFY(x);
// ...
}
Существуют обходные пути для низкоуровневых манипуляций со строками с помощью макросов. Например:
enum E { a, b };
template<int x>
constexpr const char* stringify()
{
switch (x) {
case a: return "a";
case b: return "b";
}
}
void f()
{
string s1 = stringify<a>();
string s2 = stringify<b>();
// ...
}
Это менее удобно в определении, чем макрос, но так же просто в использовании, имеет нулевые накладные расходы и является типизированным и ограниченным областью видимости.
В будущем статическая рефлексия, вероятно, устранит последние потребности в препроцессоре для манипуляций с текстом программы.
Контроль
Кричите, когда видите макрос, который используется не только для управления исходным кодом (например, #ifdef)