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


177

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

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

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

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

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

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

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


Однажды я взял приложение C ++, полное макросов, на сборку которого ушло 45 минут, заменил макросы встроенными функциями и сократил сборку до менее чем 15 минут.
— обратный порядок байт


Поток касается контекстов, в которых полезны макросы, а не контекстов, в которых они неоптимальны.
— underscore_d

Ответы:


123

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

#ifdef ( DEBUG )
#define M_DebugLog( msg )  std::cout << __FILE__ << ":" << __LINE__ << ": " << msg
#else
#define M_DebugLog( msg )
#endif

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 .


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. В коде вашего конкурента.

Нужна помощь в реализации № 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

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 ++.


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 возникают, когда они сами не согласуются с их собственной логикой.


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

9

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

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

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

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


9

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

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


Я почти во всех, если не во всех случаях, повторений кода можно избежать с помощью вызовов функций.
— 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;\
    };\
  }\

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;
   }
...

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


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


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

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


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-файлы управляли тем, какие константы определены для каждой платформы.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.