Что сделало бы макросы препроцессора C++ приемлемым инструментом разработки?
По-видимому макросы препроцессора в C++ являются
Оправданно боится и избегает сообщество C++.
Однако есть несколько случаев, когда макросы C++ полезны.
Видя в качестве препроцессора макросы могут быть чрезвычайно полезны и могут сократить повторяющийся код очень простым способом --
-- оставляет меня с вопросом, что именно делает макросы препроцессора "злыми", или, как название вопроса говорит, какая функция ( или удаление функции) была бы необходима из макросов препроцессора, чтобы сделать их полезными в качестве "хорошего" инструмента разработки (вместо заполнения, которого все стыдятся при его использовании). (В конце концов, языки Lisp, похоже, включают макросы.)
обратите внимание : это не О #include или #pragma или #ifdef. Речь идет о #define MY_MACRO(...) ...
примечание: Я не хочу, чтобы этот вопрос был субъективным. Стоит ли вам так думать это так, не стесняйтесь голосовать, чтобы переместить его в programmers.SE.
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++
Так что проще сказать "это зло", чем рисковать тем, что разработчик злоупотребляет ими.
- макросы не обеспечивают безопасность типов
- задачи, в которых параметры выполняются дважды, например #define MAX (a,b) ((a)>(b)? (a): (b)) и применить его для MAX (i++, y--)
- Проблемы с отладкой, так как их имена не встречаются в таблице символов.
Принуждение программиста использовать правильные имена для макросов... и лучшие инструменты для отслеживания замены макросов исправили бы большинство моих проблем. Я не могу сказать, что у меня были серьезные проблемы до сих пор... Это то, чем вы сжигаете себя и учитесь проявлять особую осторожность позже. Но они остро нуждаются в лучшей интеграции с IDE, отладчиками.
Comments