Lazy evaluation with C ++ operations

I am looking for a portable way to implement lazy evaluation in C ++ for logging. Say I have a simple registration function like

void syslog(int priority, const char *format, ...); 

then in the syslog () function we can do:

 if (priority < current_priority) return; 

therefore, we never call the format function (sprintf). On the other hand, if we use a logging thread like

 log << LOG_NOTICE << "test " << 123; 

all formatting is always performed, which can take a long time. Is it possible to actually use all the useful properties of ostream (for example, a custom <operator for classes, security type, elegant syntax ...) in such a way that the formation is performed after the level of logging is checked?

+8
c ++ iostream logging lazy-evaluation
source share
4 answers

This is similar to what can be handled with expression patterns. Remember, however, that expression patterns can be clearly non-trivial to implement.

The general idea of ​​how they work is that the operators simply create a temporary object and pass that temporary object to your logging object. The registration object will look at the logging level and decide whether to perform the actions embodied in the temporary object, or simply discard it.

+4
source share

What I did in our applications is to return boost::iostreams::null_stream when the logging level filters this statement. This works quite well, but will still call all <statements.

If the log level is set at compile time, you can switch to an object with a null <Operator.

Otherwise, these are expression patterns, as Jerry said.

+4
source share

The easiest and easiest way is to simply move the check out of formatting:

 MyLogger log; // Probably a global variable or similar. if (log.notice()) log << "notified!\n" << some_function("which takes forever to compute" " and which it is impossible to elide if the check is inside log's" " op<< or similar"); if (log.warn()) { log << "warned!\n"; T x; longer_code_computing(value_for, x); // easily separate out this logic log << x; } 

If you really want to shorten the general case, you can use a macro:

 #define LOG_NOTICE(logger) if (logger.notice()) logger << LOG_NOTICE(log) << "foo\n"; // instead of: if (log.notice()) log << "foo\n"; 

But the savings are negligible.

One possible MyLogger:

 struct MyLogger { int priority_threshold; bool notice() const { return notice_priority < current_priority; } bool warn() const { return warn_priority < current_priority; } bool etc() const { return etc_priority < current_priority; } template<class T> MyLogger& operator<<(T const &x) { do_something_with(x); return *this; } }; 

The problem is moving the operator overload in iostream style with a logging function like printf, in particular, translating manipulators and formatting flags / fields from iostreams to the format string. You can write to the string stream and then add it to your syslog function or try something more interesting. The aforementioned MyLogger works the easiest if it also contains a link to the stream to which it can forward, but you will need some more op <overload operations for iomanips (like endl) if you do this.

+3
source share

For mine, I created a class debug_ostream that has templated <<statements. These statements check the debugging level before calling the actual statement.

You will need to define overrides without patterns for const char* and std::ostream& (*x)(std::ostream&) , because otherwise they will not work. I'm not sure why.

With inlay and fairly high levels of optimization, the compiler will turn the entire output string into a single debug level check instead of one per output element.

I must add to this that this does not solve the original problem. For example, if part of a debugging line calls an expensive function to get a value for output, that function will still be called. My solution only skips formatting overhead.

+2
source share

All Articles