From c6d425797207908bb9468d79ac170c3f51c7a565 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Wed, 2 May 2018 17:25:44 +0100 Subject: [PATCH] PR libstdc++/69608 Move semantics for strstreambuf 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 | 8 + libstdc++-v3/include/backward/strstream | 54 +++- .../testsuite/backward/strstream_move.cc | 244 ++++++++++++++++++ 3 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/testsuite/backward/strstream_move.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 9ef88a8de9a..c53c068d0ab 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,11 @@ +2018-05-02 Jonathan Wakely + + 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 PR libstdc++/84654 diff --git a/libstdc++-v3/include/backward/strstream b/libstdc++-v3/include/backward/strstream index 0f0ede46f7a..0429c28ce35 100644 --- a/libstdc++-v3/include/backward/strstream +++ b/libstdc++-v3/include/backward/strstream @@ -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(__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 index 00000000000..7dfbd337901 --- /dev/null +++ b/libstdc++-v3/testsuite/backward/strstream_move.cc @@ -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 +// . + +// { dg-options "-Wno-deprecated" } +// { dg-do run { target c++11 } } + +#include +#include + +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(); +} -- 2.30.2