Search code examples
c++c++11gccmove-semanticsgcc4.9

Workaround for returning uncopyable object without a move ctor


In my API I have a function that returns std::istringstream.
The std::istringstream class is non-copyable but supports moving so on a conforming compiler there is no problem returning a local std::istringstream.

However, on gcc 4.9, there is no support for moving std::istringstream. Is there some workaround that I can use that std::istringstream without changing the API from the user's perspective?

The workaround suggested here, of using a unique_ptr<std::istringstream> will change the semantics of the API.


Solution

  • Answering my own question for completeness and future reference.

    The goal was to find a workaround for the gcc (< 5) bug where std::istringstream does not provide a move ctor that will work in cases where I want to return the un-copyable and (bugly-) unmovable stream.
    As mentioned in the comments, I can in fact change my function signature (at least on gcc < 5) to return a proxy object that allows copying or moving without changing the API for code used on newer/other compilers.

    The idea, suggested and implemented by a colleague, is to create a proxy object around std::istringstream which provides a similar API, but also provides a copy-ctor which manually creates and initializes a new internal std::istringstream from the copied-from stream. This proxy is used only on the offending compilers.

    The code in its natural habitat is here.
    Here's the relevant part:

    #if !defined(__GNUC__) || (__GNUC__ >= 5)
       using string_stream = std::istringstream;
    #else
        // Until GCC 5, istringstream did not have a move constructor.
        // stringstream_proxy is used instead, as a workaround.
       class stringstream_proxy
       {
       public:
          stringstream_proxy() = default;
    
          // Construct with a value.
          stringstream_proxy(std::string const& value) :
             stream_(value)
          {}
    
          // Copy constructor.
          stringstream_proxy(const stringstream_proxy& other) :
             stream_(other.stream_.str())
          {
             stream_.setstate(other.stream_.rdstate());
          }
    
          void setstate(std::ios_base::iostate state) { stream_.setstate(state); }
    
          // Stream out the value of the parameter.
          // If the conversion was not possible, the stream will enter the fail state,
          // and operator bool will return false.
          template<typename T>
          stringstream_proxy& operator >> (T& thing)
          {
             stream_ >> thing;
             return *this;
          }
    
    
          // Get the string value.
          std::string str() const { return stream_.str(); }
    
          std::stringbuf* rdbuf() const { return stream_.rdbuf(); }
    
          // Check the state of the stream. 
          // False when the most recent stream operation failed
          operator bool() const { return !!stream_; }
    
          ~stringstream_proxy() = default;
       private:
          std::istringstream stream_;
       };
       using string_stream = stringstream_proxy;
    #endif