tags:

views:

290

answers:

1

I'm adding functions to my (simple) log class to make it usable like a stream. Currently, after some modifications, I got this (in my cpp):

// blah blah blah...
// note: here String is a defined as: typedef std::string String;

void Log::logMessage( const String& message )
    {
     logText(); // to be sure we flush the current text if any (when "composing" a message)
     addText( message ); 
     logText(); // really log the message and make the current text empty
    }

// blah blah blah...

    Log& operator<<( Log& log, const std::stringstream& message )
    {
     log.logMessage( message.str() );
     return log;
    }

    Log& operator<<( Log& log, const String& message )
    {
     log.addText( message );
     return log;
    }

Now in my "client" app I'm using this code to check the result (m_log is a valid pointer as you have already guessed):

gcore::Log& log = *m_log;
log << getName() << " : application created.";
log << "This is a test for " << getName();

Now the problem I got is that logText() (and logMessage) is never called because this test code will only call the << operator with String. What I need is a way to call logText() when the given steam of string is finished :

log << getName() << " : application created.";

would be equivalent to

log.addText( getName() );
log.addText( " : application create." );
log.logText();

I'm not sure how to do this or even if it's possible. My first guess is that it would be possible to use std::endl at the end of the stream like this :

log << getName() << " : application created." << std::endl;

Or something equivalent, but if it's possible to do it without adding objects to the stream, that would be nice.

Any idea?

+3  A: 

Hi, You can create a temp object and use his destructor to catch the end of the statement:

following code should give you the basic idea

class Log
{
public:
  class Sublog
  {
  public:
    Sublog(const std::string& message)
    {
      std::cout << message;
    }

    void addText(const std::string& message)
    {
      std::cout << message;
    }

    ~Sublog()
    {
      std::cout << std::endl;
    }

    Sublog& operator<<(const std::string& message )
    {
      this->addText(message);
      return *this;
    }
  };

};

Log::Sublog operator<<( Log& log, const std::string& message )
{
  return Log::Sublog(message);
}

which would be used like this

int main()
{
    Log log;
    log << "Foo" << "bar";
    log << "baz" << "plop";
}

after each semicolon, the destructor of Sublog is called


Klaim: the (working and effective) implementation of this solution in my case :

in the Log header :

    /** To allow streaming semantic on logs (used in << operator) .
*/
class LogStreamer
{
public:

 LogStreamer( Log& log, const String& text )
  : m_log( log )
 {
  m_log.addText( text );
 }

 ~LogStreamer()
 {
  m_log.logText();
 }

 LogStreamer& operator<<( const String& text )
 {
  m_log.addText( text );
  return *this;
 }

private:

 Log& m_log;

};

GCORE_API LogStreamer operator<<( Log& log, const String& message );

and in the cpp file:

LogStreamer operator<<( Log& log, const String& message )
{
 return LogStreamer( log, message );
}
chub
Seems nice, will try that!
Klaim
Wow, using a temporary object and its destructor, did not think of that. +1
Ozan
Thanks, it woked really nice, with only one tiny temporary object!I'll edit the post to gives the current full solution as some details had to be fixed.
Klaim