'How to drop the expressions after <<?

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?



Solution 1:[1]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Turtlefight