53

Suppose that I have 10,000 lines of C++ code. 200 lines of this code are for testing purpose (for example, check the program and show an error message).

Is there an way in C++ to ignore or consider some lines of the code (maybe with preprocessor keywords)?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user1436187
  • 3,252
  • 3
  • 26
  • 59
  • 15
    `#ifdef TESTS` is called conditional compilation. – Grijesh Chauhan Jan 21 '14 at 12:44
  • 66
    When I saw the title for this question, I immediately thought "Uh, comment them out?" ;-) – Ajedi32 Jan 21 '14 at 14:52
  • I'd keep in as many debug checks as possible, only removing them in places where profiling has shown them to cause a significant performance loss. – CodesInChaos Jan 21 '14 at 15:57
  • 2
    @PyRulez No, comments were invented for writing comments. Using them to disable/enable large chunks of code depending on the compilation context is a hack, and should be avoided except for the most temporary of cases (the reasons should be obvious but think about things like maintenance, automated building, etc.). – JBentley Jan 21 '14 at 17:35
  • 5
    The question concerns me because rather than having 200 lines of code embedded within 10,000 lines (presumably in lots of files), it would make sense to have the testing code in specific unit-test, and/or integration test files. Also 200 test lines for 10K lines of production code (2%) quite likely means you have very low test coverage. I realize this isn't the focus of the question (hence a comment rather than an answer) but I think you should evaluate your testing strategy. – Dale Wilson Jan 21 '14 at 18:06
  • 10
    I am incredibly surprised this is not a duplicate after all the years of StackOverflow. – jpmc26 Jan 21 '14 at 21:40
  • 3
    Everyone seems to be suggesting `#ifdef` (or `#if defined`). That's bad style, and borderline completely broken. Use `#if`, not `#ifdef`. The reason is that `#define IS_TEST_BUILD 0` should do the same as `#undef IS_TEST_BUILD`, not the same as `#define IS_TEST_BUILD 1`. – Ben Voigt Jan 22 '14 at 01:46
  • @jpmc26 yes there are many related good questions like [gcc conditional compilation](http://stackoverflow.com/questions/6338244/gcc-conditional-compilation) – Grijesh Chauhan Jan 22 '14 at 06:30
  • @PyRulez Using comments may be reasonable for small changes, but isn't a good fit if you have a large block of code that itself has comments, or if there are just a lot of debug statements to suppress. If you frequently toggle between enabled and disabled it becomes rather labour intensive. – TheDuke Jan 22 '14 at 08:11
  • Is there any modern C++ solution that avoids compiler directives? A motivation for C++ has always been to avoid the need to #define (and other?) C directives (e.g. by using `template`, `constexpr`, etc) – Sohail Si Dec 06 '22 at 15:15

8 Answers8

79

Short answer:

Use macros and #ifdef checking. For example:

#ifdef MY_CONTROL_MACRO
...
#endif

the code within this scope will only be compiled if you already defined the MY_CONTROL_MACRO macro.


More stuff:

  1. To define such a macro, you can

    • Add #define MY_CONTROL_MACRO to your code. Or,
    • For VS, add MY_CONTROL_MACRO to Project > Properties > C/C++ > Preprocessor > Preprocessor Definitions. Or,
    • For GCC, compile your code with option -DMY_CONTROL_MACRO.
  2. You can check out here for more info.

    This block is called a conditional group. controlled text will be included in the output of the preprocessor if and only if MACRO is defined. We say that the conditional succeeds if MACRO is defined, fails if it is not.

    The controlled text inside of a conditional can include preprocessing directives. They are executed only if the conditional succeeds. You can nest conditional groups inside other conditional groups, but they must be completely nested. In other words, ‘#endif’ always matches the nearest ‘#ifdef’ (or ‘#ifndef’, or ‘#if’). Also, you cannot start a conditional group in one file and end it in another.

  3. You can also use the advanced ifdef-else-endif style:

    #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
    
herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
  • 1
    I don't know about other compilers, but VC++ automatically defines `_DEBUG` when you start a debug build. – Proxy Jan 21 '14 at 15:52
  • 3
    @herohuyongtao: How so? I assume you could run GCC with `-D_DEBUG`? – You Jan 21 '14 at 18:34
  • @You See [here](http://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html). `-D name` will pre-define name as a macro, with definition 1. – herohuyongtao Jan 21 '14 at 18:50
  • @herohuyongtao: Yes, but how does that make `_DEBUG` "not portable"? – You Jan 21 '14 at 18:56
  • @You There is nothing to do with `_DEBUG_` here, just to define your macros to control your code. – herohuyongtao Jan 21 '14 at 18:59
  • @herohuyongtao I believe you meant **preprocessor** rather than **predecessor**. – Varaquilex Jan 21 '14 at 19:00
  • 1
    The Standard macro is `NDEBUG`, which is **Not** defined for debug builds. In particular, defining it removes `assert` evaluations. – MSalters Jan 21 '14 at 21:14
  • 4
    Use `#if ONLY_FOR_DEBUG`, not `#ifdef`. – Ben Voigt Jan 22 '14 at 01:47
  • @Proxy: True, as long as we want debugging (logging) output only (and always) in debug mode. Sometimes debug/log output is useful in optimized mode e.g. when such logging is infrequent and non-optimized program takes a long time to run. In these cases it's better not to overload the _DEBUG macro and make your own. – TheDuke Jan 22 '14 at 08:15
13

Surround the code with "#ifdef...#endif", and then use the compiler options to set the flag:

#ifdef MYTEST_ONLY_FUNCTIONALITY_ENABLED
...
#endif

You can then use the compiler options to include this code. For example, in GCC:

-DMYTEST_ONLY_FUNCTIONALITY_ENABLED

Though, to be honest, I think this approach is generally not very maintainable in large projects and, if possible, it is generally better to simply move the test-only code to a completely separate library (without this conditional logic) and simply link that code into your test binary rather than your non-test binary. That also avoids having to compile each of the other libraries in both debug and non-debug modes.

Michael Aaron Safyan
  • 93,612
  • 16
  • 138
  • 200
  • 2
    I'd say this approach simplifies maintainability as the code volume increases, as the control is simply in one place. Where it does get unmaintainable, and very quickly, is when the *number* of such build switches increases. It takes around 400 switches to have more build combinations than there are atoms in the universe (yes, I've worked on codebases that complex)! However, even that is more maintainable than hacking around commenting code blocks in and out! – Cheeseminer Jan 21 '14 at 17:35
6

This is what #ifdef was designed for

You put

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

and then you can pass to the compiler options to decide if you want the test part compiled in or not. For example with g++ it's

g++ -DTESTS ...
6502
  • 112,025
  • 15
  • 165
  • 265
  • Not so much. This is what `#if` was designed for. `#ifdef` was designed to provide default values as in `#ifndef VALUE` / `#define VALUE DEFAULT_VALUE` / `#endif` – Ben Voigt Jan 22 '14 at 01:49
6

Using a preprocessor guard is definitely the most flexible and common approach. However, when possible, I suggest using an if statement. For example, instead of

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

Assuming ENABLE_DEBUG is defined to be 0 or non-zero, I would use

void example(int a){
   int some_local;

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

Since ENABLE_DEBUG is a constant, when ENABLE_DEBUG is 0 the compiler will not generate any code for statements it guards. So, why use this method instead of #ifdef?

  1. If there are many separate debug statements spread throughout the code, it can be a bit easier to read
  2. More importantly, the code is always processed for syntactic errors, even if no code is generated. This can be very helpful if the debug code is not frequently enabled. If variables change (e.g. in the above example if the argument a was renamed), then the person making the change will know they have to update the debug statement as well. If #ifdefs are used, then it can hide bit rot until someone needs to enable the debug code and then they have to go and try and fix up the code, something that may not be obvious to them.

Obviously this approach only works for debug statements inside method/function bodies.

TheDuke
  • 750
  • 1
  • 7
  • 22
3

Go with the existing convention, and use the NDEBUG macro. All common compilers define this macro for release builds, and do not define it for debug builds.

The macro originally existed to control the output of assert(3), and is defined as such all the way back in the POSIX standard and at least since C89.

Note that you have to reverse the test with #ifndef.

An example:

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

P.S. With gcc/g++, you do a debug build by adding -g to the command line.

Michael Hampton
  • 9,737
  • 4
  • 55
  • 96
2

Surround your testing code #ifdef DEBUG.

#if DEBUG
   ....
#endif
Richard Schneider
  • 34,944
  • 9
  • 57
  • 73
2

Use preprocessor #define and #if

depending on your compiler, you should have some variables available by default i.e NDEBUG (for not-debug) or DEBUG

you can define a variable yourself in code by

#define MY_VARIABLE

and use it as follows

#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

for more information search online for

#define, #if, #ifdef, #ifndef
user3218782
  • 111
  • 2
  • 7
2

The way to go is using preprocessor directive with the define passed to the compiler or taken from a header "config.h":

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

To avoid to use everywhere in source code:

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

You may do in the header

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

And so use My_Debug_function almost normally.

Jarod42
  • 203,559
  • 14
  • 181
  • 302