Возможно, это вопрос стиля, но в нашей команде разработчиков есть некоторые разногласия, и я подумал, есть ли у кого-нибудь еще какие-нибудь идеи по этому поводу ...
По сути, у нас есть некоторые отладочные операторы печати, которые мы отключаем во время нормальной разработки. Лично я предпочитаю делать следующее:
//---- SomeSourceFile.cpp ----
#define DEBUG_ENABLED (0)
...
SomeFunction()
{
int someVariable = 5;
#if (DEBUG_ENABLED)
printf("Debugging: someVariable == %d", someVariable);
#endif
}
Однако некоторые из команды предпочитают следующее:
// #define DEBUG_ENABLED
...
SomeFunction()
{
int someVariable = 5;
#ifdef DEBUG_ENABLED
printf("Debugging: someVariable == %d", someVariable);
#endif
}
... какой из этих методов вам больше подходит и почему? Я считаю, что первое безопаснее, потому что всегда есть что-то определенное и нет опасности, что это может разрушить другие определения где-то еще.





#if дает вам возможность установить для него значение 0, чтобы отключить эту функцию, но при этом определить, что переключатель присутствует.
Лично у меня всегда #define DEBUG 1, поэтому я могу поймать его с помощью #if или #ifdef
Это не удается, поскольку #define DEBUG = 0 теперь не будет запускаться #if, но будет запускаться #ifdef
В том-то и дело, я могу либо полностью удалить DEBUG, либо просто установить для него значение 0, чтобы отключить его.
это должен быть #define DEBUG 1. Не #define DEBUG=1
Сам предпочитаю:
#if defined(DEBUG_ENABLED)
Поскольку это упрощает создание кода, который ищет противоположное условие, гораздо легче обнаружить:
#if !defined(DEBUG_ENABLED)
против.
#ifndef(DEBUG_ENABLED)
Лично я считаю, что этот восклицательный знак проще пропустить!
С подсветкой синтаксиса? :) При выделении синтаксиса «n» в «ifndef» заметить гораздо труднее, поскольку все они одного цвета.
Хорошо, я имел в виду, что #ifndef легче обнаружить, чем #if! Defined, когда вы сравниваете с #if определенным ... но учитывая все #if defined / # if! Defined vs # ifdef / # ifndef, любой из них одинаково плохо читается!
@JonCage Я знаю, что прошло несколько лет с момента этого комментария, но я хотел бы отметить, что вы можете написать его как #if ! defined, чтобы сделать ! более заметным и его трудно пропустить.
@Pharap - Это определенно похоже на улучшение :)
Первое кажется мне более ясным. Кажется более естественным сделать его флагом по сравнению с определенным / не определенным.
В качестве альтернативы вы можете объявить глобальную константу и использовать C++ if вместо препроцессора #if. Компилятор должен оптимизировать для вас неиспользуемые ветки, и ваш код будет чище.
Вот что говорит Проблемы с C++ Стивена К. Дьюхерста об использовании # if.
Это паршивое решение, оно имеет следующие проблемы: 1. Работает только в функциях, вы не можете удалить ненужные переменные класса и т. д. 2. Компиляторы могут выдавать предупреждения о недоступности кода 3. Код в if все еще нуждается в компиляции, что означает вы должны сохранить все свои функции отладки и т. д.
Сначала вопрос был конкретно об отладке printfs, поэтому ненужные переменные класса здесь не проблема. Во-вторых, учитывая возможности современных компиляторов, вам следует как можно меньше использовать #ifdefs. В большинстве случаев вместо этого вы можете использовать конфигурации сборки или специализации шаблонов.
Я всегда использовал #ifdef и флаги компилятора для его определения ...
По какой-то конкретной причине (из любопытства)?
Честно говоря, я никогда об этом не думал - просто как это делалось там, где я работал. Это дает то преимущество, что вместо того, чтобы вносить изменения в код для производственной сборки, все, что вам нужно сделать, это «сделать DEBUG» для отладки или «сделать PRODUCTION» для обычного
Думаю, это полностью вопрос стиля. Ни у одного из них нет очевидного преимущества перед другим.
Последовательность важнее, чем какой-либо конкретный выбор, поэтому я рекомендую вам собраться вместе со своей командой и выбрать один стиль и придерживаться его.
Оба абсолютно эквивалентны. В идиоматическом использовании #ifdef используется только для проверки определенности (и того, что я бы использовал в вашем примере), тогда как #if используется в более сложных выражениях, таких как #if defined (A) &&! Defined (B).
OP не спрашивал, что лучше между "#ifdef" и "#if defined", а скорее между "# ifdef / # if defined" и "#if".
Использование #if означает, что вы создали макрос «определить», то есть что-то, что будет искать в коде и заменить на «(0)». Это «ад макросов», который я ненавижу видеть в C++, потому что он загрязняет код потенциальными модификациями кода.
Например:
#define MY_MACRO (0)
int doSomething(int p_iValue)
{
return p_iValue + 1 ;
}
int main(int argc, char **argv)
{
int MY_MACRO = 25 ;
doSomething(MY_MACRO) ;
return 0;
}
дает следующую ошибку на g ++:
main.cpp|408|error: lvalue required as left operand of assignment|
||=== Build finished: 1 errors, 0 warnings ===|
Только ошибка один.
Это означает, что ваш макрос успешно взаимодействовал с вашим кодом C++: вызов функции был успешным. В этом простом случае это забавно. Но мой собственный опыт с макросами, незаметно играющими с моим кодом, не полон радости и наполненности, так что ...
Использование #ifdef означает, что вы что-то «определяете». Не то чтобы вы придали этому значение. Он по-прежнему загрязняет окружающую среду, но, по крайней мере, он будет «ничем не заменен» и не будет восприниматься кодом C++ как запаздывающий оператор кода. Тот же код выше, с простым определением:
#define MY_MACRO
int doSomething(int p_iValue)
{
return p_iValue + 1 ;
}
int main(int argc, char **argv)
{
int MY_MACRO = 25 ;
doSomething(MY_MACRO) ;
return 0;
}
Дает следующие предупреждения:
main.cpp||In function ‘int main(int, char**)’:|
main.cpp|406|error: expected unqualified-id before ‘=’ token|
main.cpp|399|error: too few arguments to function ‘int doSomething(int)’|
main.cpp|407|error: at this point in file|
||=== Build finished: 3 errors, 0 warnings ===|
Так...
Я бы предпочел жить без макросов в моем коде, но по нескольким причинам (определение защиты заголовков или отладка макросов) я не могу.
Но, по крайней мере, мне нравится делать их как можно менее интерактивными с моим законным кодом C++. Это означает использование #define без значения, использование #ifdef и #ifndef (или даже #if, определенных в соответствии с предложением Джима Баком), и, прежде всего, присвоение им таких длинных и чужеродных имен, которые никто в здравом уме не будет использовать это «случайно», и это никоим образом не повлияет на законный код C++.
Теперь, когда я перечитываю свой пост, мне интересно, не стоит ли мне пытаться найти какое-то значение, которое никогда не будет правильным C++, чтобы добавить к моему определению. Что-то типа
#define MY_MACRO @@@@@@@@@@@@@@@@@@
который можно использовать с #ifdef и #ifndef, но не позволять коду компилироваться, если он используется внутри функции ... Я успешно пробовал это на g ++, и он дал ошибку:
main.cpp|410|error: stray ‘@’ in program|
Интересно. :-)
Я согласен с тем, что макросы могут быть опасными, но этот первый пример довольно очевиден для отладки и, конечно, дает только одну ошибку. Почему вы ожидаете большего? Я видел гораздо более отвратительные ошибки из-за макросов ...
Это правда, что разница между одним решением и другим почти тривиальна. Но в этом случае, поскольку мы говорим о двух конкурирующих стилях кодирования, то даже тривиальное нельзя игнорировать, потому что после этого все, что остается, - это личный вкус (и в этот момент я считаю, что его не следует нормализовать. )
Немного ОТ, но включение / выключение логирования с помощью препроцессора определенно неоптимально для C++. Существуют хорошие инструменты для ведения журнала, такие как Apache log4cxx, которые имеют открытый исходный код и не ограничивают способ распространения вашего приложения. Они также позволяют изменять уровни ведения журнала без перекомпиляции, имеют очень низкие накладные расходы, если вы отключите выход, и дают вам возможность полностью отключить выход в производственный процесс.
Я согласен, и мы действительно делаем это в нашем коде, мне просто нужен пример того, что вы могли бы использовать #if и т. д. Для
Моя первоначальная реакция была #ifdef, конечно, но я думаю, что #if действительно имеет для этого некоторые существенные преимущества - вот почему:
Во-первых, вы можете использовать DEBUG_ENABLED в тестах, скомпилированных препроцессором и. Пример. Часто мне нужны более длительные тайм-ауты, когда включена отладка, поэтому, используя #if, я могу написать это
DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);
... вместо ...
#ifdef DEBUG_MODE
DoSomethingSlowWithTimeout(5000);
#else
DoSomethingSlowWithTimeout(1000);
#endif
Во-вторых, вы находитесь в лучшем положении, если хотите перейти с #define на глобальную константу. #define обычно не одобряются большинством программистов на C++.
И, в-третьих, вы говорите, что в вашей команде раскол. Я предполагаю, что это означает, что разные участники уже приняли разные подходы, и вам необходимо стандартизировать их. Решение о том, что #if является предпочтительным выбором, означает, что код, использующий #ifdef, будет компилироваться и запускаться, даже если DEBUG_ENABLED является ложным. И много легче отследить и удалить отладочный вывод, который создается, когда он не должен быть, чем наоборот.
Да, и еще один незначительный момент для удобочитаемости. Вы должны иметь возможность использовать true / false, а не 0/1 в вашем #define, и поскольку значение является одним лексическим токеном, это единственный раз, когда вам не нужны круглые скобки.
#define DEBUG_ENABLED true
вместо
#define DEBUG_ENABLED (1)
Константа может не использоваться для включения / отключения отладки, поэтому запуск #ifdef с #define равным 0 может быть не таким уж безобидным. Что касается true / false, они были добавлены в C99 и отсутствуют в C89 / C90.
Майкл: Он / она защищал против использование #ifdef ?!
... хороший момент относительно истинного / ложного - тем более, что наша встроенная платформа на самом деле не определяет bool!
@ Джон Кейдж - какая платформа? Некоторые из них есть, но в странных местах.
Да, одна проблема с #ifdef заключается в том, что он работает с вещами, которые не определены; не определены ли они намеренно, из-за опечатки или чего-то еще.
Ваше добавление к ответу неверно. #if DEBUG_ENBALED не является ошибкой, обнаруженной препроцессором. Если DEBUG_ENBALED не определен, он заменяется маркером 0 в директивах #if.
@R .. Во многих компиляторах вы можете включить предупреждение для "#if DEBUG_ENABLED", когда DEBUG_ENABLED не определен. В GCC используйте "-Wundef". В Microsoft Visual Studio используйте «/ w14668», чтобы включить C4668 как предупреждение уровня 1.
Это вопрос стиля. Но я рекомендую более лаконичный способ сделать это:
#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print
#endif
debug_print("i=%d\n", i);
Вы делаете это один раз, а затем всегда используете debug_print (), чтобы либо печатать, либо ничего не делать. (Да, это будет компилироваться в обоих случаях.) Таким образом, ваш код не будет искажен директивами препроцессора.
Если вы получили предупреждение «выражение не действует» и хотите от него избавиться, вот альтернатива:
void dummy(const char*, ...)
{}
#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print dummy
#endif
debug_print("i=%d\n", i);
Возможно, макрос печати был не лучшим примером - на самом деле мы уже делаем это в нашей кодовой базе для нашего более стандартного кода отладки. Мы используем биты #if / #ifdefined для областей, в которых вы, возможно, захотите включить дополнительную отладку.
В целях выполнения условной компиляции #if и #ifdef являются почти одинаковыми, но не совсем. Если ваша условная компиляция зависит от двух символов, то #ifdef также не будет работать. Например, предположим, что у вас есть два символа условной компиляции, PRO_VERSION и TRIAL_VERSION, у вас может быть что-то вроде этого:
#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif
Использование #ifdef становится намного сложнее, особенно для того, чтобы заставить работать часть #else.
Я работаю над кодом, который широко использует условную компиляцию, и у нас есть смесь #if и #ifdef. Мы склонны использовать # ifdef / # ifndef для простого случая и #if всякий раз, когда оцениваются два или более символа.
в #if defined что такое defined это ключевое слово или?
#ifdef просто проверяет, определен ли токен, учитывая
#define FOO 0
потом
#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"
У нас была такая же проблема с несколькими файлами, и всегда была проблема, когда люди забывали включить файл «флаг функций» (с базой кода> 41 000 файлов это легко сделать).
Если у вас был feature.h:
#ifndef FEATURE_H
#define FEATURE_H
// turn on cool new feature
#define COOL_FEATURE 1
#endif // FEATURE_H
Но потом вы забыли включить заголовочный файл в file.cpp:
#if COOL_FEATURE
// definitely awesome stuff here...
#endif
Тогда у вас есть проблема, компилятор интерпретирует неопределенное значение COOL_FEATURE как «ложь» в этом случае и не может включить код. Да, gcc поддерживает флаг, который вызывает ошибку для неопределенных макросов ... но большая часть стороннего кода либо определяет, либо не определяет функции, поэтому это не будет таким переносимым.
Мы приняли переносимый способ исправления в этом случае, а также протестировали состояние функции: макросы функций.
если вы изменили вышеуказанный feature.h на:
#ifndef FEATURE_H
#define FEATURE_H
// turn on cool new feature
#define COOL_FEATURE() 1
#endif // FEATURE_H
Но потом вы снова забыли включить заголовочный файл в file.cpp:
#if COOL_FEATURE()
// definitely awseome stuff here...
#endif
В препроцессоре произошла ошибка из-за использования неопределенного функционального макроса.
Они оба ужасны. Вместо этого сделайте следующее:
#ifdef DEBUG
#define D(x) do { x } while(0)
#else
#define D(x) do { } while(0)
#endif
Затем, когда вам понадобится отладочный код, поместите его в D();. И ваша программа не засорена отвратительными лабиринтами #ifdef.
@MatthieuM. На самом деле, я думаю, что оригинальная версия была в порядке. Точка с запятой будет интерпретироваться как пустой оператор. Однако если забыть точку с запятой, это может стать опасным.
Это вообще не вопрос стиля. К сожалению, вопрос неверный. Вы не можете сравнивать эти директивы препроцессора в смысле «лучше» или «безопаснее».
#ifdef macro
означает «если макрос определен» или «если макрос существует». Значение макроса здесь не имеет значения. Это может быть что угодно.
#if macro
если всегда сравнивать со значением. В приведенном выше примере это стандартное неявное сравнение:
#if macro !=0
пример использования #if
#if CFLAG_EDITION == 0
return EDITION_FREE;
#elif CFLAG_EDITION == 1
return EDITION_BASIC;
#else
return EDITION_PRO;
#endif
теперь вы можете либо поместить определение CFLAG_EDITION либо в свой код
#define CFLAG_EDITION 1
или вы можете установить макрос как флаг компилятора. Также глянь сюда.
Раньше я использовал #ifdef, но когда я переключился на Doxygen для документации, я обнаружил, что закомментированные макросы не могут быть задокументированы (или, по крайней мере, Doxygen выдает предупреждение). Это означает, что я не могу задокументировать макросы переключения функций, которые в настоящее время не включены.
Хотя можно определить макросы только для Doxygen, это означает, что макросы в неактивных частях кода также будут задокументированы. Я лично хочу показать переключатели функций, а в остальном задокументировать только то, что выбрано в данный момент. Более того, это делает код довольно беспорядочным, если есть много макросов, которые нужно определять только тогда, когда Doxygen обрабатывает файл.
Поэтому в этом случае лучше всегда определять макросы и использовать #if.
Есть разница в случае другого способа указать условное определение для драйвера:
diff <( echo | g++ -DA= -dM -E - ) <( echo | g++ -DA -dM -E - )
выход:
344c344
< #define A
---
> #define A 1
Это означает, что -DA является синонимом -DA=1, и если значение не указано, это может привести к проблемам в случае использования #if A.
Мне нравится #define DEBUG_ENABLED (0), когда вам может понадобиться несколько уровней отладки. Например:
#define DEBUG_RELEASE (0)
#define DEBUG_ERROR (1)
#define DEBUG_WARN (2)
#define DEBUG_MEM (3)
#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL (DEBUG_RELEASE)
#endif
//...
//now not only
#if (DEBUG_LEVEL)
//...
#endif
//but also
#if (DEBUG_LEVEL >= DEBUG_MEM)
LOG("malloc'd %d bytes at %s:%d\n", size, __FILE__, __LINE__);
#endif
Упрощает отладку утечек памяти, избавляя от необходимости использовать все эти строки журнала для отладки других вещей.
Кроме того, #ifndef вокруг определения упрощает выбор определенного уровня отладки в командной строке:
make -DDEBUG_LEVEL=2
cmake -DDEBUG_LEVEL=2
etc
Если бы не это, я бы дал преимущество #ifdef, потому что флаг компилятора / make был бы отменен флагом в файле. Таким образом, вам не нужно беспокоиться об изменении заголовка перед выполнением фиксации.
#if, you can also then use#elifin a consistent manner, unlike with#ifdef. Thus, instead of just using#define BLAH, use#define BLAH 1with#if BLAH, etc...