1

I'm developing a log lib for myself, and want it can be used in a way like the style of iostream. For example:

log_debug << "Log body strings..." << endlog;

instead of:

log_debug( "Log body strings..." );

My codes is here:

class Log_t {
public:
   friend Log_t& endlog( Log_t& rp_Logger );
   friend Log_t& operator<<( Log_t& rp_Logger, const char* p_pchBody );
private:
   std::stringstream m_ssBuffer;
};

Log_t& endlog( Log_t& rp_Logger ) {
   std::cout << rp_Logger.m_ssBuffer.str() << std::endl;
   rp_Logger.m_ssBuffer = std::stringstream();

   return rp_Logger;
};

Log_t& operator<<( Log_t& rp_Logger, const char* p_pchBody ) {
   rp_Logger.m_ssBuffer << p_pchBody;
   return rp_Logger;
};

int main() {
   Log_t log;

   log << "Hello Logger!" << endlog;
   return EXIT_SUCCESS;
};
  1. These codes can not pass through compilation, and I got "no match for ‘operator<<’ (operand types are ‘Log_t’ and ‘Log_t&(Log_t&)’)".

  2. I can't found a way to tell out the end of a single log, while with the style of calling-a-function, it is not a problem.

As calling-a-function: log_debug( "Log body strings..." );, the end of a log has been impied by calling. --One calling, One log line-- But in the style with "<<" overloading, we can not tell where is the end of one individual log, because follows should be valid too:

log_debug << "Log " << "body " << "goes " << "here...\n"
          << "the rest of the same log goes here."
          << endlog;

It is why I coded a function "endlog()", neither for insertint a "\n" character, nor for flushing IO, rather in order to tell out "here is an end of one single log".

Would anyone please help me? Sorry for my poor English.

Leon
  • 1,489
  • 1
  • 12
  • 31
  • iostreams/<< style formatting is basically a PITA. They are replacing it with a Python style format library in C++20. Shame they cannot deprecate << fast enough! – n. m. could be an AI Aug 07 '19 at 05:22
  • Kinda-sorta-duplicate of [How does “std::cout << std::endl;” compile?](https://stackoverflow.com/questions/40322128/how-does-stdcout-stdendl-compile) – user4581301 Aug 07 '19 at 05:27
  • @n.m. thanks! Would you please give me something about the new formating style in C++20? – Leon Aug 07 '19 at 05:29
  • Future standard: https://fmt.dev/Text%20Formatting.html Implementation and documentation: https://github.com/fmtlib/fmt – n. m. could be an AI Aug 07 '19 at 06:44

1 Answers1

2

Your problem is that streams are non copyable:

// In C++03 this is a copy and not allowed.
rp_Logger.m_ssBuffer = std::stringstream();

In C++11 and later this is allowed as it becomes a move operation. But there is a better way to expresses this:

// You want to clear the stream
rp_Logger.m_ssBuffer.str("");

The next problem is that you have not overloaded the operator<< for functions only for C-Strings.

So we need to define operator<< so that you can pass functions and they get called. So you can do this.

 Log_t& operator<<( Log_t& rp_Logger, std::function<Log_t&(Log_t&)>&& action)
 {
     return action(rp_Logger);
 }

That should solve your compilation problems.

But there is what I would consider a design issue here. Presumably you can turn your logging on/off (more less verbose) something like that (most logging systems have this ability).

The problem here is that even when the logging system is inactive you will still get a call for every operator<< in the chain which could be a bit inefficient if it is not logging anything.

Also every parameter needs to be evaluated. Which can potentially be expensive especially if those parameters are then simply thrown away when the logging level is turned down.

log << "Error: " << expensiveCallToGetState() << " Line 10: " << anotherCallToGetHumanString() << endl;

Here we have 5 calls to operator<< and both function calls must be evaluated before the call.

Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Thank you very much. I thought the problems you mentioned. About former --using std::stringstream::clear() instead-- , I tried it and failed. All the things I inserted always exist in it, even though I called clear() method for a few times. – Leon Aug 07 '19 at 09:24
  • About the latter --the performance--, my foucus is making it easy to use for now. Maybe optimization is the next step. Moreover there has been traditional function calling way for using in my log-lib, if the performance is more important than ease to use. – Leon Aug 07 '19 at 09:30
  • By the way, have you any good idea to prevent unnecessary functions call from being evaluated, when current log-level is higher than the level of the log to be added. – Leon Aug 07 '19 at 09:37
  • Opps: clear() is not the correct function (fixed above). https://stackoverflow.com/questions/20731/how-do-you-clear-a-stringstream-variable – Martin York Aug 07 '19 at 23:27
  • Performance. No way around that using just C++. Some people do tricks with the pre-processor to hide it but you need an `if statement()` or the trinary expression. – Martin York Aug 07 '19 at 23:29