Что сделало бы макросы препроцессора C++ приемлемым инструментом разработки?



По-видимому макросы препроцессора в C++ являются




Оправданно боится и избегает сообщество C++.




Однако есть несколько случаев, когда макросы C++ полезны.

Видя в качестве препроцессора макросы могут быть чрезвычайно полезны и могут сократить повторяющийся код очень простым способом --



-- оставляет меня с вопросом, что именно делает макросы препроцессора "злыми", или, как название вопроса говорит, какая функция ( или удаление функции) была бы необходима из макросов препроцессора, чтобы сделать их полезными в качестве "хорошего" инструмента разработки (вместо заполнения, которого все стыдятся при его использовании). (В конце концов, языки Lisp, похоже, включают макросы.)



обратите внимание : это не О #include или #pragma или #ifdef. Речь идет о #define MY_MACRO(...) ...



примечание: Я не хочу, чтобы этот вопрос был субъективным. Стоит ли вам так думать это так, не стесняйтесь голосовать, чтобы переместить его в programmers.SE.

683   7  

7 ответов:

Большинство злоупотреблений препроцессором происходят от непонимания, цитируя Павла Менсонида (автора Boost.Библиотека препроцессора ):

Практически все проблемы, связанные с неправильным использованием препроцессора, связаны с попытками сделать объектно-подобные макросы похожими на постоянные переменные и функционально-подобными макрокоманды выглядят как вызовы функций базового языка. В лучшем случае, корреляция между функциональными вызовами макросов и вызовами функций должно быть случайный. Это никогда не должно рассматриваться как цель. Тот это фундаментально сломанный менталитет.

Поскольку препроцессор хорошо интегрирован в C++, его легче размыть, и большинство людей не видят разницы. Например, попросите кого-нибудь написать макрос, чтобы сложить два числа вместе, Большинство людей напишут что-то вроде этого:
#define ADD(x, y) ((x) + (y))
Это совершенно неверно. Запускает это через препроцессор:
#define ADD(x, y) ((x) + (y))
ADD(1, 2) // outputs ((1) + (2))

Но ответ должен быть 3, так как добавление 1 к 2 ИС 3. Однако вместо этого макрос пишется для генерации выражения C++. Мало того, его можно было бы рассматривать как функцию C++, но это не так. Вот где это приводит к злоупотреблениям. Он просто генерирует выражение C++, и функция-это гораздо лучший способ.

Кроме того, макросы вообще не работают как функции. Препроцессор работает через процесс сканирования и расширения макросов, который очень отличается от использования стека вызовов для вызова функций.

Бывают времена, когда это может быть допустимо для макросов генерировать код C++, если он не размывает строки. Точно так же, как если бы вы использовали python в качестве препроцессора для генерации кода, препроцессор может делать то же самое и имеет то преимущество, что ему не требуется дополнительный шаг сборки.

Также препроцессор может использоваться с DSL, например здесь и здесь, но эти DSL имеют предопределенную грамматику в препроцессоре, которую он использует для генерации кода C++. Это не очень размывает линии, так как он использует другая грамматика.

Макросы широко считаются злом, потому что препроцессор-это глупый инструмент для замены текста, который практически ничего не знает о C/C++.

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

Там, где это возможно, шаблоны и встроенные функции являются лучшим выбором. Единственная причина, по которой я могу думать о том, почему C++ все еще нуждается в препроцессоре, - это удаление #includes и комментариев.

Широко оспариваемое преимущество состоит в том, чтобы использовать его для уменьшения повторение кода; но, как вы можете видеть по библиотеке препроцессора boost, нужно приложить много усилий, чтобы злоупотребить препроцессором для простой логики, такой как циклы, что приводит к уродливому синтаксису. На мой взгляд, лучше писать сценарии на реальном языке программирования высокого уровня для генерации кода, а не использовать препроцессор.

У макросов есть одна примечательная особенность - ими очень легко злоупотреблять и довольно трудно отлаживать. Вы можете написать практически все, что угодно с помощью макросов, затем макросырасширяются в однострочные , и когда ничего не работает, вам очень трудно отладить полученный код.

Одна только эта функция заставляет десять раз подумать о том, следует ли и как использовать макросы для своей задачи.

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

Самое важное в макросах то, что они не имеют области действия и не заботятся о контексте. Они почти инструмент замены текста дампа. Поэтому, когда вы #определите max(.... затем везде, где у вас есть max, он заменяется; поэтому, если кто-то добавляет слишком общие имена макросов в свои заголовки, они, как правило, влияют на код, который они не предназначались.

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

Хорошим руководством является выбор уникальных имен, и при создании шаблонного кода, #undef их как можно скорее, чтобы не загрязнять пространство имен.

Кроме того, они не обеспечивают безопасность типа или перегрузку.

Иногда макросы, возможно, являются хорошим инструментом для генерации шаблонного кода, например, с помощью boost.pp вы можете создать макрос, который поможет вам создавать перечисления, такие как:

ENUM(xenum,(a,b,(c,7)));

Который мог бы расширяться

enum xenum { a, b, c=7 };

std::string to_string( xenum x ) { .... }

Такие вещи, как assert (), которые должны реагировать на NDEBUG, также часто проще реализовать в виде макросов

Есть много применений, где разработчик C использует макросы, а разработчик C++ использует шаблоны.

Есть, очевидно, угловые случаи, когда они полезны, но в большинстве случаев это плохие привычки из мира C, применяемые к C++ людьми, которые считают, что есть такой язык, называемый C/C++

Так что проще сказать "это зло", чем рисковать тем, что разработчик злоупотребляет ими.
  1. макросы не обеспечивают безопасность типов
  2. задачи, в которых параметры выполняются дважды, например #define MAX (a,b) ((a)>(b)? (a): (b)) и применить его для MAX (i++, y--)
  3. Проблемы с отладкой, так как их имена не встречаются в таблице символов.

Принуждение программиста использовать правильные имена для макросов... и лучшие инструменты для отслеживания замены макросов исправили бы большинство моих проблем. Я не могу сказать, что у меня были серьезные проблемы до сих пор... Это то, чем вы сжигаете себя и учитесь проявлять особую осторожность позже. Но они остро нуждаются в лучшей интеграции с IDE, отладчиками.

Comments

    Ничего не найдено.