Sunday, March 20, 2016

Idiomatic way in C++ to test debug/release etc

Idiomatic way in C++ to test debug/release etc.



This is just a quick post on how one can improve their code by removing some #ifdefs and doing things a better way.

It is common to see this style of code:


void SomeFunction(int x)
{
      ...
#ifndef NDEBUG    // Double negative - if ! ! debug - eg: is debug
      ...
      // extra debug checks
      ...
#else
      ...
#endif
      ...
}


Usually NDEBUG is the define that is checked for release/debug checking as it is defined in the C standard in regards to assert.h. I don't know of any other defines the language specifies in relation to release/debug modes. So typically release builds will have NDEBUG defined, and non-release builds do not.

However using preprocessor checks is not very idiomatic C++. It also means the preprocessor hides part of the code from the compiler, skipping important compiler checks of the other half of the code, which means that if not regularly compiling all the permutations of defines that some sides of #if conditions can rot and then require additional maintenance when enabling them. Better is to have the compiler parse both sides and allow it to determine that one side of the branch is never taken and allow the compiler to eliminate it.

So what is a good way to formulate turning a define in to an idiomatic way to test this in C++?

One can do this in a header:


enum BuildType
{
#ifndef NDEBUG
 isRelease = 0,
 isDebug = 1
#else
 isDebug = 0,
 isRelease = 1
#endif
};


And then in code check the condition like this:


void SomeFunction(int x)
{
      ...
      if ( isDebug ) {
         ...
         // extra debug checks
         ...
      } else {
         ...
      }
      ...
}


Using an enum means that we don't need to create any variables to do this.

Obviously you can extrapolate this to testing for things besides debug/release. Also in the place where we declare the enum, if wanting to make it a runtime setting, instead of compile time setting, it becomes quite trivial to change that.
Where this doesn't work so well is if using #ifdef to choose between two options that will either work or not depending on the compiler or target platform, eg if one side is iOS code and the other Win32 code, obviously it may not be worth making the compiler able to accept both sides of the #if/#else. I believe in these cases where you have platform specific code, generally you want to hide that from the generic code and abstract the platform code and put platform specific implementations in their own separate files.

No comments:

Post a Comment