views:

249

answers:

5

I've a very basic class, name it Basic, used in nearly all other files in a bigger project. In some cases, there needs to be debug output, but in release mode, this should not be enabled and be a NOOP.

Currently there is a define in the header, which switches a makro on or off, depending on the setting. So this is definetely a NOOP, when switched off. I'm wondering, if I have the following code, if a compiler (MSVS / gcc) is able to optimize out the function call, so that it is again a NOOP. (By doing that, the switch could be in the .cpp and switching will be much faster, compile/link time wise).

--Header--
void printDebug(const Basic* p);

class Basic {
   Basic() {
      simpleSetupCode;

      // this should be a NOOP in release, 
      // but constructor could be inlined
      printDebug(this);
   }
};
--Source--
// PRINT_DEBUG defined somewhere else or here
#if PRINT_DEBUG
void printDebug(const Basic* p) {
   // Lengthy debug print
}
#else
void printDebug(const Basic* p) {}
#endif
+3  A: 

As with all questions like this, the answer is - if it really matters to you, try the approach and examine the emitted assembly language.

anon
Mawg
@mawg It does cover a lot of the uses of this site, and it's my opinion that probably 50% of the questions posted here should not be asked. This one in particular gets asked time and again and my answer is the honest one - we can't tell what your optimiser is going to do with your code, only you can do that.
anon
(-1) So then your answer is not really "useful" to the user. You have not in fact answered their questions, regardless of whether it was an appropriate question or not. In fact, you did not vote to close as duplicate. If I ask "what is 2 + 2" and you say "figure it out for yourself" you're not *helpful* and you're not *correct*. It's true that I should figure it out for myself, but you have **not* answered me. Your response should be a comment.
I had a slight hope that this might be answerable, but as for what a compiler does or not, there might be only the assembler code which tells the truth. Sorry for the lame question and not searching (with the correct keywords).
Dodo
@devinb Then why did the user accept it?
anon
@DoDo Don't worry - it's a difficult thing to search for, and there is of course no dupe of your specific question. My answer is a generic one for questions about what the optimiser may or may not get up to.
anon
@commenters: I hope I don't start a flame war here... Just wanted to mark it as answered, as I don't see, that there can be an answer, except "go look"...
Dodo
+1  A: 

Compiler possibly may optimize this code, if it knows printDebug function implementation at compilation time. If printDebug is in another object module, this possibly may be optimized only by linker, using the whole program optimization. But the only way to test this is to read compiler-generated Assembly code. If you already have PRINT_DEBUG macro, you can extend it by the way as TRACE is defined:

#define PRINT_DEBUG    // optional
#ifdef PRINT_DEBUG
#define PRINT_DEBUG_CALL(p) printDebug(p)
#else
#define PRINT_DEBUG_CALL(p)
#endif


void printDebug(const Basic* p);

class Basic {
   Basic() {
      simpleSetupCode;

      // this should be a NOOP in release, 
      // but constructor could be inlined
      PRINT_DEBUG_CALL(this);
   }
};
--Source--
// PRINT_DEBUG defined somewhere else or here
#if PRINT_DEBUG
void printDebug(const Basic* p) {
   // Lengthy debug print
}
#endif
Alex Farber
This is exactly what is used. But switching back and forth using that define issues a project recompile.
Dodo
Actually, this is not what is used, according to your code. In my version, printDebug function and calls to it don't exist in the program, when PRINT_DEBUG is not defined. About recompiling, yes, both versions require recompiling. But you cannot expect compiler to optimize empty function call without recompiling...
Alex Farber
I'm sorry that I've been unclear. The code I wrote in the question, is not the code in-use by the Basic class within my project. However, the code you provided is basically the equal code as the current state of the Basic class within my project.With the code provided in the question, the compile and link time is greatly reduced, compared to the version you an some others provided, when defining/undefining the flag PRINT_DEBUG.
Dodo
A: 

errm, why not use the pre-processor macro differently?

Just of the top of my head, something like:

  #define DEBUG_TRACE(p)
  #ifdef PRINT_DEBUG
    printDebug(p);
  #else
    ;
  #endif
Mawg
This is not different than what is used, only written differently.
Dodo
A: 

Currently most of the optimizations are done at compile time. Some compilers as LLVM are able to optimize at link time. This is a really interesting idea. I suggest you to take a look at.

Waiting for these kind of optimization, what you can do is the following. Define a macro that let you include the following statement depending on whether DEGUG is defined or not.

#ifdef DEBUG
#define IF_DEBUG (false) {} else
#else
#define IF_DEBUG 
#endif

You can the use it like this

   Basic() {
      simpleSetupCode;

      // this should be a NOOP in release, 
      // but constructor could be inlined
      IF_DEBUG printDebug(this);
   }

which is already much more readable than

   Basic() {
      simpleSetupCode;

      // this should be a NOOP in release, 
      // but constructor could be inlined
#if DEBUG
      printDebug(this);
#endif
   }

Note that you can use it as if it was a keyword

IF_DEBUG {
   printDebug(this);
   printDebug(thas);
}
Vicente Botet Escriba
A: 
#if PRINT_DEBUG
#define printDebug _real_print_debug
#else
#define printDebug(...)
#endif

This way the preprocessor will strip all debug code before it even gets to the compiler.

drawnonward