Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

<< operator overloading in C++ for logging purposes

I have a C++ class where I place many std::cout statements to print informative text messages about a mass of signals that this class is handling. My intentition is to redirect these text messages to a function named log. In this function, I have flag named mVerbose which defines if the log text should be printed. The content of this function is as follows:

 void XXXProxy::log(std::stringstream& ss)
 {
   if(mVerbose)
   {
     std::cout << ss;
     ss << "";
   }
 }

Then, the caller code snippet to this function is as follows: std::stringstream logStr;

 logStr << "SE"
        << getAddr().toString()
        << ": WAITING on epoll..."
        << std::endl;      
 log(logStr);

I would like to overload the << operator in my XXXProxy in a way that I can get rid of creating a std::stringstream object and calling the log function. I want to be able to log the text messages as below and let the << operator aggregate everything into:

 << "SE"
 << getAddr().toString()
 << ": WAITING on epoll..."
 << std::endl;      

So I wouldlike to have an member << function that looks like:

 void XXXProxy::operator << (std::stringstream& ss)
 {
   if(mVerbose)
   {
     std::cout << ss;
     ss << "";
   }
 } 

QUESTION

I am relatively a novice C++ developer and get lots of compilation errors when attemting to write the above stated like << operator. Could you please make some suggestions or direct me to some links for me to correctly implement this << operator. Thanks.

like image 502
F. Aydemir Avatar asked Dec 08 '22 14:12

F. Aydemir


1 Answers

If you don't want to use std::cout directly and you want to have your own Log class, you could implement a simple wrapper providing the same interface of std::ostream: operator<<:

class Log {
private: 
    std::ostream& _out_stream;

    //Constructor: User provides custom output stream, or uses default (std::cout).
    public: Log(std::ostream& stream = std::cout): _out_stream(stream) {} 

    //Implicit conversion to std::ostream
    operator std::ostream() {
        return _out_stream;
    } 

    //Templated operator>> that uses the std::ostream: Everything that has defined 
    //an operator<< for the std::ostream (Everithing "printable" with std::cout 
    //and its colleages) can use this function.    
    template<typename T> 
    Log& operator<< (const T& data) 
    {
        _out_stream << data;
    }
}

So if you implement std::ostream& operator>>(std::ostream& os , const YourClass& object) for your classes, you can use this Log class.

The advantage of this approach is that you use the same mechanism to make std::cout << your_class_object work, and to make the class work with the Log.

Example:

struct Foo
{
    int x = 0; //You marked your question as C++11, so in class initializers 
               //are allowed. 

    //std::ostream::operator<< overload for Foo:
    friend std::ostream& operator<<(std::ostream& os , const Foo& foo)
    {
        os << foo.x;
    }
};

int main()
{
  Log my_log;
  Foo my_foo;

  my_foo.x = 31415;

  my_log << my_foo << std::endl; //This prints "31415" using std::cout.
}

Possible improvements:

  • You could write a extern const of class Log, and make the class implement a singleton. This allows you to access the Log everywhere in your program.
  • It's common in log outputs to have a header, like Log output (17:57): log message. To do that, you could use std::endl as a sentinel and store a flag that says when the next output is the beginning of a line (the beginning of a log message). Checkout the next answer for a complete and working implementation.

References:

  • std::ostream
  • operator<< for std::ostream
  • std::enable_if
  • std::is_same
  • decltype specifier
like image 93
Manu343726 Avatar answered Dec 11 '22 02:12

Manu343726