Search code examples
c++stringstreamostreamistream

Overloading the ostream operator for a class wrapping stringstream


I'm currently writing a class to wrap stringstream. My overall goal is to provide a threadsafe << (ostream) for my stringstream. I'm having trouble figuring out the correct syntax of what I'm trying to do. Any help would be most appreciative!

Below you'll find what I've tried. I know this is not the correct syntax, but it is kind of what I'm shooting for. I could of course not overload the << operator and force users to use the AddValue, but that wouldn't be ideal for quick and easy string manipulation while writing the code.

class WrappedStringStream

{

public :

   WrappedStringStream() ;

   template<typename T>
   void AddValue( const T & value )
   {

      m_mutex.Lock() ;

      //here I'd like to do something like m_stringstream << value, but of course since
      //I'm overloading operator<< that won't work

      m_mutex.Unlock() ;
   }

   friend std::ostream & operator<<( std::ostream & out, const WrappedStringStream & string )

   {

      string.AddValue( out ) ;
      return out ;   
   }

protected :

   std::stringstream m_stringstream ;

   mutable Mutex m_mutex ; 
}

As mentioned above it doesn't compile, which I understand since I'm passing WrappedStringStream as a const parameter and calling AddValue which isn't const - resulting in the discards qualifier error.


Solution

  • Here is the solution

    #include <iostream>
    #include <sstream>
    #include <mutex>
    
    using namespace std;
    
    class MutexWrapper
    {
        private:
            mutex& m_mutex;
        public:
            MutexWrapper(mutex& mtx) : m_mutex(mtx) { mtx.lock () ; };
        ~MutexWrapper() { m_mutex.unlock () ; };
    };
    
    class WrappedStringStream
    {
        public :
       WrappedStringStream() { };
    
        template<typename T>
        std::ostream & operator<<(const T& value)
       {
          MutexWrapper wrapper(m_mutex);
          return m_stringstream << value;
       }
    
    void showStream()
    {
        cout << m_stringstream.str();
    }
    
    protected :
       stringstream m_stringstream;
    mutable mutex m_mutex ; 
    };
    
    int main()
    {
        WrappedStringStream ws;
    
        ws << "This is a string, " << 5 << 6.78;
        ws.showStream();
    
        return 0;
    }
    

    Output

    This is a string, 56.78
    

    ======== edit ==========
    Originally I was not quiet understand what the questioner's final goal, but just focus on how to fix his syntax issue.It is not a good idea to use << in a multithreading environment. We have a Log class, and in our log class we only have a Log method which takes variable amount of parameters. That will solve the problem.
    Still, there is a solution for using << to lock the thread, but really urgly and NOT recommend. Downside is obvious - if you forgot to add the 'LoggingStart' and 'LoggingEnd', you might caught deadlock.
    Also thanks @RemyLebeau, It should return *this instead of m_stringstream.

    Please see code below.

    #include <iostream>
    #include <sstream>
    #include <mutex>
    
    using namespace std;
    
    class WrappedStringStream
    {
        public:
            enum StreamSignals
            {
                LoggingStart,
                LoggingEnd
            };
    
        WrappedStringStream() { };
    
        std::ostream & operator<<(const StreamSignals& signal)
        {
            if (signal == LoggingStart)
                 m_mutex.lock();
            else if (signal == LoggingEnd)
                 m_mutex.unlock();
    
            return *this;
        }
    
        template<typename T>
        std::ostream & operator<<(const T& value)
        {
            m_stringstream << value;
            return *this;
        }
    
        void showStream()
        {
            cout << m_stringstream.str();
        }
    
    protected :
       stringstream m_stringstream;
       mutable mutex m_mutex ; 
    };
    
    int main()
    {
        WrappedStringStream ws;
    
        ws << WrappedStringStream::StreamSignals::LoggingStart; 
        ws << "This is a string, " << 5 << 6.78;
        ws << WrappedStringStream::StreamSignals::LoggingEnd;
        ws.showStream();
    
        return 0;
    }