Когда полезны макросы C ++? [закрыто]

177

C препроцессор оправданно боялся и избегал сообщества C ++. Встроенные функции, константы и шаблоны обычно являются более безопасной и превосходной альтернативой #define.

Следующий макрос:

#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)  

ни в коем случае не превосходит тип safe:

inline bool succeeded(int hr) { return hr >= 0; }

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

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

Motti
источник
Однажды я взял приложение C ++, полное макросов, на сборку которого ушло 45 минут, заменил макросы встроенными функциями и сократил сборку до менее чем 15 минут.
обратный порядок байт
Static Assert
Озгюр
Поток касается контекстов, в которых полезны макросы, а не контекстов, в которых они неоптимальны.
underscore_d

Ответы:

123

В качестве обёрток для функций отладки, для автоматической передачи таких вещей, как __FILE__, __LINE__и т.д:

#ifdef ( DEBUG )
#define M_DebugLog( msg )  std::cout << __FILE__ << ":" << __LINE__ << ": " << msg
#else
#define M_DebugLog( msg )
#endif
Frank Szczerba
источник
14
На самом деле исходный фрагмент: << FILE ":" << в порядке, FILE генерирует строковую константу, которая будет объединена препроцессором с ":" в одну строку.
Франк Щерба
12
Это требует только препроцессора, потому что __FILE__и __LINE__ также требует препроцессора. Использование их в вашем коде похоже на вектор заражения препроцессора.
TED
93

Методы всегда должны быть полным, компилируемым кодом; Макросы могут быть фрагментами кода. Таким образом, вы можете определить макрос foreach:

#define foreach(list, index) for(index = 0; index < list.size(); index++)

И используйте это так:

foreach(cookies, i)
    printf("Cookie: %s", cookies[i]);

Начиная с C ++ 11, это заменяется основанным на диапазоне циклом for .

jdmichal
источник
6
+1 Если вы используете какой-то смехотворно сложный синтаксис итератора, написание макроса в стиле foreach может значительно облегчить чтение и поддержку вашего кода. Я сделал это, это работает.
постфутурист
9
Большинство комментариев совершенно не имеют отношения к тому, что макросы могут быть фрагментами кода, а не полным кодом. Но спасибо за придирки.
jdmichal
12
Это C, а не C ++. Если вы делаете C ++, вы должны использовать итераторы и std :: for_each.
chrish
20
Я не согласен, Криш. До лямбды это for_eachбыло неприятно, потому что код, через который проходил каждый элемент, не был локальным по отношению к точке вызова. foreach, (и я настоятельно рекомендую BOOST_FOREACHвместо ручного решения) давайте держать код близко к сайту итерации, делая его более читабельным. Тем не менее, как только лямбда выкатится, for_eachснова может быть путь.
GManNickG
8
И стоит отметить, что BOOST_FOREACH сам по себе является макросом (но очень хорошо продуманным)
Тайлер МакГенри,
59

Защита заголовочных файлов требует макросов.

Есть ли другие области, которые требуют макросов? Не много (если есть).

Существуют ли другие ситуации, в которых полезны макросы? ДА!!!

Одним из мест, где я использую макросы, является очень повторяющийся код. Например, при переносе кода C ++ для использования с другими интерфейсами (.NET, COM, Python и т. Д.) Мне нужно отлавливать различные типы исключений. Вот как я это делаю:

#define HANDLE_EXCEPTIONS \
catch (::mylib::exception& e) { \
    throw gcnew MyDotNetLib::Exception(e); \
} \
catch (::std::exception& e) { \
    throw gcnew MyDotNetLib::Exception(e, __LINE__, __FILE__); \
} \
catch (...) { \
    throw gcnew MyDotNetLib::UnknownException(__LINE__, __FILE__); \
}

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

void Foo()
{
    try {
        ::mylib::Foo()
    }
    HANDLE_EXCEPTIONS
}

Это также облегчает обслуживание. Если мне когда-нибудь понадобится добавить новый тип исключения, мне нужно будет добавить только одно место.

Есть и другие полезные примеры: многие из них включают макросы __FILE__и __LINE__препроцессор.

В любом случае, макросы очень полезны при правильном использовании. Макросы не являются злом - их неправильное использование является злом.

Кевин
источник
7
Большинство компиляторов поддерживают #pragma onceэти дни, поэтому я сомневаюсь, что охрана действительно необходима
1800 ИНФОРМАЦИЯ
13
Они, если вы пишете для всех компиляторов, а не только для большинства ;-)
Стив Джессоп
30
Таким образом, вместо переносимой стандартной функциональности препроцессора, вы рекомендуете использовать расширение препроцессора, чтобы избежать использования препроцессора? Кажется смешным для меня.
Логан Капальдо
#pragma onceломается на многих распространенных системах сборки.
Майлз Рут
4
Существует решение для того , что не требует макросов: void handleExceptions(){ try { throw } catch (::mylib::exception& e) {....} catch (::std::exception& e) {...} ... }. И на стороне функции:void Foo(){ try {::mylib::Foo() } catch (...) {handleExceptions(); } }
MikeMB
51

В основном:

  1. Включить охранников
  2. Условная компиляция
  3. Отчетность (предопределенные макросы, такие как __LINE__и __FILE__)
  4. (редко) Дублирование повторяющихся шаблонов кода.
  5. В коде вашего конкурента.
David Thornley
источник
Нужна помощь в реализации № 5. Можете ли вы привести меня к решению?
Макс
50

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

#ifdef ARE_WE_ON_WIN32
#define close(parm1)            _close (parm1)
#define rmdir(parm1)            _rmdir (parm1)
#define mkdir(parm1, parm2)     _mkdir (parm1)
#define access(parm1, parm2)    _access(parm1, parm2)
#define create(parm1, parm2)    _creat (parm1, parm2)
#define unlink(parm1)           _unlink(parm1)
#endif
Эндрю Стейн
источник
12
В C ++ то же самое можно получить с помощью встроенных функций: <code> #ifdef ARE_WE_ON_WIN32 <br> inline int close (int i) {return _close (i); } <br> #endif </ code>
paercebal
2
Это удаляет # define, но не #ifdef и #endif. Во всяком случае, я согласен с вами.
Горпик
19
НИКОГДА не определяйте строчные макросы. Макросы для изменения функций - мой кошмар (спасибо Microsoft). Лучший пример в первой строке. Многие библиотеки имеют closeфункции или методы. Затем, когда вы включаете заголовок этой библиотеки и заголовок с этим макросом, чем у вас есть большая проблема, вы не можете использовать библиотечный API.
Марек Р.
AndrewStein, видите ли вы какую-либо пользу от использования макросов в этом контексте по сравнению с предложением @ paercebal? Если нет, то кажется, что макросы на самом деле бесплатны.
einpoklum
1
#ifdef WE_ARE_ON_WIN32Плз :)
Гонки
38

Если вы хотите сделать строку из выражения, лучшим примером для этого является assert( #xпревращает значение xв строку).

#define ASSERT_THROW(condition) \
if (!(condition)) \
     throw std::exception(#condition " is false");
Мотти
источник
5
Просто придирка, но я бы лично оставил точку с запятой в выключенном состоянии.
Майкл Майерс
10
Я согласен, на самом деле я бы поставил его в do {} while (false) (чтобы предотвратить еще один хищник), но я хотел, чтобы все было просто.
Motti
33

Строковые константы иногда лучше определить как макросы, поскольку вы можете делать больше с строковыми литералами, чем с const char *.

Например, строковые литералы могут быть легко объединены .

#define BASE_HKEY "Software\\Microsoft\\Internet Explorer\\"
// Now we can concat with other literals
RegOpenKey(HKEY_CURRENT_USER, BASE_HKEY "Settings", &settings);
RegOpenKey(HKEY_CURRENT_USER, BASE_HKEY "TypedURLs", &URLs);

Если бы const char *использовался a, то для выполнения конкатенации во время выполнения пришлось бы использовать какой-то класс строки.

const char* BaseHkey = "Software\\Microsoft\\Internet Explorer\\";
RegOpenKey(HKEY_CURRENT_USER, (string(BaseHkey) + "Settings").c_str(), &settings);
RegOpenKey(HKEY_CURRENT_USER, (string(BaseHkey) + "TypedURLs").c_str(), &URLs);
Мотти
источник
2
В C ++ 11 я бы посчитал, что это самая важная часть (кроме включения охраны). Макросы - действительно лучшее, что у нас есть для обработки строк во время компиляции. Это особенность, которую, я надеюсь, мы получим в C ++ 11 ++
Дэвид Стоун
1
Это ситуация, которая привела меня к желанию макросов в C #.
Роулинг
2
Я хотел бы +42 это. Очень важный, хотя и не часто запоминающийся аспект строковых литералов.
Даниэль Камил Козар
24

Когда вы хотите изменить программный поток ( return, breakи continue), код в функции ведет себя иначе, чем код, который фактически встроен в функцию.

#define ASSERT_RETURN(condition, ret_val) \
if (!(condition)) { \
    assert(false && #condition); \
    return ret_val; }

// should really be in a do { } while(false) but that's another discussion.
Мотти
источник
Бросок исключения кажется мне лучшей альтернативой.
einpoklum
При написании расширений Python C (++) исключения распространяются путем установки строки исключения, а затем возврата -1или NULL. Таким образом, макрос может значительно сократить стандартный код.
black_puppydog
20

Очевидное включает охранников

#ifndef MYHEADER_H
#define MYHEADER_H

...

#endif
Кена
источник
17

Вы не можете выполнить короткое замыкание аргументов вызова функции, используя обычный вызов функции. Например:

#define andm(a, b) (a) && (b)

bool andf(bool a, bool b) { return a && b; }

andm(x, y) // short circuits the operator so if x is false, y would not be evaluated
andf(x, y) // y will always be evaluated
1800 ИНФОРМАЦИЯ
источник
3
Возможно, более общий момент: функции оценивают свои аргументы ровно один раз. Макросы могут оценивать аргументы больше или меньше раз.
Стив Джессоп
@ [Greg Rogers] все, что делает макропроцессор - это заменяет текст. Как только вы это поймете, в этом не должно быть больше тайны.
18:00 ИНФОРМАЦИЯ
Вы можете получить эквивалентное поведение, задавая шаблон andf вместо принудительного выполнения вычисления до вызова функции. Я бы не понял, что ты сказал, правда, если бы не попробовал это для себя. Интересный.
Грег Роджерс
Как именно вы можете сделать это с помощью шаблона?
18:00 ИНФОРМАЦИЯ
6
Сокрытие операций короткого замыкания за макросом стиля функции - это одна из вещей, которую я действительно не хочу видеть в производственном коде.
MikeMB
17

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

Иногда вы хотите сгенерировать код, который должен быть скопирован / вставлен прекомпилятором:

#define RAISE_ERROR_STL(p_strMessage)                                          \
do                                                                             \
{                                                                              \
   try                                                                         \
   {                                                                           \
      std::tstringstream strBuffer ;                                           \
      strBuffer << p_strMessage ;                                              \
      strMessage = strBuffer.str() ;                                           \
      raiseSomeAlert(__FILE__, __FUNCSIG__, __LINE__, strBuffer.str().c_str()) \
   }                                                                           \
   catch(...){}                                                                \
   {                                                                           \
   }                                                                           \
}                                                                              \
while(false)

что позволяет вам кодировать это:

RAISE_ERROR_STL("Hello... The following values " << i << " and " << j << " are wrong") ;

И может генерировать сообщения, такие как:

Error Raised:
====================================
File : MyFile.cpp, line 225
Function : MyFunction(int, double)
Message : "Hello... The following values 23 and 12 are wrong"

Обратите внимание, что смешивание шаблонов с макросами может привести к еще лучшим результатам (т.е. автоматически генерировать значения рядом с именами переменных)

В других случаях вам нужны __FILE__ и / или __LINE__ некоторого кода, например, для генерации отладочной информации. Вот классика для Visual C ++:

#define WRNG_PRIVATE_STR2(z) #z
#define WRNG_PRIVATE_STR1(x) WRNG_PRIVATE_STR2(x)
#define WRNG __FILE__ "("WRNG_PRIVATE_STR1(__LINE__)") : ------------ : "

Как и в следующем коде:

#pragma message(WRNG "Hello World")

он генерирует сообщения вроде:

C:\my_project\my_cpp_file.cpp (225) : ------------ Hello World

В других случаях вам нужно сгенерировать код с помощью операторов конкатенации # и ##, таких как генерация геттеров и сеттеров для свойства (это в довольно ограниченных случаях).

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

#define MY_TRY      try{
#define MY_CATCH    } catch(...) {
#define MY_END_TRY  }

Который можно использовать как

MY_TRY
   doSomethingDangerous() ;
MY_CATCH
   tryToRecoverEvenWithoutMeaningfullInfo() ;
   damnThoseMacros() ;
MY_END_TRY

( до сих пор я видел только этот вид кода правильно используется один раз )

Последнее, но не менее важное, знаменитый boost::foreach!!!

#include <string>
#include <iostream>
#include <boost/foreach.hpp>

int main()
{
    std::string hello( "Hello, world!" );

    BOOST_FOREACH( char ch, hello )
    {
        std::cout << ch;
    }

    return 0;
}

(Примечание: копия кода / вставлена ​​с домашней страницы Boost)

Что (ИМХО) намного лучше, чем std::for_each.

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

paercebal
источник
1
Используйте CPP только для того, что компилятор не может сделать. Например, RAISE_ERROR_STL должен использовать CPP только для определения файла, строки и сигнатуры функции и передавать их функции (возможно, встроенной), которая делает все остальное.
Райнер Блум
Пожалуйста, обновите свой ответ, чтобы отразить C ++ 11 и адрес @ комментарий RainerBlome.
einpoklum
@RainerBlome: мы согласны. Макрос RAISE_ERROR_STL является пред-C ++ 11, поэтому в этом контексте он полностью оправдан. Насколько я понимаю (но у меня никогда не было возможности разобраться с этими специфическими функциями), вы можете использовать вариационные шаблоны (или макросы?) В Modern C ++ для более элегантного решения проблемы.
paercebal
@einpoklum: «Пожалуйста, обновите свой ответ, чтобы отразить C ++ 11 и адрес комментария RainerBlome» Нет :-). , , Я полагаю, что в лучшем случае я добавлю раздел для Modern C ++ с альтернативными реализациями, уменьшающими или устраняющими необходимость в макросах, но суть в том, что макросы ужасны и злы, но когда вам нужно что-то делать, компилятор не понимает Вы делаете это с помощью макросов.
paercebal
Даже в C ++ 11 многое из того, что делает ваш макрос, можно оставить для функции: таким #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; }образом, макрос намного короче.
Райнер Блум
16

Фреймворки модульного тестирования для C ++, такие как UnitTest ++, в значительной степени вращаются вокруг макросов препроцессора. Несколько строк кода модульного теста расширяются в иерархию классов, которые было бы неудобно печатать вручную. Без чего-то вроде UnitTest ++ и его волшебства препроцессора, я не знаю, как бы вы эффективно написали модульные тесты для C ++.

Джо
источник
Унитестесты прекрасно можно писать без рамок. В конце концов, это зависит только от того, какой результат вы хотите получить. Если вам все равно, простое значение выхода, указывающее на успех или неудачу, должно быть в порядке.
яснее
15

Бояться препроцессора С - это все равно, что бояться ламп накаливания только потому, что мы получаем люминесцентные лампы. Да, первое может быть {электричество | время программиста} неэффективно. Да, вы можете (буквально) сжечь их. Но они могут выполнить работу, если вы правильно с ней справитесь.

Когда вы программируете встраиваемые системы, C обычно является единственным вариантом, кроме ассемблера форм. После программирования на настольном компьютере с использованием C ++, а затем перехода к более мелким встроенным целям вы научитесь перестать беспокоиться о «неэффективности» стольких голых функций C (включая макросы) и просто попытаетесь найти лучшее и безопасное использование, которое вы можете получить из этих функции.

Александр Степанов говорит :

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

VictorH
источник
Я думаю, что это неправильное отношение. То, что вы можете научиться «правильно с этим обращаться», не означает, что это стоит чьего-то времени и усилий.
Нил Г
9

Мы используем __FILE__и__LINE__ макросы макросы в диагностических целях для обработки, обработки и регистрации исключений, а также для автоматических сканеров файлов журналов в нашей инфраструктуре контроля качества.

Например, бросающий макрос OUR_OWN_THROWможет использоваться с типом исключения и параметрами конструктора для этого исключения, включая текстовое описание. Как это:

OUR_OWN_THROW(InvalidOperationException, (L"Uninitialized foo!"));

Этот макрос, конечно, сгенерирует InvalidOperationExceptionисключение с описанием в качестве параметра конструктора, но он также запишет сообщение в файл журнала, состоящий из имени файла и номера строки, где произошел выброс, и его текстового описания. Сгенерированное исключение получит идентификатор, который также будет зарегистрирован. Если исключение когда-либо обнаруживается где-то еще в коде, оно будет помечено как таковое, и в файле журнала будет указано, что это конкретное исключение было обработано и, следовательно, оно вряд ли является причиной какого-либо сбоя, который может быть зарегистрирован позже. Необработанные исключения могут быть легко обнаружены нашей автоматизированной инфраструктурой контроля качества.

Иоганн Герелл
источник
9

Повторение кода.

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

Ruggero Turra
источник
Я почти во всех, если не во всех случаях, повторений кода можно избежать с помощью вызовов функций.
einpoklum
@einpoklum: я не согласен. Посмотрите на ссылку
Ruggero Turra
9

Некоторые очень продвинутые и полезные вещи все еще могут быть собраны с использованием препроцессора (макросы), что вы никогда не сможете сделать с помощью «языковых конструкций» c ++, включая шаблоны.

Примеры:

Создание чего-то и идентификатора C, и строки

Простой способ использовать переменные типов enum в виде строки в C

Boost Препроцессор Метапрограммирование

Сума
источник
Третье звено разрывается FYI
Робин Хартленд
Взгляните stdio.hи sal.hзапишите vc12для лучшего понимания.
Эльшан
7

Я иногда использую макросы, чтобы определить информацию в одном месте, но по-разному использовать ее в разных частях кода. Это только немного зла :)

Например, в "field_list.h":

/*
 * List of fields, names and values.
 */
FIELD(EXAMPLE1, "first example", 10)
FIELD(EXAMPLE2, "second example", 96)
FIELD(ANOTHER, "more stuff", 32)
...
#undef FIELD

Тогда для публичного перечисления можно определить просто использование имени:

#define FIELD(name, desc, value) FIELD_ ## name,

typedef field_ {

#include "field_list.h"

    FIELD_MAX

} field_en;

А в частной функции инициализации все поля можно использовать для заполнения таблицы данными:

#define FIELD(name, desc, value) \
    table[FIELD_ ## name].desc = desc; \
    table[FIELD_ ## name].value = value;

#include "field_list.h"
Эндрю Джонсон
источник
1
Примечание: подобная методика может быть реализована даже без отдельного включения. См .: stackoverflow.com/questions/147267/… stackoverflow.com/questions/126277/…
Suma
6

Одним из распространенных применений является обнаружение среды компиляции. Для кросс-платформенной разработки вы можете написать, например, один набор кода для linux, а другой - для окон, когда для ваших целей не существует кросс-платформенной библиотеки.

Итак, в грубом примере кросс-платформенный мьютекс может иметь

void lock()
{
    #ifdef WIN32
    EnterCriticalSection(...)
    #endif
    #ifdef POSIX
    pthread_mutex_lock(...)
    #endif
}

Для функций они полезны, когда вы хотите явно игнорировать безопасность типов. Например, множество примеров выше и ниже для выполнения ASSERT. Конечно, как и многие функции C / C ++, вы можете выстрелить себе в ногу, но язык дает вам инструменты и позволяет вам решать, что делать.

Дуг Т.
источник
Поскольку спрашивающий спросил: это можно сделать без макросов путем включения разных заголовков через разные пути включения для каждой платформы. Я склонен согласиться с тем, что макросы часто более удобны.
Стив Джессоп
Я второй это. Если вы начнете использовать макросы для этой цели, код может быстро стать менее читабельным
Nemanja Trifunovic,
6

Что-то вроде

void debugAssert(bool val, const char* file, int lineNumber);
#define assert(x) debugAssert(x,__FILE__,__LINE__);

Так что вы можете просто, например, иметь

assert(n == true);

и получите имя исходного файла и номер строки проблемы, распечатанной в вашем журнале, если n равно false.

Если вы используете обычный вызов функции, такой как

void assert(bool val);

вместо макроса все, что вы можете получить, это номер строки вашей функции assert, напечатанный в журнале, что было бы менее полезно.

Кеши
источник
Зачем изобретать велосипед , когда реализация стандартной библиотеки уже обеспечивает с помощью <cassert>в assert()макросе, который выгружает / линию / данные функции файла? (во всех реализациях, которые я видел, во всяком случае)
underscore_d
4
#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])

В отличие от «предпочтительного» шаблонного решения, обсуждаемого в текущем потоке, вы можете использовать его как константное выражение:

char src[23];
int dest[ARRAY_SIZE(src)];
оборота физзер
источник
2
Это можно сделать с помощью шаблонов более безопасным способом (который не будет компилироваться, если передан указатель, а не массив) stackoverflow.com/questions/720077/calculating-size-of-an-array/…
Motti
1
Теперь, когда у нас есть constexpr в C ++ 11, безопасная (не макро) версия также может использоваться в константном выражении. template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
Дэвид Стоун
3

Вы можете использовать #defines для помощи в сценариях отладки и модульного тестирования. Например, создайте специальные варианты регистрации функций памяти и создайте специальный memlog_preinclude.h:

#define malloc memlog_malloc
#define calloc memlog calloc
#define free memlog_free

Скомпилируйте ваш код, используя:

gcc -Imemlog_preinclude.h ...

Ссылка в вашем memlog.o на окончательное изображение. Теперь вы управляете malloc и т. Д., Возможно, для целей ведения журнала или для имитации сбоев размещения для модульных тестов.

Эндрю Джонсон
источник
3

Когда вы принимаете решение во время компиляции по конкретному поведению компилятора / ОС / оборудования.

Это позволяет вам сделать ваш интерфейс со спецификой Comppiler / OS / Hardware.

#if defined(MY_OS1) && defined(MY_HARDWARE1)
#define   MY_ACTION(a,b,c)      doSothing_OS1HW1(a,b,c);}
#elif define(MY_OS1) && defined(MY_HARDWARE2)
#define   MY_ACTION(a,b,c)      doSomthing_OS1HW2(a,b,c);}
#elif define(MY_SUPER_OS)
          /* On this hardware it is a null operation */
#define   MY_ACTION(a,b,c)
#else
#error  "PLEASE DEFINE MY_ACTION() for this Compiler/OS/HArdware configuration"
#endif
оборота Локи Астари
источник
3

Я использую макросы, чтобы легко определить исключения:

DEF_EXCEPTION(RessourceNotFound, "Ressource not found")

где DEF_EXCEPTION это

#define DEF_EXCEPTION(A, B) class A : public exception\
  {\
  public:\
    virtual const char* what() const throw()\
    {\
      return B;\
    };\
  }\
MrBeast
источник
2

Компиляторы могут отклонить ваш запрос в строке.

Макросы всегда будут на своем месте.

Что-то, что я считаю полезным, это #define DEBUG для трассировки отладки - вы можете оставить его 1 во время отладки проблемы (или даже оставить его включенным в течение всего цикла разработки), а затем выключить его, когда придет время доставки.

громоздкий
источник
10
Если компилятор отклоняет ваш запрос на включение, это может иметь очень вескую причину. Хороший компилятор будет лучше встраивать должным образом, чем вы, а плохой даст вам больше проблем с производительностью, чем этот.
Дэвид Торнли
@DavidThornley Или, возможно, это не лучший оптимизирующий компилятор, такой как GCC или CLANG / LLVM. Некоторые компиляторы просто дерьмо.
Майлз Рут
2

На моей последней работе я работал над антивирусом. Чтобы мне было легче отлаживать, у меня было повсеместно много журналирования, но в таком приложении с высоким спросом затраты на вызов функции просто слишком дороги. Итак, я придумал этот маленький макрос, который все еще позволял мне включать ведение журнала отладки в версии выпуска на сайте клиента, без затрат на вызов функции, проверял бы флаг отладки и просто возвращался без регистрации чего-либо, или, если он был включен , будет делать запись ... Макрос был определен следующим образом:

#define dbgmsg(_FORMAT, ...)  if((debugmsg_flag  & 0x00000001) || (debugmsg_flag & 0x80000000))     { log_dbgmsg(_FORMAT, __VA_ARGS__);  }

Из-за VA_ARGS в функциях журнала это был хороший случай для такого макроса.

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

Макрос (ы) определены как:

#define SECURITY_CHECK(lRequiredSecRoles) if(!DoSecurityCheck(lRequiredSecRoles, #lRequiredSecRoles, true)) return
#define SECURITY_CHECK_QUIET(lRequiredSecRoles) (DoSecurityCheck(lRequiredSecRoles, #lRequiredSecRoles, false))

Затем мы можем просто распределить проверки по всему пользовательскому интерфейсу, и он скажет вам, каким ролям разрешено выполнять действие, которое вы пытались выполнить, если у вас еще не было этой роли. Причиной для двух из них было возвращение значения в некоторых местах и ​​возврат из функции void в других ...

SECURITY_CHECK(ROLE_BUSINESS_INFORMATION_STEWARD | ROLE_WORKER_ADMINISTRATOR);

LRESULT CAddPerson1::OnWizardNext() 
{
   if(m_Role.GetItemData(m_Role.GetCurSel()) == parent->ROLE_EMPLOYEE) {
      SECURITY_CHECK(ROLE_WORKER_ADMINISTRATOR | ROLE_BUSINESS_INFORMATION_STEWARD ) -1;
   } else if(m_Role.GetItemData(m_Role.GetCurSel()) == parent->ROLE_CONTINGENT) {
      SECURITY_CHECK(ROLE_CONTINGENT_WORKER_ADMINISTRATOR | ROLE_BUSINESS_INFORMATION_STEWARD | ROLE_WORKER_ADMINISTRATOR) -1;
   }
...

В любом случае, вот как я их использовал, и я не уверен, как это могло бы помочь с шаблонами ... Кроме этого, я стараюсь избегать их, если ДЕЙСТВИТЕЛЬНО не требуется.

LarryF
источник
2

Еще один макрос foreach. T: тип, c: контейнер, i: итератор

#define foreach(T, c, i) for(T::iterator i=(c).begin(); i!=(c).end(); ++i)
#define foreach_const(T, c, i) for(T::const_iterator i=(c).begin(); i!=(c).end(); ++i)

Использование (показ концепции, не реально):

void MultiplyEveryElementInList(std::list<int>& ints, int mul)
{
    foreach(std::list<int>, ints, i)
        (*i) *= mul;
}

int GetSumOfList(const std::list<int>& ints)
{
    int ret = 0;
    foreach_const(std::list<int>, ints, i)
        ret += *i;
    return ret;
}

Доступны лучшие реализации: Google "BOOST_FOREACH"

Хорошие доступные статьи: Условная любовь: FOREACH Redux (Эрик Ниблер) http://www.artima.com/cppsource/foreach.html

Notinlist
источник
2

Может быть, лучшее использование макросов находится в независящей от платформы разработке. Подумайте о случаях несоответствия типов - с помощью макросов вы можете просто использовать разные заголовочные файлы, например: --WIN_TYPES.H

typedef ...some struct

--POSIX_TYPES.h

typedef ...some another struct

--program.h

#ifdef WIN32
#define TYPES_H "WINTYPES.H"
#else 
#define TYPES_H "POSIX_TYPES.H"
#endif

#include TYPES_H

На мой взгляд, гораздо удобнее, чем реализовывать это другими способами.

rkellerm
источник
2

Кажется, что VA_ARGS упоминается только косвенно:

При написании универсального кода C ++ 03, и вам нужно переменное количество (универсальных) параметров, вы можете использовать макрос вместо шаблона.

#define CALL_RETURN_WRAPPER(FnType, FName, ...)          \
  if( FnType theFunction = get_op_from_name(FName) ) {   \
    return theFunction(__VA_ARGS__);                     \
  } else {                                               \
    throw invalid_function_name(FName);                  \
  }                                                      \
/**/

Примечание: в общем, проверка имени / броска также может быть включена в гипотетическийget_op_from_name функцию. Это всего лишь пример. Может быть другой общий код, окружающий вызов VA_ARGS.

Как только мы получим шаблоны с переменным числом символов в C ++ 11, мы сможем решить это «правильно» с помощью шаблона.

Мартин Ба
источник
1

Я думаю, что этот трюк - умное использование препроцессора, которое нельзя эмулировать с помощью функции:

#define COMMENT COMMENT_SLASH(/)
#define COMMENT_SLASH(s) /##s

#if defined _DEBUG
#define DEBUG_ONLY
#else
#define DEBUG_ONLY COMMENT
#endif

Тогда вы можете использовать это так:

cout <<"Hello, World!" <<endl;
DEBUG_ONLY cout <<"This is outputed only in debug mode" <<endl;

Вы также можете определить макрос RELEASE_ONLY.

Матье Паге
источник
2
Этот трюк не работает в соответствии со стандартом. Он пытается создать маркер комментария через препроцессор, но комментарии должны быть удалены перед запуском препроцессора. Соответствующий компилятор вызовет здесь синтаксическую ошибку.
Дэвид Торнли
2
Извините, Дэвид, но компилятор должен содержать вторую копию удаления комментариев.
Джошуа
гораздо проще сделать флаг отладки глобальным const bool и использовать такой код: if (debug) cout << "..."; - нет необходимости в макросах!
Стефан Монов
@ Стефан: Действительно, это то, что я делаю сейчас. Любой достойный компилятор не будет генерировать какой-либо код, если в этом случае отладка ложна.
Матье Паге
1

Вы можете использовать #defineконстанты в командной строке компилятора с помощью опции -Dили /D. Это часто полезно при кросс-компиляции одного и того же программного обеспечения для нескольких платформ, потому что вы можете сделать так, чтобы ваши make-файлы управляли тем, какие константы определены для каждой платформы.

bk1e
источник