On 14/11/14 15:43 +0000, Jonathan Wakely wrote:
This is the long-awaited ABI break for std::string, replacing our venerable Copy-On-Write implementation with a C++11-conforming Small-String-Optimization implementation (based on Paolo's vstring).
This patch fixes move construction and assignment in <sstream>. I'm still trying to fix the locale facet instantiations.
diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h index 66560d2..ebcc462 100644 --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -471,7 +471,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_capacity(__str._M_allocated_capacity); } - _M_set_length(__str.length()); + // Must use _M_length() here not _M_set_length() because + // basic_stringbuf relies on writing into unallocated capacity so + // we mess up the contents if we put a '\0' in the string. + _M_length(__str.length()); __str._M_data(__str._M_local_data()); __str._M_set_length(0); } diff --git a/libstdc++-v3/include/std/sstream b/libstdc++-v3/include/std/sstream index be44dae..1940ddd 100644 --- a/libstdc++-v3/include/std/sstream +++ b/libstdc++-v3/include/std/sstream @@ -64,6 +64,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION class _GLIBCXX_DEFAULT_ABI_TAG basic_stringbuf : public basic_streambuf<_CharT, _Traits> { + struct __xfer_bufptrs; public: // Types: typedef _CharT char_type; @@ -118,9 +119,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION basic_stringbuf(const basic_stringbuf&) = delete; basic_stringbuf(basic_stringbuf&& __rhs) - : __streambuf_type(static_cast<const __streambuf_type&>(__rhs)), - _M_mode(__rhs._M_mode), _M_string(std::move(__rhs._M_string)) - { __rhs._M_stringbuf_init(__rhs._M_mode); } + : basic_stringbuf(std::move(__rhs), __xfer_bufptrs(__rhs, this)) + { __rhs._M_sync(const_cast<char_type*>(__rhs._M_string.data()), 0, 0); } // 27.8.2.2 Assign and swap: @@ -130,18 +130,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION basic_stringbuf& operator=(basic_stringbuf&& __rhs) { + __xfer_bufptrs __st{__rhs, this}; const __streambuf_type& __base = __rhs; __streambuf_type::operator=(__base); this->pubimbue(__rhs.getloc()); _M_mode = __rhs._M_mode; _M_string = std::move(__rhs._M_string); - __rhs._M_stringbuf_init(__rhs._M_mode); + __rhs._M_sync(const_cast<char_type*>(__rhs._M_string.data()), 0, 0); return *this; } void swap(basic_stringbuf& __rhs) { + __xfer_bufptrs __l_st{*this, std::__addressof(__rhs)}; + __xfer_bufptrs __r_st{__rhs, this}; __streambuf_type& __base = __rhs; __streambuf_type::swap(__base); __rhs.pubimbue(this->pubimbue(__rhs.getloc())); @@ -288,6 +291,60 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // interface of basic_streambuf, taking just an int. void _M_pbump(char_type* __pbeg, char_type* __pend, off_type __off); + + private: +#if __cplusplus >= 201103L +#if _GLIBCXX_USE_CXX11_ABI + // This type captures the state of the gptr / pptr pointers as offsets + // so they can be restored in another object after moving the string. + struct __xfer_bufptrs + { + __xfer_bufptrs(const basic_stringbuf& __from, basic_stringbuf* __to) + : _M_to{__to}, _M_goff{-1, -1, -1}, _M_poff{-1, -1, -1} + { + const _CharT* __str = __from._M_string.data(); + if (__from.eback()) + { + _M_goff[0] = __from.eback() - __str; + _M_goff[1] = __from.gptr() - __str; + _M_goff[2] = __from.egptr() - __str; + } + if (__from.pbase()) + { + _M_poff[0] = __from.pbase() - __str; + _M_poff[1] = __from.pptr() - __from.pbase(); + _M_poff[2] = __from.epptr() - __str; + } + } + + ~__xfer_bufptrs() + { + char_type* __str = const_cast<char_type*>(_M_to->_M_string.data()); + if (_M_goff[0] != -1) + _M_to->setg(__str+_M_goff[0], __str+_M_goff[1], __str+_M_goff[2]); + if (_M_poff[0] != -1) + _M_to->_M_pbump(__str+_M_poff[0], __str+_M_poff[2], _M_poff[1]); + } + + basic_stringbuf* _M_to; + off_type _M_goff[3]; + off_type _M_poff[3]; + }; +#else + // This type does nothing when using Copy-On-Write strings. + struct __xfer_bufptrs + { + __xfer_bufptrs(const basic_stringbuf&, basic_stringbuf*) { } + }; +#endif + + // The move constructor initializes an __xfer_bufptrs temporary then + // delegates to this constructor to performs moves during its lifetime. + basic_stringbuf(basic_stringbuf&& __rhs, __xfer_bufptrs&&) + : __streambuf_type(static_cast<const __streambuf_type&>(__rhs)), + _M_mode(__rhs._M_mode), _M_string(std::move(__rhs._M_string)) + { } +#endif };