Tuesday, December 31, 2013

C++ preprocessor macros and conditional tetrary operator

I had a simple code with conditional logging:
#include <iostream>

#define LOG(condition) \
  !(condition) ? std::cout : std::cout

int main () {
  LOG(true) << "LOG(true)\n";
  LOG(false) << "LOG(false)\n";
  return 0;
}
Can be compile by below command:
g++ -o main main.cc
The output looks:
$./main
LOG(true)
$
So it misses second log message. To debug I've used command:
g++ -E -C -P -o main_pre.cc main.cc
Flags corresponds to (gcc manual):
  • -E; "Preprocess only; do not compile, assemble or link"
  • -C; "Do not discard comments. All comments are passed through to the output file, except for comments in processed directives, which are deleted along with the directive. You should be prepared for side effects when using -C; it causes the preprocessor to treat comments as tokens in their own right. For example, comments appearing at the start of what would be a directive line have the effect of turning that line into an ordinary source line, since the first token on the line is no longer a ‘#’."
  • -P; "Inhibit generation of linemarkers in the output from the preprocessor. This might be useful when running the preprocessor on something that is not C code, and will be sent to a program which might be confused by the linemarkers."
This produces file main_pre.cc which is an expanded main.cc. When you search for int main(), you will get something similar to (around line 67560):
int main() {
  !(true) ? std::cout : std::cout << "LOG(true)\n";
  !(false) ? std::cout : std::cout << "LOG(false)\n";
  return 0;
}
So, above code is equivalent to:
int main() {
  if(!(true)) { 
    std::cout;
  } else { 
    std::cout << "INFO(true)\n";
  }
  if(!(false)) {
    std::cout;
  else {
    std::cout << "LOG(false)\n";
  }
  return 0;
}

As you can see, the problem is a lack of parenthesis in macro. The correct macro:
#define LOG(condition) \
  (!(condition) ? std::cout : std::cout)
And a real life macro:
#define LOG(condition) \
  !(condition) ? 0 : std::cout
Lesson learned: macros are misleading, always put correct parenthesis.

No comments:

Post a Comment