Lazy evaluation with ostream C++ operators

泄露秘密 提交于 2019-12-10 03:09:19

问题


I am looking for a portable way to implement lazy evaluation in C++ for logging class. Let's say that I have a simple logging function like

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

then in syslog() function we can do:

if (priority < current_priority)
  return;

so we never actually call the formatting function (sprintf). On the other hand, if we use logging stream like

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

all the formating is always executed, which may take a lot of time. Is there any possibility to actually use all the goodies of ostream (like custom << operator for classes, type safety, elegant syntax...) in a way that the formating is executed AFTER the logging level is checked ?


回答1:


This looks like something that could be handled with expression templates. Beware, however, that expression templates can be decidedly non-trivial to implement.

The general idea of how they work is that the operators just build up a temporary object, and you pass that temporary object to your logging object. The logging object would look at the logging level and decide whether to carry out the actions embodied in the temporary object, or just discard it.




回答2:


What I've done in our apps is to return a boost::iostreams::null_stream in the case where the logging level filters that statement. That works reasonably well, but will still call all << operators.

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

Otherwise, it's expression templates as Jerry said.




回答3:


The easiest and most straight-forward way is to simply move the check outside of the 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 wanted to shorten the common case, you could 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 is marginal.

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 here is mixing iostream-style operator overloading with a printf-like logging function – specifically translating manipulators and formatting flags/fields from iostreams into a format string. You could write to a stringstream and then chunk that to your syslog function, or try something fancier. The above MyLogger works easiest if it also contains an ostream reference to which it can forward, but you'll need a few more op<< overloads for iomanips (e.g. endl) if you do that.




回答4:


For mine I made a debug_ostream class which has templated << operators. These operators check the debug level before calling the real operator.

You will need to define non-template overrides for const char* and std::ostream& (*x)(std::ostream&) because otherwise those don't work. I'm not sure why.

With inlining and high enough optimization levels the compiler will turn the whole output line into a single check of the debug level instead of one per output item.

I should add to this that this doesn't solve the original problem. For example if part of the debug line is to call an expensive function to get a value for output, that function will still be called. My solution only skips the formatting overhead.



来源:https://stackoverflow.com/questions/5035840/lazy-evaluation-with-ostream-c-operators

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!