1

There is a very simple log "system":

#include <iostream>

#if true
#  define LOG std::cout
#else
#  define LOG
#endif

int main()
{
  LOG << "hi" << std::endl;
   
}

My goal is to make it conditional i.e. to not do anything when the condition is false.

What I've tried:

  • #undef LOG: not good, "error: 'LOG' was not declared in this scope"
  • #define LOG {}: not good, "error: expected primary-expression before '<<' token"

If I need to keep the << functionality, how can I drop all inputs towards LOG?

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Daniel
  • 2,318
  • 2
  • 22
  • 53
  • This isn't C. Looks like C++. – Andrew Henle Apr 22 '22 at 18:41
  • 11
    Write your own ostream which just ignores everything. – tkausl Apr 22 '22 at 18:41
  • 3
    Don't use `#define` for this. Make `LOG` a real object that has behavior at runtime. – Silvio Mayolo Apr 22 '22 at 18:48
  • 1
    Actually, I found one. [See if this question helps](https://stackoverflow.com/questions/6240950/platform-independent-dev-null-in-c). – Silvio Mayolo Apr 22 '22 at 18:49
  • I just want to *not* build the log messages into the binary when not needed. So now I can do `#if preprocess_condition \ LOG << "something debug"; \ #endif` everywhere which is too much typing. Hence I thought to solve this purely with preprocessor macros. – Daniel Apr 22 '22 at 19:11
  • 2
    Another option is to define `LOG` to be a function-style macro that takes in an input parameter for the output data, and then you can simply ignore that parameter if needed: `#if condition==true #define LOG(values) std::cout << values << std::endl; #else #define LOG(values) #endif` And then you can use it like this: `LOG("hi")`, `LOG("value = " << variable)`, etc – Remy Lebeau Apr 22 '22 at 19:12
  • 2
    If you *must* use the `<<` syntax, consider `boost::iostreams`: to create a stream that goes nowhere: https://godbolt.org/z/M6sxKan9K – AndyG Apr 22 '22 at 19:20

1 Answers1

2

The easiest option would be to use an object that just swallows all the operator<< calls (like @tkausl suggested in the comments):

godbolt example

#if SOMETHING
#define LOG std::cout
#else
struct nullout_t {};
template<class T>
constexpr nullout_t const& operator<<(nullout_t const& no, T&&) { return no; }
constexpr nullout_t nullout;
#define LOG nullout
#endif // SOMETHING

int main()
{
    LOG << "hello world\n";
    return 0;
}

Please note however this method completely relies on the compiler being able to eliminate the entire log statement.

So if your logging has any observable side-effects (or the compiler simply isn't able to prove it's dead code), you still will end up with some of the logging code running.

e.g. a simple string concatenation is already enough to prevent gcc from optimizing it out completely:

godbolt example

int main()
{
    LOG << std::string("hello") + std::string("world\n");
    return 0;
}

I wouldn't recommend using boost:iostreams with null_sink like @AndyG suggested, because that makes it very hard (if not impossible) for compilers to completely optimize out. (In the linked example you can see that main() still actually calls operator<< on the iostream) This is due to iostreams performing formatting first before actually passing the final result to the sink (null_sink) - and formatting depends on the stream manipulators applied to the stream and the locale imbued into it - so the compiler would need to prove that nothing of that has any side effect for it to be able to optimize it out completely (which is rather hard).


If you want to be able to completely get rid of the entire logging code, you'd have to change your LOG macro so that the preprocessor can completely remove the operator<< calls as well, e.g.:

godbolt example

#include <iostream>

#if SOMETHING
#define LOG(...) std::cout __VA_ARGS__
#else
#define LOG(...)
#endif // SOMETHING

int main()
{
    LOG(<< "hello world!\n");
    LOG(<< "there are " << 5 << " cookies in the " << std::string("jar") << std::endl);
    return 0;
}

By using the preprocessor to remove the code you can get rid of all logging logic, even if it would have observable side effects.

Turtlefight
  • 9,420
  • 2
  • 23
  • 40