I posted this answer. Code:
#include <atomic>
#include <utility>
void printImpl(...);
std::atomic<bool> printLog = false;
class Log {
public:
template <typename T>
const auto& operator<<(T&& t) {
if (printLog) {
ulog.active = true;
return ulog << std::forward<T>(t);
} else {
ulog.active = false;
return ulog;
}
}
private:
struct unchecked_log {
template <typename T>
const auto& operator<<(T&& t) const {
if (active) {
printImpl(std::forward<T>(t));
}
return *this;
}
bool active{false};
};
unchecked_log ulog{};
};
// Instead of the macro. Doesn't break backward compatibility
Log LOG;
void test(bool) { LOG << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10; }
In essence, the code either ignores or logs all the data. The idea was to record the atomic<bool>
in a regular bool
which can be optimized out more easily. I thought most compilers could easily optimize out the if (active)
part since there is no way it can change between calls. Turns out though, most compilers do inline the function call to unchecked_log::operator<<
but do not optimize out the branching. Is there something preventing this optimization? Would it be illegal.