PR libstdc++/69608 Move semantics for strstreambuf
authorJonathan Wakely <jwakely@redhat.com>
Wed, 2 May 2018 16:25:44 +0000 (17:25 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 2 May 2018 16:25:44 +0000 (17:25 +0100)
In libstdc++ the deprecated char* streams are non-copyable, as was
required pre-C++11.

Since C++11 the standard implies that those streams should be copyable,
but doesn't specify the effects of copying them. This is surely a
defect, so for consistency with other implementations this change makes
them movable, but not copyable.

PR libstdc++/69608
* include/backward/strstream (strstreambuf): Define move constructor
and move assignment operator.
(istrstream, ostrstream, strstream): Likewise.
* testsuite/backward/strstream_move.cc: New.

From-SVN: r259842

libstdc++-v3/ChangeLog
libstdc++-v3/include/backward/strstream
libstdc++-v3/testsuite/backward/strstream_move.cc [new file with mode: 0644]

index 9ef88a8de9a3ddb871ef6d81e0cce629ed121dbb..c53c068d0ab3683d5cfdd78247e60dd6e52bae7b 100644 (file)
@@ -1,3 +1,11 @@
+2018-05-02  Jonathan Wakely  <jwakely@redhat.com>
+
+       PR libstdc++/69608
+       * include/backward/strstream (strstreambuf): Define move constructor
+       and move assignment operator.
+       (istrstream, ostrstream, strstream): Likewise.
+       * testsuite/backward/strstream_move.cc: New.
+
 2018-05-01  Tulio Magno Quites Machado Filho  <tuliom@linux.vnet.ibm.com>
 
        PR libstdc++/84654
index 0f0ede46f7aecd9527d61fb883ee1b0e689737ad..0429c28ce356904452c95a869ca6059c706289a3 100644 (file)
@@ -81,6 +81,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     virtual ~strstreambuf();
 
+#if __cplusplus >= 201103L
+    strstreambuf(strstreambuf&& __rhs) noexcept
+    : _Base(__rhs), _M_alloc_fun(__rhs._M_alloc_fun),
+      _M_free_fun(__rhs._M_free_fun), _M_dynamic(__rhs._M_dynamic),
+      _M_frozen(__rhs._M_frozen), _M_constant(__rhs._M_constant)
+    {
+      __rhs.setg(nullptr, nullptr, nullptr);
+      __rhs.setp(nullptr, nullptr);
+    }
+
+    strstreambuf&
+    operator=(strstreambuf&& __rhs) noexcept
+    {
+      if (_M_dynamic && !_M_frozen)
+       _M_free(eback());
+      _Base::operator=(static_cast<const _Base&>(__rhs));
+      _M_alloc_fun = __rhs._M_alloc_fun;
+      _M_free_fun = __rhs._M_free_fun;
+      _M_dynamic = __rhs._M_dynamic;
+      _M_frozen = __rhs._M_frozen;
+      _M_constant = __rhs._M_constant;
+      __rhs.setg(nullptr, nullptr, nullptr);
+      __rhs.setp(nullptr, nullptr);
+      return *this;
+    }
+#endif
+
   public:
     void freeze(bool = true) throw ();
     char* str() throw ();
@@ -98,10 +125,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                             = ios_base::in | ios_base::out);
 
   private:
+#if __cplusplus < 201103L
     strstreambuf&
     operator=(const strstreambuf&);
 
     strstreambuf(const strstreambuf&);
+#endif
 
     // Dynamic allocation, possibly using _M_alloc_fun and _M_free_fun.
     char* _M_alloc(size_t);
@@ -110,7 +139,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     // Helper function used in constructors.
     void _M_setup(char* __get, char* __put, streamsize __n) throw ();
 
-  private:
     // Data members.
     void* (*_M_alloc_fun)(size_t);
     void  (*_M_free_fun)(void*);
@@ -130,6 +158,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     istrstream(const char*, streamsize);
     virtual ~istrstream();
 
+#if __cplusplus >= 201103L
+    istrstream(istrstream&& __rhs)
+    : istream(std::move(__rhs)), _M_buf(std::move(__rhs._M_buf))
+    { set_rdbuf(&_M_buf); }
+
+    istrstream& operator=(istrstream&&) = default;
+#endif
+
     _GLIBCXX_CONST strstreambuf* rdbuf() const throw ();
     char* str() throw ();
 
@@ -145,6 +181,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     ostrstream(char*, int, ios_base::openmode = ios_base::out);
     virtual ~ostrstream();
 
+#if __cplusplus >= 201103L
+    ostrstream(ostrstream&& __rhs)
+    : ostream(std::move(__rhs)), _M_buf(std::move(__rhs._M_buf))
+    { set_rdbuf(&_M_buf); }
+
+    ostrstream& operator=(ostrstream&&) = default;
+#endif
+
     _GLIBCXX_CONST strstreambuf* rdbuf() const throw ();
     void freeze(bool = true) throw();
     char* str() throw ();
@@ -167,6 +211,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     strstream(char*, int, ios_base::openmode = ios_base::in | ios_base::out);
     virtual ~strstream();
 
+#if __cplusplus >= 201103L
+    strstream(strstream&& __rhs)
+    : iostream(std::move(__rhs)), _M_buf(std::move(__rhs._M_buf))
+    { set_rdbuf(&_M_buf); }
+
+    strstream& operator=(strstream&&) = default;
+#endif
+
     _GLIBCXX_CONST strstreambuf* rdbuf() const throw ();
     void freeze(bool = true) throw ();
     _GLIBCXX_PURE int pcount() const throw ();
diff --git a/libstdc++-v3/testsuite/backward/strstream_move.cc b/libstdc++-v3/testsuite/backward/strstream_move.cc
new file mode 100644 (file)
index 0000000..7dfbd33
--- /dev/null
@@ -0,0 +1,244 @@
+// Copyright (C) 2018 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-Wno-deprecated" }
+// { dg-do run { target c++11 } }
+
+#include <strstream>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  std::istrstream is("15 16");
+  std::istrstream is2 = std::move(is);
+  int a;
+  is >> a;
+  VERIFY( !is );
+  is2 >> a;
+  VERIFY( is2 );
+  VERIFY( a == 15 );
+  std::istrstream is3 = std::move(is2);
+  int b;
+  is2 >> b;
+  VERIFY( !is2 );
+  is3 >> b;
+  VERIFY( is3 );
+  VERIFY( b == 16 );
+}
+
+void
+test02()
+{
+  std::istrstream is("");
+  int a;
+  is >> a;
+  VERIFY( !is );
+  is = std::istrstream("17 18");
+  is >> a;
+  VERIFY( is );
+  VERIFY( a == 17 );
+  is = std::istrstream("");
+  int b;
+  is >> b;
+  VERIFY( !is );
+}
+
+void
+test03()
+{
+  std::ostrstream os;
+  os << "a few chars";  // fits in initial allocation
+  char* s = os.str(); // os now frozen
+  std::ostrstream os2 = std::move(os);
+  VERIFY( os2.str() == s );
+  VERIFY( os.str() == nullptr );
+  os2.freeze(false);
+
+  os2 << "enough additional chars to force a reallocation";
+  VERIFY( os2 );
+  s = os2.str();  // os2 now frozen
+  std::ostrstream os3 = std::move(os2);
+  VERIFY( os3.str() == s );
+  VERIFY( os2.str() == nullptr );
+  delete[] s;
+}
+
+void
+test04()
+{
+  char buf[16];
+  std::ostrstream os(buf, sizeof(buf));
+  os << "a few chars";
+  char* s = os.str(); // os now frozen
+  VERIFY( s == buf );
+  std::ostrstream os2 = std::move(os);
+  VERIFY( os2.str() == s );
+  VERIFY( os.str() == nullptr );
+  os2.freeze(false);
+
+  os2 << "enough additional chars to force a reallocation";
+  VERIFY( !os2 );
+  s = os2.str();  // os2 now frozen
+  VERIFY( s == buf );
+  std::ostrstream os3 = std::move(os2);
+  VERIFY( os3.str() == s );
+  VERIFY( os2.str() == nullptr );
+}
+
+void
+test05()
+{
+  char buf[] = "0123456789";
+  std::ostrstream os(buf, 1);
+  os << "aa";
+  VERIFY( !os );
+  os = std::ostrstream(buf, 10);
+  os << "some chars";
+  VERIFY( os );
+  VERIFY( os.pcount() == 10 );
+  os << "a";
+  VERIFY( !os );
+  os = std::ostrstream();
+  os << "a";
+  VERIFY( os );
+  VERIFY( os.pcount() == 1 );
+  char* s = os.str(); // os now frozen
+  os = std::ostrstream();
+  os.freeze(false);   // no effect
+  delete[] s;
+}
+
+void
+test06()
+{
+  char buf[] = "15 16";
+  std::strstream ss(buf, 5, std::ios::in|std::ios::app);
+  std::strstream ss2 = std::move(ss);
+  int a;
+  ss >> a;
+  VERIFY( !ss );
+  ss2 >> a;
+  VERIFY( ss2 );
+  VERIFY( a == 15 );
+  std::strstream ss3 = std::move(ss2);
+  int b;
+  ss2 >> b;
+  VERIFY( !ss2 );
+  ss3 >> b;
+  VERIFY( ss3 );
+  VERIFY( b == 16 );
+}
+
+void
+test07()
+{
+  std::strstream ss;
+  int a;
+  ss >> a;
+  VERIFY( !ss );
+  char buf[] = "17 18";
+  ss = std::strstream(buf, 5, std::ios::in|std::ios::app);
+  ss >> a;
+  VERIFY( ss );
+  VERIFY( a == 17 );
+  ss = std::strstream();
+  int b;
+  ss >> b;
+  VERIFY( !ss );
+}
+
+void
+test08()
+{
+  std::strstream ss;
+  ss << "a few chars";  // fits in initial allocation
+  char* s = ss.str(); // ss now frozen
+  std::strstream ss2 = std::move(ss);
+  VERIFY( ss2.str() == s );
+  VERIFY( ss.str() == nullptr );
+  ss2.freeze(false);
+
+  ss2 << "enough additional chars to force a reallocation";
+  VERIFY( ss2 );
+  s = ss2.str();  // ss2 now frozen
+  std::strstream ss3 = std::move(ss2);
+  VERIFY( ss3.str() == s );
+  VERIFY( ss2.str() == nullptr );
+  delete[] s;
+}
+
+void
+test09()
+{
+  char buf[16];
+  std::strstream ss(buf, sizeof(buf));
+  ss << "a few chars";
+  char* s = ss.str(); // ss now frozen
+  VERIFY( s == buf );
+  std::strstream ss2 = std::move(ss);
+  VERIFY( ss2.str() == s );
+  VERIFY( ss.str() == nullptr );
+  ss2.freeze(false);
+
+  ss2 << "enough additional chars to force a reallocation";
+  VERIFY( !ss2 );
+  s = ss2.str();  // ss2 now frozen
+  VERIFY( s == buf );
+  std::strstream ss3 = std::move(ss2);
+  VERIFY( ss3.str() == s );
+  VERIFY( ss2.str() == nullptr );
+}
+
+void
+test10()
+{
+  char buf[] = "0123456789";
+  std::strstream ss(buf, 1);
+  ss << "aa";
+  VERIFY( !ss );
+  ss = std::strstream(buf, 10);
+  ss << "some chars";
+  VERIFY( ss );
+  VERIFY( ss.pcount() == 10 );
+  ss << "a";
+  VERIFY( !ss );
+  ss = std::strstream();
+  ss << "a";
+  VERIFY( ss );
+  VERIFY( ss.pcount() == 1 );
+  char* s = ss.str(); // ss now frozen
+  ss = std::strstream();
+  ss.freeze(false);   // no effect
+  delete[] s;
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test05();
+  test06();
+  test07();
+  test08();
+  test09();
+  test10();
+}