Когда макросы C++ выгодны? [закрытый]

Когда вы объявляете ссылочную переменную (т. е. объект), вы действительно создаете указатель на объект. Рассмотрим следующий код, в котором вы объявляете переменную примитивного типа int:

int x;
x = 10;

В этом примере переменная x является int, и Java инициализирует ее для 0. Когда вы назначаете его 10 во второй строке, ваше значение 10 записывается в ячейку памяти, на которую указывает x.

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

Integer num;
num = new Integer(10);

Первая строка объявляет переменную с именем num, но она не содержит примитивного значения. Вместо этого он содержит указатель (потому что тип Integer является ссылочным типом). Поскольку вы еще не указали, что указать на Java, он устанавливает значение null, что означает «Я ничего не указываю».

Во второй строке ключевое слово new используется для создания экземпляра (или создания ) объекту типа Integer и переменной указателя num присваивается этот объект. Теперь вы можете ссылаться на объект, используя оператор разыменования . (точка).

Exception, о котором вы просили, возникает, когда вы объявляете переменную, но не создавали объект. Если вы попытаетесь разыменовать num. Перед созданием объекта вы получите NullPointerException. В самых тривиальных случаях компилятор поймает проблему и сообщит вам, что «num не может быть инициализирован», но иногда вы пишете код, который непосредственно не создает объект.

Например, вы можете имеют следующий метод:

public void doSomething(SomeObject obj) {
   //do something to obj
}

В этом случае вы не создаете объект obj, скорее предполагая, что он был создан до вызова метода doSomething. К сожалению, этот метод можно вызвать следующим образом:

doSomething(null);

В этом случае obj имеет значение null. Если метод предназначен для того, чтобы что-то сделать для переданного объекта, целесообразно бросить NullPointerException, потому что это ошибка программиста, и программисту понадобится эта информация для целей отладки.

Альтернативно, там могут быть случаи, когда цель метода заключается не только в том, чтобы работать с переданным в объекте, и поэтому нулевой параметр может быть приемлемым. В этом случае вам нужно будет проверить нулевой параметр и вести себя по-другому. Вы также должны объяснить это в документации. Например, doSomething может быть записано как:

/**
  * @param obj An optional foo for ____. May be null, in which case 
  *  the result will be ____.
  */
public void doSomething(SomeObject obj) {
    if(obj != null) {
       //do something
    } else {
       //do something else
    }
}

Наконец, Как определить исключение & amp; причина использования Трассировки стека

171
задан 8 revs, 4 users 75% 3 June 2016 в 17:28
поделиться

33 ответа

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

#ifdef ( DEBUG )
#define M_DebugLog( msg )  std::cout << __FILE__ << ":" << __LINE__ << ": " << msg
#else
#define M_DebugLog( msg )
#endif
120
ответ дан 3 revs, 3 users 88% 23 November 2019 в 20:41
поделиться

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

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

Что-то я нахожу полезными, ОТЛАДКА #define для трассировки отладки - можно оставить его 1 при отладке проблемы (или даже уехать, это на во время целого цикла разработки) тогда выключают его, когда пора поставляться.

2
ответ дан unwieldy 23 November 2019 в 20:41
поделиться

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

Это позволяет Вам делать свой интерфейс к 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
2
ответ дан 2 revs 23 November 2019 в 20:41
поделиться

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

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

1
ответ дан Mathieu Pagé 23 November 2019 в 20:41
поделиться

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

#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))

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

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
ответ дан 3 revs, 2 users 93% 23 November 2019 в 20:41
поделиться

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

1
ответ дан bk1e 23 November 2019 в 20:41
поделиться

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

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

Компиляция Вы кодируете использование:

gcc -Imemlog_preinclude.h ...

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

3
ответ дан Andrew Johnson 23 November 2019 в 20:41
поделиться
#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])

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

char src[23];
int dest[ARRAY_SIZE(src)];
4
ответ дан 2 revs 23 November 2019 в 20:41
поделиться

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

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

И использование это как таким образом:

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

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

93
ответ дан 2 revs, 2 users 88% 23 November 2019 в 20:41
поделиться

Что-то как

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

Так, чтобы Вы могли просто, например, иметь

assert(n == true);

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

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

void assert(bool val);

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

6
ответ дан Keshi 23 November 2019 в 20:41
поделиться

Главным образом:

  1. Включают защиту
  2. Условная компиляция
  3. Создание отчетов (предопределенные макросы как __LINE__ и __FILE__)
  4. (редко) Копирующие повторяющиеся кодовые комбинации.
  5. В коде Вашего конкурента.
51
ответ дан 2 revs, 2 users 92% 23 November 2019 в 20:41
поделиться

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

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

там какие-либо другие ситуации то преимущество от макросов? ДА!!!

Одно место я использую макросы, с очень повторяющимся кодом. Например, когда обертывание 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__); \
}

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

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

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

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

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

59
ответ дан 4 revs, 4 users 96% 23 November 2019 в 20:41
поделиться

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

#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
50
ответ дан Andrew Stein 23 November 2019 в 20:41
поделиться

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

#define ASSERT_THROW(condition) \
if (!(condition)) \
     throw std::exception(#condition " is false");
38
ответ дан 3 revs 23 November 2019 в 20:41
поделиться

Строковые константы иногда лучше определяются как макросы, так как можно сделать больше со строковыми литералами, чем с 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 * использовались тогда, своего рода строковый класс должен был бы использоваться для выполнения конкатенации во времени выполнения:

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);
31
ответ дан 2 revs 23 November 2019 в 20:41
поделиться

Когда Вы хотите изменить процесс выполнения программы (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.
24
ответ дан 2 revs 23 November 2019 в 20:41
поделиться

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

#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
17
ответ дан 2 revs 23 November 2019 в 20:41
поделиться

Очевидные включают защиту

#ifndef MYHEADER_H
#define MYHEADER_H

...

#endif
20
ответ дан Kena 23 November 2019 в 20:41
поделиться

Скажем, мы проигнорируем очевидные вещи как защита заголовка.

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

#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"

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

Другие времена, Вам нужно __ ФАЙЛ __ и/или __ СТРОКА __ некоторого кода, для генерации информации об отладке, например. Следующее является классиком для 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

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

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

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

, Который может использоваться в качестве [1 122]

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

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

, Который является (по моему скромному мнению), путем лучше, чем [1 110].

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

16
ответ дан 4 revs, 2 users 97% 23 November 2019 в 20:41
поделиться

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

16
ответ дан 3 revs, 2 users 88% 23 November 2019 в 20:41
поделиться

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

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

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

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

9
ответ дан Johann Gerell 23 November 2019 в 20:41
поделиться

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

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

Alexander Stepanov говорит :

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

14
ответ дан VictorH 23 November 2019 в 20:41
поделиться

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

, Например, в "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;

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

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

#include "field_list.h"
7
ответ дан Andrew Johnson 23 November 2019 в 20:41
поделиться

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

Примеры:

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

Простой способ использовать переменные перечислимых типов как строка в Метапрограммировании Препроцессора Повышения C

9
ответ дан 3 revs 23 November 2019 в 20:41
поделиться

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

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

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

Для функций, они полезны, когда Вы хотите явно проигнорировать безопасность типов. Такой, поскольку много примеров выше и ниже для того, чтобы сделать УТВЕРЖДАЮТ. Конечно, как много функций C/C++ можно выстрелить себе в ногу, но язык дает Вам инструменты и позволяет Вам решить, что сделать.

6
ответ дан 2 revs 23 November 2019 в 20:41
поделиться

Еще один макрос foreach. T: напечатать, c: контейнер, я: использование iterator

#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»

Good: Условная Любовь: Возвращение FOREACH (Эрик Ниблер) http://www.artima.com/cppsource/foreach.html

2
ответ дан 23 November 2019 в 20:41
поделиться

Может быть, больше всего макросов используется при разработке, не зависящей от платформы. Подумайте о случаях несогласованности типов - с макросами вы можете просто использовать разные файлы заголовков, например: - 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
ответ дан 23 November 2019 в 20:41
поделиться

Вам нужны макросы для идентификаторов ресурсов в Visual Studio, поскольку компилятор ресурса только понимает их (т.е. это не работает с константой или перечислением).

0
ответ дан Harold Ekstrom 23 November 2019 в 20:41
поделиться

Можно ли реализовать это как подставляемую функцию?

#define my_free(x) do { free(x); x = NULL; } while (0)
0
ответ дан mbac32768 23 November 2019 в 20:41
поделиться

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

void Log::trace(const char *pszMsg) {
    if (!bDebugBuild) {
        return;
    }
    // Do the logging
}

...

log.trace("Inside MyFunction");

Вы можете иметь:

#ifdef _DEBUG
#define LOG_TRACE log.trace
#else
#define LOG_TRACE void
#endif

...

LOG_TRACE("Inside MyFunction");

, Когда _DEBUG не будет определен, это не генерирует кода вообще. Ваша программа будет работать быстрее, и текст для входа трассировки не будет скомпилирован в Ваш исполняемый файл.

0
ответ дан Ates Goral 23 November 2019 в 20:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: