C препроцессор оправданно боялся и избегал сообщества C ++. Встроенные функции, константы и шаблоны обычно являются более безопасной и превосходной альтернативой #define
.
Следующий макрос:
#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
ни в коем случае не превосходит тип safe:
inline bool succeeded(int hr) { return hr >= 0; }
Но макросы имеют свое место, пожалуйста, перечислите виды использования макросов, которые вы не можете сделать без препроцессора.
Пожалуйста, поместите каждый вариант использования в отдельный ответ, чтобы за него проголосовали, и если вы знаете, как добиться одного из ответов без препроцессора, укажите, как в комментариях этого ответа.
c++
c-preprocessor
Motti
источник
источник
Ответы:
В качестве обёрток для функций отладки, для автоматической передачи таких вещей, как
__FILE__
,__LINE__
и т.д:источник
__FILE__
и__LINE__
также требует препроцессора. Использование их в вашем коде похоже на вектор заражения препроцессора.Методы всегда должны быть полным, компилируемым кодом; Макросы могут быть фрагментами кода. Таким образом, вы можете определить макрос foreach:
И используйте это так:
Начиная с C ++ 11, это заменяется основанным на диапазоне циклом for .
источник
for_each
было неприятно, потому что код, через который проходил каждый элемент, не был локальным по отношению к точке вызова.foreach
, (и я настоятельно рекомендуюBOOST_FOREACH
вместо ручного решения) давайте держать код близко к сайту итерации, делая его более читабельным. Тем не менее, как только лямбда выкатится,for_each
снова может быть путь.Защита заголовочных файлов требует макросов.
Есть ли другие области, которые требуют макросов? Не много (если есть).
Существуют ли другие ситуации, в которых полезны макросы? ДА!!!
Одним из мест, где я использую макросы, является очень повторяющийся код. Например, при переносе кода C ++ для использования с другими интерфейсами (.NET, COM, Python и т. Д.) Мне нужно отлавливать различные типы исключений. Вот как я это делаю:
Я должен поставить эти защелки в каждой функции оболочки. Вместо того, чтобы каждый раз печатать полные блоки catch, я просто набираю:
Это также облегчает обслуживание. Если мне когда-нибудь понадобится добавить новый тип исключения, мне нужно будет добавить только одно место.
Есть и другие полезные примеры: многие из них включают макросы
__FILE__
и__LINE__
препроцессор.В любом случае, макросы очень полезны при правильном использовании. Макросы не являются злом - их неправильное использование является злом.
источник
#pragma once
эти дни, поэтому я сомневаюсь, что охрана действительно необходима#pragma once
ломается на многих распространенных системах сборки.void handleExceptions(){ try { throw } catch (::mylib::exception& e) {....} catch (::std::exception& e) {...} ... }
. И на стороне функции:void Foo(){ try {::mylib::Foo() } catch (...) {handleExceptions(); } }
В основном:
__LINE__
и__FILE__
)источник
Внутри условной компиляции, чтобы преодолеть проблемы различий между компиляторами:
источник
close
функции или методы. Затем, когда вы включаете заголовок этой библиотеки и заголовок с этим макросом, чем у вас есть большая проблема, вы не можете использовать библиотечный API.#ifdef WE_ARE_ON_WIN32
Плз :)Если вы хотите сделать строку из выражения, лучшим примером для этого является
assert
(#x
превращает значениеx
в строку).источник
Строковые константы иногда лучше определить как макросы, поскольку вы можете делать больше с строковыми литералами, чем с
const char *
.Например, строковые литералы могут быть легко объединены .
Если бы
const char *
использовался a, то для выполнения конкатенации во время выполнения пришлось бы использовать какой-то класс строки.источник
Когда вы хотите изменить программный поток (
return
,break
иcontinue
), код в функции ведет себя иначе, чем код, который фактически встроен в функцию.источник
-1
илиNULL
. Таким образом, макрос может значительно сократить стандартный код.Очевидное включает охранников
источник
Вы не можете выполнить короткое замыкание аргументов вызова функции, используя обычный вызов функции. Например:
источник
Допустим, мы будем игнорировать очевидные вещи, такие как охрана заголовков.
Иногда вы хотите сгенерировать код, который должен быть скопирован / вставлен прекомпилятором:
что позволяет вам кодировать это:
И может генерировать сообщения, такие как:
Обратите внимание, что смешивание шаблонов с макросами может привести к еще лучшим результатам (т.е. автоматически генерировать значения рядом с именами переменных)
В других случаях вам нужны __FILE__ и / или __LINE__ некоторого кода, например, для генерации отладочной информации. Вот классика для Visual C ++:
Как и в следующем коде:
он генерирует сообщения вроде:
В других случаях вам нужно сгенерировать код с помощью операторов конкатенации # и ##, таких как генерация геттеров и сеттеров для свойства (это в довольно ограниченных случаях).
В других случаях вы будете генерировать код, который не будет компилироваться, если он используется через функцию, например:
Который можно использовать как
( до сих пор я видел только этот вид кода правильно используется один раз )
Последнее, но не менее важное, знаменитый
boost::foreach
!!!(Примечание: копия кода / вставлена с домашней страницы Boost)
Что (ИМХО) намного лучше, чем
std::for_each
.Таким образом, макросы всегда полезны, потому что они находятся за пределами нормальных правил компилятора. Но я обнаружил, что в большинстве случаев они фактически остаются остатками кода C, никогда не переводимого в надлежащий C ++.
источник
#include <sstream> #include <iostream> using namespace std; void trace(char const * file, int line, ostream & o) { cerr<<file<<":"<<line<<": "<< static_cast<ostringstream & >(o).str().c_str()<<endl; } struct Oss { ostringstream s; ostringstream & lval() { return s; } }; #define TRACE(ostreamstuff) trace(__FILE__, __LINE__, Oss().lval()<<ostreamstuff) int main() { TRACE("Hello " << 123); return 0; }
образом, макрос намного короче.Фреймворки модульного тестирования для C ++, такие как UnitTest ++, в значительной степени вращаются вокруг макросов препроцессора. Несколько строк кода модульного теста расширяются в иерархию классов, которые было бы неудобно печатать вручную. Без чего-то вроде UnitTest ++ и его волшебства препроцессора, я не знаю, как бы вы эффективно написали модульные тесты для C ++.
источник
Бояться препроцессора С - это все равно, что бояться ламп накаливания только потому, что мы получаем люминесцентные лампы. Да, первое может быть {электричество | время программиста} неэффективно. Да, вы можете (буквально) сжечь их. Но они могут выполнить работу, если вы правильно с ней справитесь.
Когда вы программируете встраиваемые системы, C обычно является единственным вариантом, кроме ассемблера форм. После программирования на настольном компьютере с использованием C ++, а затем перехода к более мелким встроенным целям вы научитесь перестать беспокоиться о «неэффективности» стольких голых функций C (включая макросы) и просто попытаетесь найти лучшее и безопасное использование, которое вы можете получить из этих функции.
Александр Степанов говорит :
источник
Мы используем
__FILE__
и__LINE__
макросы макросы в диагностических целях для обработки, обработки и регистрации исключений, а также для автоматических сканеров файлов журналов в нашей инфраструктуре контроля качества.Например, бросающий макрос
OUR_OWN_THROW
может использоваться с типом исключения и параметрами конструктора для этого исключения, включая текстовое описание. Как это:Этот макрос, конечно, сгенерирует
InvalidOperationException
исключение с описанием в качестве параметра конструктора, но он также запишет сообщение в файл журнала, состоящий из имени файла и номера строки, где произошел выброс, и его текстового описания. Сгенерированное исключение получит идентификатор, который также будет зарегистрирован. Если исключение когда-либо обнаруживается где-то еще в коде, оно будет помечено как таковое, и в файле журнала будет указано, что это конкретное исключение было обработано и, следовательно, оно вряд ли является причиной какого-либо сбоя, который может быть зарегистрирован позже. Необработанные исключения могут быть легко обнаружены нашей автоматизированной инфраструктурой контроля качества.источник
Повторение кода.
Взгляните, чтобы увеличить библиотеку препроцессора , это своего рода мета-метапрограммирование. В теме-> мотивация вы можете найти хороший пример.
источник
Некоторые очень продвинутые и полезные вещи все еще могут быть собраны с использованием препроцессора (макросы), что вы никогда не сможете сделать с помощью «языковых конструкций» c ++, включая шаблоны.
Примеры:
Создание чего-то и идентификатора C, и строки
Простой способ использовать переменные типов enum в виде строки в C
Boost Препроцессор Метапрограммирование
источник
stdio.h
иsal.h
запишитеvc12
для лучшего понимания.Я иногда использую макросы, чтобы определить информацию в одном месте, но по-разному использовать ее в разных частях кода. Это только немного зла :)
Например, в "field_list.h":
Тогда для публичного перечисления можно определить просто использование имени:
А в частной функции инициализации все поля можно использовать для заполнения таблицы данными:
источник
Одним из распространенных применений является обнаружение среды компиляции. Для кросс-платформенной разработки вы можете написать, например, один набор кода для linux, а другой - для окон, когда для ваших целей не существует кросс-платформенной библиотеки.
Итак, в грубом примере кросс-платформенный мьютекс может иметь
Для функций они полезны, когда вы хотите явно игнорировать безопасность типов. Например, множество примеров выше и ниже для выполнения ASSERT. Конечно, как и многие функции C / C ++, вы можете выстрелить себе в ногу, но язык дает вам инструменты и позволяет вам решать, что делать.
источник
Что-то вроде
Так что вы можете просто, например, иметь
и получите имя исходного файла и номер строки проблемы, распечатанной в вашем журнале, если n равно false.
Если вы используете обычный вызов функции, такой как
вместо макроса все, что вы можете получить, это номер строки вашей функции assert, напечатанный в журнале, что было бы менее полезно.
источник
<cassert>
вassert()
макросе, который выгружает / линию / данные функции файла? (во всех реализациях, которые я видел, во всяком случае)В отличие от «предпочтительного» шаблонного решения, обсуждаемого в текущем потоке, вы можете использовать его как константное выражение:
источник
template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
Вы можете использовать #defines для помощи в сценариях отладки и модульного тестирования. Например, создайте специальные варианты регистрации функций памяти и создайте специальный memlog_preinclude.h:
Скомпилируйте ваш код, используя:
Ссылка в вашем memlog.o на окончательное изображение. Теперь вы управляете malloc и т. Д., Возможно, для целей ведения журнала или для имитации сбоев размещения для модульных тестов.
источник
Когда вы принимаете решение во время компиляции по конкретному поведению компилятора / ОС / оборудования.
Это позволяет вам сделать ваш интерфейс со спецификой Comppiler / OS / Hardware.
источник
Я использую макросы, чтобы легко определить исключения:
где DEF_EXCEPTION это
источник
Компиляторы могут отклонить ваш запрос в строке.
Макросы всегда будут на своем месте.
Что-то, что я считаю полезным, это #define DEBUG для трассировки отладки - вы можете оставить его 1 во время отладки проблемы (или даже оставить его включенным в течение всего цикла разработки), а затем выключить его, когда придет время доставки.
источник
На моей последней работе я работал над антивирусом. Чтобы мне было легче отлаживать, у меня было повсеместно много журналирования, но в таком приложении с высоким спросом затраты на вызов функции просто слишком дороги. Итак, я придумал этот маленький макрос, который все еще позволял мне включать ведение журнала отладки в версии выпуска на сайте клиента, без затрат на вызов функции, проверял бы флаг отладки и просто возвращался без регистрации чего-либо, или, если он был включен , будет делать запись ... Макрос был определен следующим образом:
Из-за VA_ARGS в функциях журнала это был хороший случай для такого макроса.
До этого я использовал макрос в приложении с высоким уровнем безопасности, который должен был сказать пользователю, что у него нет правильного доступа, и он сообщал бы им, какой флаг ему нужен.
Макрос (ы) определены как:
Затем мы можем просто распределить проверки по всему пользовательскому интерфейсу, и он скажет вам, каким ролям разрешено выполнять действие, которое вы пытались выполнить, если у вас еще не было этой роли. Причиной для двух из них было возвращение значения в некоторых местах и возврат из функции void в других ...
В любом случае, вот как я их использовал, и я не уверен, как это могло бы помочь с шаблонами ... Кроме этого, я стараюсь избегать их, если ДЕЙСТВИТЕЛЬНО не требуется.
источник
Еще один макрос foreach. T: тип, c: контейнер, i: итератор
Использование (показ концепции, не реально):
Доступны лучшие реализации: Google "BOOST_FOREACH"
Хорошие доступные статьи: Условная любовь: FOREACH Redux (Эрик Ниблер) http://www.artima.com/cppsource/foreach.html
источник
Может быть, лучшее использование макросов находится в независящей от платформы разработке. Подумайте о случаях несоответствия типов - с помощью макросов вы можете просто использовать разные заголовочные файлы, например: --WIN_TYPES.H
--POSIX_TYPES.h
--program.h
На мой взгляд, гораздо удобнее, чем реализовывать это другими способами.
источник
Кажется, что VA_ARGS упоминается только косвенно:
При написании универсального кода C ++ 03, и вам нужно переменное количество (универсальных) параметров, вы можете использовать макрос вместо шаблона.
Примечание: в общем, проверка имени / броска также может быть включена в гипотетический
get_op_from_name
функцию. Это всего лишь пример. Может быть другой общий код, окружающий вызов VA_ARGS.Как только мы получим шаблоны с переменным числом символов в C ++ 11, мы сможем решить это «правильно» с помощью шаблона.
источник
Я думаю, что этот трюк - умное использование препроцессора, которое нельзя эмулировать с помощью функции:
Тогда вы можете использовать это так:
Вы также можете определить макрос RELEASE_ONLY.
источник
Вы можете использовать
#define
константы в командной строке компилятора с помощью опции-D
или/D
. Это часто полезно при кросс-компиляции одного и того же программного обеспечения для нескольких платформ, потому что вы можете сделать так, чтобы ваши make-файлы управляли тем, какие константы определены для каждой платформы.источник