Заставить компилятор игнорировать некоторые строки в программе

Предположим, что у меня есть 10 000 строк кода на C++. 200 строк этого кода предназначены для тестирования (например, проверить программу и показать сообщение об ошибке).

Есть ли способ в C++ игнорировать или учитывать некоторые строки кода (возможно, с ключевыми словами preprocessor)?


person user1436187    schedule 21.01.2014    source источник
comment
#ifdef TESTS называется условной компиляцией.   -  person Grijesh Chauhan    schedule 21.01.2014
comment
Когда я увидел заголовок этого вопроса, я сразу подумал: «А, закомментировать их?» ;-)   -  person Ajedi32    schedule 21.01.2014
comment
Я бы оставил как можно больше отладочных проверок, удаляя их только в тех местах, где профилирование показало, что они вызывают значительную потерю производительности.   -  person CodesInChaos    schedule 21.01.2014
comment
@PyRulez Нет, комментарии были придуманы для написания комментариев. Использование их для отключения/включения больших фрагментов кода в зависимости от контекста компиляции является хаком, и его следует избегать, за исключением самых временных случаев (причины должны быть очевидны, но подумайте о таких вещах, как обслуживание, автоматическая сборка и т. д.).   -  person JBentley    schedule 21.01.2014
comment
Этот вопрос беспокоит меня, потому что вместо того, чтобы иметь 200 строк кода, встроенных в 10 000 строк (предположительно, во многих файлах), было бы разумно иметь код тестирования в конкретных файлах модульного теста и/или файлах интеграционного теста. Кроме того, 200 тестовых строк для 10 000 строк производственного кода (2%), скорее всего, означают, что у вас очень низкое тестовое покрытие. Я понимаю, что это не главное в вопросе (следовательно, комментарий, а не ответ), но я думаю, что вам следует оценить свою стратегию тестирования.   -  person Dale Wilson    schedule 21.01.2014
comment
Я невероятно удивлен, что это не дубликат после всех лет StackOverflow.   -  person jpmc26    schedule 22.01.2014
comment
Кажется, все предлагают #ifdef (или #if defined). Это плохой стиль, и граница полностью нарушена. Используйте #if, а не #ifdef. Причина в том, что #define IS_TEST_BUILD 0 должен делать то же самое, что и #undef IS_TEST_BUILD, а не то же самое, что #define IS_TEST_BUILD 1.   -  person Ben Voigt    schedule 22.01.2014
comment
@jpmc26 jpmc26 да, есть много связанных хороших вопросов, таких как условная компиляция gcc   -  person Grijesh Chauhan    schedule 22.01.2014
comment
@PyRulez Использование комментариев может быть разумным для небольших изменений, но не подходит, если у вас есть большой блок кода, который сам имеет комментарии, или если нужно подавить множество операторов отладки. Если вы часто переключаетесь между включенным и отключенным, это становится довольно трудоемким.   -  person TheDuke    schedule 22.01.2014


Ответы (8)


Короткий ответ:

Используйте макросы и проверку #ifdef. Например:

#ifdef MY_CONTROL_MACRO
...
#endif

код в этой области будет скомпилирован только в том случае, если вы уже определили макрос MY_CONTROL_MACRO.


Еще материалы:

  1. Чтобы определить такой макрос, вы можете

    • Add #define MY_CONTROL_MACRO to your code. Or,
    • Для VS добавьте MY_CONTROL_MACRO к Project > Properties > C/C++ > Preprocessor > Preprocessor Definitions. Или,
    • Для GCC скомпилируйте свой код с опцией -DMY_CONTROL_MACRO.
  2. #P4# <блочная цитата> #P5# #P6#
  3. Вы также можете использовать расширенный стиль ifdef-else-endif:

    #ifdef MY_CONTROL_MACRO
        ... // this part will be valid if MY_CONTROL_MACRO is defined
    #else
        ... // this part will be valid if MY_CONTROL_MACRO is NOT defined
    #endif
    
person herohuyongtao    schedule 21.01.2014
comment
Я не знаю о других компиляторах, но VC++ автоматически определяет _DEBUG при запуске отладочной сборки. - person Proxy; 21.01.2014
comment
@herohuyongtao: Как так? Я предполагаю, что вы могли бы запустить GCC с -D_DEBUG? - person You; 21.01.2014
comment
@ Вы видите здесь. -D name будет предварительно определять имя как макрос с определением 1. - person herohuyongtao; 21.01.2014
comment
@herohuyongtao: Да, но как это делает _DEBUG не переносимым? - person You; 21.01.2014
comment
@You Здесь нет ничего общего с _DEBUG_, просто чтобы определить ваши макросы для управления вашим кодом. - person herohuyongtao; 21.01.2014
comment
@herohuyongtao Я полагаю, вы имели в виду препроцессор, а не предшественник. - person Varaquilex; 21.01.2014
comment
Стандартный макрос — NDEBUG, который не определен для отладочных сборок. В частности, его определение удаляет assert оценок. - person MSalters; 22.01.2014
comment
Используйте #if ONLY_FOR_DEBUG, а не #ifdef. - person Ben Voigt; 22.01.2014
comment
@Proxy: Верно, пока мы хотим, чтобы отладка (регистрация) выводилась только (и всегда) в режиме отладки. Иногда вывод отладки/журнала полезен в оптимизированном режиме, например. когда такое логирование нечасто и неоптимизированная программа запускается долго. В этих случаях лучше не перегружать макрос _DEBUG и сделать свой. - person TheDuke; 22.01.2014

Окружите код "#ifdef...#endif", а затем используйте параметры компилятора, чтобы установить флаг:

#ifdef MYTEST_ONLY_FUNCTIONALITY_ENABLED
...
#endif

Затем вы можете использовать параметры компилятора для включения этого кода. Например, в GCC:

-DMYTEST_ONLY_FUNCTIONALITY_ENABLED

Хотя, если честно, я думаю, что этот подход вообще не очень ремонтопригоден в больших проектах и, если возможно, вообще лучше просто переместить тестовый код в совершенно отдельную библиотеку (без этой условной логики) и просто связать это код в свой тестовый двоичный файл, а не в нетестовый двоичный файл. Это также позволяет избежать компиляции каждой из других библиотек как в режиме отладки, так и в режиме без отладки.

person Michael Aaron Safyan    schedule 21.01.2014
comment
Я бы сказал, что такой подход упрощает сопровождение по мере увеличения объема кода, поскольку управление находится просто в одном месте. Там, где это действительно становится неуправляемым, и очень быстро, когда увеличивается число таких переключений сборки. Требуется около 400 переключателей, чтобы иметь больше комбинаций сборки, чем атомов во вселенной (да, я работал над такими сложными кодовыми базами)! Тем не менее, даже это более удобно, чем хакерство с комментариями блоков кода! - person Cheeseminer; 21.01.2014

Это то, для чего был разработан #ifdef

Вы положили

#ifdef TESTS
... test code ...
#endif

а затем вы можете перейти к параметрам компилятора, чтобы решить, хотите ли вы скомпилировать тестовую часть или нет. Например, с g++ это

g++ -DTESTS ...
person 6502    schedule 21.01.2014
comment
Не так много. Это то, для чего был разработан #if. #ifdef был разработан для предоставления значений по умолчанию, как в #ifndef VALUE / #define VALUE DEFAULT_VALUE / #endif - person Ben Voigt; 22.01.2014

Использование защиты препроцессора, безусловно, является наиболее гибким и распространенным подходом. Однако, когда это возможно, я предлагаю использовать оператор if. Например, вместо

void example(int a){
   int some_local;
   ...
   #ifdef _DEBUG
   std::cout << "In function " << __FUNCTION__ << "(" << a <<")" << std::endl;
   #endif
   ....
}

Предполагая, что ENABLE_DEBUG определен как 0 или ненулевое значение, я бы использовал

void example(int a){
   int some_local;

   ...
   if(ENABLE_DEBUG) std::cout << "In function " << __FUNCTION__ << "(" << a <<")" << std::endl;
   ...
}

Поскольку ENABLE_DEBUG является константой, когда ENABLE_DEBUG равен 0, компилятор не будет генерировать код для охраняемых им операторов. Итак, зачем использовать этот метод вместо #ifdef?

  1. Если в коде много отдельных операторов отладки, его может быть немного легче читать.
  2. Что еще более важно, код всегда обрабатывается на наличие синтаксических ошибок, даже если код не генерируется. Это может быть очень полезно, если код отладки включается нечасто. Если переменные изменяются (например, в приведенном выше примере, если аргумент a был переименован), то человек, делающий изменение, будет знать, что он также должен обновить оператор отладки. Если используются #ifdefs, то это может скрыть битовую гниль до тех пор, пока кому-то не понадобится включить код отладки, а затем им придется пойти и попытаться исправить код, что может быть для них неочевидным.

Очевидно, что этот подход работает только для операторов отладки внутри тел методов/функций.

person TheDuke    schedule 22.01.2014
comment
Это отличный совет — обеспечить проверку отладочного кода — отличная идея! - person Daniel Buckmaster; 21.11.2014

Следуйте существующему соглашению и используйте макрос NDEBUG. Все распространенные компиляторы определяют этот макрос для релизных сборок и не определяют его для отладочных сборок.

Макрос изначально существовал для управления выводом assert(3) и был определен как таковой с самого начала в стандарте POSIX и, по крайней мере, начиная с C89.

Обратите внимание, что вы должны отменить тест с помощью #ifndef.

Пример:

#ifndef NDEBUG
    /* Debugging code */
    std::cerr << "So far we have seen " << unicorns << " unicorns" << std::endl;
#endif

P.S. С помощью gcc/g++ вы выполняете отладочную сборку, добавляя -g в командную строку.

person Michael Hampton    schedule 21.01.2014

Окружите код тестирования #ifdef DEBUG.

#if DEBUG
   ....
#endif
person Richard Schneider    schedule 21.01.2014

Можно использовать директиву препроцессора с define, переданным компилятору или взятым из заголовка «config.h»:

#if defined(DEBUG) // or #ifdef DEBUG
    // Debug code
#endif

Чтобы не использовать везде в исходном коде:

#if defined(DEBUG)
    My_Debug_function(some_variable)
#endif

можно сделать в шапке

#if !defined(DEBUG) // or #ifndef DEBUG
# define My_Debug_function(some_variable) do { static_cast<void>(some_variable); } while (false)  /* Do nothing */
#endif

А так пользуюсь My_Debug_function почти нормально.

person Jarod42    schedule 21.01.2014

Используйте препроцессор #define и #if

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

вы можете определить переменную самостоятельно в коде

#define MY_VARIABLE

и используйте его следующим образом

#ifdef MY_VARIABLE
  //code that compiles only if MY_VARIABLE is defined
  printf("test output here");
#else
  //code that compiles only if MY_VARIABLE is NOT defined
  printf("MY_VARIABLE is not defined");
#endif

для получения дополнительной информации поищите в Интернете

#define, #if, #ifdef, #ifndef
person user3218782    schedule 21.01.2014