diff --git a/groups/bsl/bslstl/bslstl_istringstream.h b/groups/bsl/bslstl/bslstl_istringstream.h index cde1db9d00..c7ee6017be 100644 --- a/groups/bsl/bslstl/bslstl_istringstream.h +++ b/groups/bsl/bslstl/bslstl_istringstream.h @@ -99,13 +99,17 @@ BSL_OVERRIDES_STD mode" #endif #include +#include #include +#include +#include #include #include #include #include +#include #include #include @@ -143,6 +147,8 @@ class basic_istringstream typedef native_std::basic_istream BaseStream; typedef native_std::ios_base ios_base; + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + private: // NOT IMPLEMENTED basic_istringstream(const basic_istringstream&); // = delete @@ -159,20 +165,16 @@ class basic_istringstream // CREATORS explicit - basic_istringstream( - const allocator_type& basicAllocator = allocator_type()); + basic_istringstream(const allocator_type& allocator = allocator_type()); explicit - basic_istringstream( - ios_base::openmode modeBitMask, - const allocator_type& basicAllocator = allocator_type()); + basic_istringstream(ios_base::openmode modeBitMask, + const allocator_type& allocator = allocator_type()); explicit - basic_istringstream( - const StringType& initialString, - const allocator_type& basicAllocator = allocator_type()); - basic_istringstream( - const StringType& initialString, - ios_base::openmode modeBitMask, - const allocator_type& basicAllocator = allocator_type()); + basic_istringstream(const StringType& initialString, + const allocator_type& allocator = allocator_type()); + basic_istringstream(const StringType& initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator = allocator_type()); // Create a 'basic_istringstream' object. Optionally specify a // 'modeBitMask' indicating whether the underlying stream-buffer may // also be written to ('rdbuf' is created using @@ -181,15 +183,98 @@ class basic_istringstream // 'initialString' indicating the sequence of characters from which // input will be streamed. If 'initialString' is not supplied, there // will not be data to stream (until a subsequent call to the 'str' - // manipulator). Optionally specify the 'basicAllocator' used to - // supply memory. If 'basicAllocator' is not supplied, a - // default-constructed object of the (template parameter) 'ALLOCATOR' - // type is used. If the 'ALLOCATOR' argument is of type - // 'bsl::allocator' (the default), then 'basicAllocator', if supplied, - // shall be convertible to 'bslma::Allocator *'. If the 'ALLOCATOR' - // argument is of type 'bsl::allocator' and 'basicAllocator' is not - // supplied, the currently installed default allocator will be used to - // supply memory. + // manipulator). Optionally specify the 'allocator' used to supply + // memory. If 'allocator' is not supplied, a default-constructed + // object of the (template parameter) 'ALLOCATOR' type is used. If the + // 'ALLOCATOR' argument is of type 'bsl::allocator' (the default), then + // 'allocator', if supplied, shall be convertible to + // 'bslma::Allocator *'. If the 'ALLOCATOR' argument is of type + // 'bsl::allocator' and 'allocator' is not supplied, the currently + // installed default allocator will be used to supply memory. If + // 'initialString' is passed by 'MovableRef', it is left in a valid but + // unspecified state. + + explicit + basic_istringstream( + BloombergLP::bslmf::MovableRef initialString); + basic_istringstream( + BloombergLP::bslmf::MovableRef initialString, + const allocator_type& allocator); + basic_istringstream( + BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask); + basic_istringstream( + BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator); + // Create a 'basic_istringstream' object. Use the specified + // 'initialString' indicating the initial sequence of characters from + // which input will be streamed. Optionally specify a 'modeBitMask' + // indicating whether the underlying stream-buffer may also be written + // to ('rdbuf' is created using 'modeBitMask | ios_base::in'). If + // 'modeBitMask' is not supplied, 'rdbuf' will be created using + // 'ios_base::in'. Optionally specify the 'allocator' used to supply + // memory. If 'allocator' is not supplied, the allocator in + // 'initialString' is used. 'initialString' is left in a valid but + // unspecified state. + + template + basic_istringstream( + const bsl::basic_string& + initialString, + const allocator_type& allocator = allocator_type(), + typename bsl::enable_if< + !bsl::is_same::value, void *>::type = 0) + // Create a 'basic_istringstream' object. Use the specified + // 'initialString' indicating the sequence of characters from which + // input will be streamed. 'rdbuf' is created using 'ios_base::in'. + // Optionally specify the 'allocator' used to supply memory. If + // 'allocator' is not supplied, a default-constructed object of the + // (template parameter) 'ALLOCATOR' type is used. If the 'ALLOCATOR' + // argument is of type 'bsl::allocator' (the default), then + // 'allocator', if supplied, shall be convertible to + // 'bslma::Allocator *'. If the 'ALLOCATOR' argument is of type + // 'bsl::allocator' and 'allocator' is not supplied, the currently + // installed default allocator will be used to supply memory. + // + // Note: implemented inline due to Sun CC compilation error. + : BaseType(initialString.begin(), + initialString.end(), + ios_base::in, + allocator) + , BaseStream(BaseType::rdbuf()) + { + } + + template + basic_istringstream( + const bsl::basic_string& + initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator = allocator_type(), + typename bsl::enable_if< + !bsl::is_same::value, void *>::type = 0) + // Create a 'basic_istringstream' object. Use the specified + // 'initialString' indicating the sequence of characters from which + // input will be streamed. Use the specified 'modeBitMask' to indicate + // whether this buffer may be read from, written to, or both. + // Optionally specify the 'allocator' used to supply memory. If + // 'allocator' is not supplied, a default-constructed object of the + // (template parameter) 'ALLOCATOR' type is used. If the 'ALLOCATOR' + // argument is of type 'bsl::allocator' (the default), then + // 'allocator', if supplied, shall be convertible to + // 'bslma::Allocator *'. If the 'ALLOCATOR' argument is of type + // 'bsl::allocator' and 'allocator' is not supplied, the currently + // installed default allocator will be used to supply memory. + // + // Note: implemented inline due to Sun CC compilation error. + : BaseType(initialString.begin(), + initialString.end(), + modeBitMask | ios_base::in, + allocator) + , BaseStream(BaseType::rdbuf()) + { + } #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE basic_istringstream(basic_istringstream&& original); @@ -213,9 +298,18 @@ class basic_istringstream void str(const StringType& value); void str(BloombergLP::bslmf::MovableRef value); + template + typename + bsl::enable_if::value, void>::type + str(const basic_string& value) // Reset the internally buffered sequence of characters provided as // input by this stream to the specified 'value'. If 'value' is passed // by 'MovableRef', it is left in a valid but unspecified state. + // + // Note: implemented inline due to Sun CC compilation error. + { + this->rdbuf()->str(value); + } #ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS StringType str() &&; @@ -237,6 +331,23 @@ class basic_istringstream // Return the internally buffered sequence of characters maintained by // this stream object. +#ifndef BSLS_PLATFORM_CMP_SUN + // To be enabled once {DRQS 168075157} is resolved + template + typename bsl::enable_if< + bsl::IsStdAllocator::value, + basic_string >::type + str(const SALLOC& allocator) const + // Return a copy of the internally buffered sequence of characters + // maintained by this stream object in a 'basic_string' that uses the + // specified 'allocator'. + // + // Note: implemented inline due to Sun CC compilation error. + { + return this->rdbuf()->str(allocator); + } +#endif + ViewType view() const BSLS_KEYWORD_NOEXCEPT; // Return a view of the internally buffered sequence of characters // maintained by this stream object. @@ -260,8 +371,8 @@ typedef basic_istringstream, allocator > template inline basic_istringstream:: -basic_istringstream(const allocator_type& basicAllocator) -: BaseType(ios_base::in, basicAllocator) +basic_istringstream(const allocator_type& allocator) +: BaseType(ios_base::in, allocator) , BaseStream(BaseType::rdbuf()) { } @@ -270,8 +381,8 @@ template inline basic_istringstream:: basic_istringstream(ios_base::openmode modeBitMask, - const allocator_type& basicAllocator) -: BaseType(modeBitMask | ios_base::in, basicAllocator) + const allocator_type& allocator) +: BaseType(modeBitMask | ios_base::in, allocator) , BaseStream(BaseType::rdbuf()) { } @@ -280,8 +391,8 @@ template inline basic_istringstream:: basic_istringstream(const StringType& initialString, - const allocator_type& basicAllocator) -: BaseType(initialString, ios_base::in, basicAllocator) + const allocator_type& allocator) +: BaseType(initialString, ios_base::in, allocator) , BaseStream(BaseType::rdbuf()) { } @@ -291,8 +402,50 @@ inline basic_istringstream:: basic_istringstream(const StringType& initialString, ios_base::openmode modeBitMask, - const allocator_type& basicAllocator) -: BaseType(initialString, modeBitMask | ios_base::in, basicAllocator) + const allocator_type& allocator) +: BaseType(initialString, modeBitMask | ios_base::in, allocator) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_istringstream:: +basic_istringstream(BloombergLP::bslmf::MovableRef initialString) +: BaseType(MoveUtil::move(initialString), ios_base::in) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_istringstream:: +basic_istringstream(BloombergLP::bslmf::MovableRef initialString, + const allocator_type& allocator) +: BaseType(MoveUtil::move(initialString), ios_base::in, allocator) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_istringstream:: +basic_istringstream(BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask) +: BaseType(MoveUtil::move(initialString), modeBitMask | ios_base::in) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_istringstream:: +basic_istringstream(BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator) +: BaseType(MoveUtil::move(initialString), + modeBitMask | ios_base::in, + allocator) , BaseStream(BaseType::rdbuf()) { } @@ -337,9 +490,7 @@ inline void basic_istringstream::str( BloombergLP::bslmf::MovableRef value) { - typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; - StringType& lvalue = value; - this->rdbuf()->str(MoveUtil::move(lvalue)); + this->rdbuf()->str(MoveUtil::move(value)); } #ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS diff --git a/groups/bsl/bslstl/bslstl_istringstream.t.cpp b/groups/bsl/bslstl/bslstl_istringstream.t.cpp index 010c0e49a7..20ce55d69a 100644 --- a/groups/bsl/bslstl/bslstl_istringstream.t.cpp +++ b/groups/bsl/bslstl/bslstl_istringstream.t.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -47,16 +48,22 @@ // [ 3] istringstream(const A&& a); // [ 5] istringstream(openmode mask, const A& a = A()); // [ 6] istringstream(const STRING& s, const A& a = A()); +// [ 6] istringstream(MovableRef s, const A& a = A()); +// [ 6] istringstream(const OTHER_STRING& s, const A& a = A()); // [ 7] istringstream(const STRING& s, openmode mask, const A& a = A()); +// [ 7] istringstream(MovableRef s, openmode mask, const A& a = A()); +// [ 7] istringstream(const OTHER_STRING& s, openmode mask, const A& a = A()); // // MANIPULATORS // [ 3] operator=(const A&& a); // [ 4] void str(const StringType& value); // [ 4] void str(MovableRef value); +// [ 4] void str(const StringType& value); // // ACCESSORS // [ 4] StringType str() const; // [ 4] StringType str() &&; +// [ 4] StringType str(const SALLOC&); // [ 4] ViewType view() const; // [ 2] StreamBufType *rdbuf() const; // ---------------------------------------------------------------------------- @@ -213,6 +220,25 @@ void loadString(StringT *value, int length) } } +template +bool stringCouldBeMovedFrom(const StringT& s, + const typename StringT::allocator_type& otherAlloc) + // Return 'true' if the specified string 's' could be moved to another + // string that uses the specified allocator 'otherAlloc', and 'false' + // otherwise. +{ + return s.size() >= LENGTH_OF_SUFFICIENTLY_LONG_STRING && + s.get_allocator() == otherAlloc; +} + +template +bool stringWasMovedFrom(const StringT& s) + // Return 'true' if the specified string 's' was possibly moved from, and + // 'false' otherwise. +{ + return 0 == s.size(); +} + template void testCase2() { @@ -599,8 +625,10 @@ void testCase4() // Testing: // void str(const StringType& value); // void str(MovableRef value); + // void str(const StringType& value); // StringType str() const; // StringType str() &&; + // StringType str(const SALLOC&); // ViewType view() const; // ------------------------------------------------------------------------ @@ -634,6 +662,57 @@ void testCase4() ASSERT(X.str() == T); ASSERT(X.view() == T); } + + for (int tj = 0; tj < NUM_STRLEN_DATA; ++tj) { + const int LENGTH_TJ = STRLEN_DATA[tj].d_length; + + typedef bsl::basic_string< + CharT, + typename StringT::traits_type, + bslma::StdTestAllocator > TestString; + + TestString oS; const TestString& OS = oS; + loadString(&oS, LENGTH_TJ); + + // same string as 'oS'; but easier to compare + StringT mT(&da); const StringT& T = mT; + loadString(&mT, LENGTH_TJ); + + mX.str(OS); + ASSERT(X.str() == T); + ASSERT(X.view() == T); + } + +#ifndef BSLS_PLATFORM_CMP_SUN + // The call 'str(OtherAllocator) is not supported on SunOS, because the + // std::allocator there does not support rebind. This limitation can + // be lifted once we fully support C++20, where rebind is removed, and + // always goes through 'allocator_traits'. See {DRQS 168075157} and + // https://github.com/bloomberg/bde/pull/268 + for (int tj = 0; tj < NUM_STRLEN_DATA; ++tj) { + typedef std::allocator OtherAllocator; + typedef bsl::basic_string OtherString; + + const int LENGTH_TJ = STRLEN_DATA[tj].d_length; + + OtherString oS; const OtherString& OS = oS; + loadString(&oS, LENGTH_TJ); + + // same string as 'oS'; but easier to compare + StringT mT(&da); const StringT& T = mT; + loadString(&mT, LENGTH_TJ); + + mX.str(OS); + ASSERT(X.str() == T); + ASSERT(X.view() == T); + + OtherString oS2 = X.str(OtherAllocator()); + ASSERT(oS2 == oS); + } +#endif + for (int tj = 0; tj < NUM_STRLEN_DATA; ++tj) { const int LENGTH_TJ = STRLEN_DATA[tj].d_length; @@ -910,6 +989,8 @@ void testCase6() // // Testing: // istringstream(const STRING& s, const A& a = A()); + // istringstream(MovableRef s, const A& a = A()); + // istringstream(const OTHER_STRING& s, const A& a = A()); // ------------------------------------------------------------------------ if (verbose) printf("\nSTRING CTOR" @@ -1013,6 +1094,183 @@ void testCase6() ASSERTV(CONFIG, oa.numBlocksInUse(), 0 == oa.numBlocksInUse()); ASSERTV(CONFIG, noa.numBlocksInUse(), 0 == noa.numBlocksInUse()); } + + // Once again, only with MovableRef + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf("\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + StringT mS(&da); const StringT& S = mS; + loadString(&mS, LENGTH); + StringT mO(&da); const StringT& O = mO; + loadString(&mO, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(MoveUtil::move(mS)); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + ASSERTV(CONFIG, !X.eof()); + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, X.str() == O); + + if (stringCouldBeMovedFrom(S, objAllocatorPtr)) { + ASSERTV(CONFIG, O.size(), S.size(), stringWasMovedFrom(S)); + } + + CharT c = 'X'; + mX >> c; + + const CharT EXPECTED = 0 == LENGTH ? 'X' : 'a'; + + ASSERTV(CONFIG, EXPECTED == c); + ASSERTV(CONFIG, X.eof() == (0 == LENGTH)); + ASSERTV(CONFIG, X.str() == O); + + // Reclaim dynamically allocated object under test. + fa.deleteObject(objPtr); + } + + // And with a different string allocator + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef bsl::basic_string< + CharT, + bsl::char_traits, + bslma::StdTestAllocator > SallocString; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf( + "\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + bslma::TestAllocator scratch("scratch", veryVeryVeryVerbose); + + SallocString mSallocStr(&scratch); + const SallocString& sallocStr = mSallocStr; + loadString(&mSallocStr, LENGTH); + + StringT mS(&scratch); const StringT& S = mS; + loadString(&mS, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(sallocStr); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT( + sallocStr, + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(sallocStr, &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + bslma::TestAllocator& oa = *objAllocatorPtr; + bslma::TestAllocator& noa = 'c' != CONFIG ? sa : da; + + // Verify allocations from the object/non-object allocators. + + if ('N' == MEM) { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 == oa.numBlocksTotal()); + ASSERTV(CONFIG, noa.numBlocksTotal(), + 0 == noa.numBlocksTotal()); + } + else { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 != oa.numBlocksTotal()); + } + + ASSERTV(CONFIG, !X.eof()); + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, X.str() == S); + + CharT c = 'X'; + mX >> c; + + const CharT EXPECTED = 0 == LENGTH ? 'X' : 'a'; + + ASSERTV(CONFIG, EXPECTED == c); + ASSERTV(CONFIG, X.eof() == (0 == LENGTH)); + ASSERTV(CONFIG, X.str() == S); + + // Verify no temporary memory is allocated from the object + // allocator when supplied. + + if ('c' == CONFIG) { + ASSERTV(CONFIG, oa.numBlocksTotal(), oa.numBlocksInUse(), + oa.numBlocksTotal() == oa.numBlocksInUse()); + } + + // Reclaim dynamically allocated object under test. + + fa.deleteObject(objPtr); + + // Verify all memory is released on object destruction. + + ASSERTV(CONFIG, fa.numBlocksInUse(), + 0 == fa.numBlocksInUse()); + ASSERTV(CONFIG, oa.numBlocksInUse(), + 0 == oa.numBlocksInUse()); + ASSERTV(CONFIG, noa.numBlocksInUse(), + 0 == noa.numBlocksInUse()); + } } } @@ -1075,6 +1333,8 @@ void testCase7() // // Testing: // istringstream(const STRING& s, openmode mask, const A& a = A()); + // istringstream(MovableRef s, openmode mask, const A& a = A()); + // istringstream(const OTHER_STRING& s, openmode mask, const A& a = A()); // ------------------------------------------------------------------------ if (verbose) printf("\nSTRING & OPENMODE CTOR" @@ -1187,6 +1447,187 @@ void testCase7() ASSERTV(CONFIG, noa.numBlocksInUse(), 0 == noa.numBlocksInUse()); } + + // Once again, only with MovableRef + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf( + "\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + StringT mS(&da); const StringT& S = mS; + loadString(&mS, LENGTH); + StringT mO(&da); const StringT& O = mO; + loadString(&mO, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), MODE); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT( + MoveUtil::move(mS), + MODE, + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), MODE, &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + ASSERTV(CONFIG, !X.eof()); + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, X.str() == O); + + if (stringCouldBeMovedFrom(S, objAllocatorPtr)) { + ASSERTV(CONFIG, O.size(), S.size(), stringWasMovedFrom(S)); + } + + CharT c = 'X'; + mX >> c; + + const CharT EXPECTED = 0 == LENGTH ? 'X' : 'a'; + + ASSERTV(CONFIG, ti, tj, EXPECTED == c); + ASSERTV(CONFIG, ti, tj, X.eof() == (0 == LENGTH)); + ASSERTV(CONFIG, ti, tj, X.str() == O); + + // Reclaim dynamically allocated object under test. + fa.deleteObject(objPtr); + } + + // And with a different string allocator + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef bsl::basic_string< + CharT, + bsl::char_traits, + bslma::StdTestAllocator > SallocString; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf( + "\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + bslma::TestAllocator scratch("scratch", veryVeryVeryVerbose); + + SallocString mSallocStr(&scratch); + const SallocString& sallocStr = mSallocStr; + loadString(&mSallocStr, LENGTH); + + StringT mS(&scratch); const StringT& S = mS; + loadString(&mS, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(sallocStr, MODE); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT( + sallocStr, + MODE, + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(sallocStr, MODE, &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + bslma::TestAllocator& oa = *objAllocatorPtr; + bslma::TestAllocator& noa = 'c' != CONFIG ? sa : da; + + // Verify allocations from the object/non-object allocators. + + if ('N' == MEM) { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 == oa.numBlocksTotal()); + ASSERTV(CONFIG, noa.numBlocksTotal(), + 0 == noa.numBlocksTotal()); + } + else { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 != oa.numBlocksTotal()); + } + + ASSERTV(CONFIG, !X.eof()); + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, X.str() == S); + + CharT c = 'X'; + mX >> c; + + const CharT EXPECTED = 0 == LENGTH ? 'X' : 'a'; + + ASSERTV(CONFIG, EXPECTED == c); + ASSERTV(CONFIG, X.eof() == (0 == LENGTH)); + ASSERTV(CONFIG, X.str() == S); + + // Verify no temporary memory is allocated from the object + // allocator when supplied. + + if ('c' == CONFIG) { + ASSERTV(CONFIG, oa.numBlocksTotal(), oa.numBlocksInUse(), + oa.numBlocksTotal() == oa.numBlocksInUse()); + } + + // Reclaim dynamically allocated object under test. + + fa.deleteObject(objPtr); + + // Verify all memory is released on object destruction. + + ASSERTV(CONFIG, fa.numBlocksInUse(), + 0 == fa.numBlocksInUse()); + ASSERTV(CONFIG, oa.numBlocksInUse(), + 0 == oa.numBlocksInUse()); + ASSERTV(CONFIG, noa.numBlocksInUse(), + 0 == noa.numBlocksInUse()); + } } } } diff --git a/groups/bsl/bslstl/bslstl_ostringstream.h b/groups/bsl/bslstl/bslstl_ostringstream.h index 15ddead112..837525dc99 100644 --- a/groups/bsl/bslstl/bslstl_ostringstream.h +++ b/groups/bsl/bslstl/bslstl_ostringstream.h @@ -98,13 +98,17 @@ BSL_OVERRIDES_STD mode" #endif #include +#include #include +#include +#include #include #include #include #include +#include #include #include @@ -142,6 +146,8 @@ class basic_ostringstream typedef native_std::basic_ostream BaseStream; typedef native_std::ios_base ios_base; + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + private: // NOT IMPLEMENTED basic_ostringstream(const basic_ostringstream&); // = delete @@ -158,16 +164,16 @@ class basic_ostringstream // CREATORS explicit - basic_ostringstream(const allocator_type& basicAllocator=allocator_type()); + basic_ostringstream(const allocator_type& allocator = allocator_type()); explicit basic_ostringstream(ios_base::openmode modeBitMask, - const allocator_type& basicAllocator=allocator_type()); + const allocator_type& allocator = allocator_type()); explicit basic_ostringstream(const StringType& initialString, - const allocator_type& basicAllocator=allocator_type()); + const allocator_type& allocator = allocator_type()); basic_ostringstream(const StringType& initialString, ios_base::openmode modeBitMask, - const allocator_type& basicAllocator=allocator_type()); + const allocator_type& allocator = allocator_type()); // Create a 'basic_ostringstream' object. Optionally specify a // 'modeBitMask' indicating whether the underlying stream-buffer may // also be read from ('rdbuf' is created using @@ -176,16 +182,100 @@ class basic_ostringstream // an 'initialString' indicating the value that will be returned by a // call to 'str' prior to any streaming operations performed on this // object. If 'initialString' is not supplied, 'str' will initially - // return an empty string. Optionally specify the 'basicAllocator' - // used to supply memory. If 'basicAllocator' is not supplied, a + // return an empty string. Optionally specify the 'allocator' used to + // supply memory. If 'allocator' is not supplied, a // default-constructed object of the (template parameter) 'ALLOCATOR' // type is used. If the 'ALLOCATOR' argument is of type - // 'bsl::allocator' (the default), then 'basicAllocator', if supplied, + // 'bsl::allocator' (the default), then 'allocator', if supplied, // shall be convertible to 'bslma::Allocator *'. If the 'ALLOCATOR' - // argument is of type 'bsl::allocator' and 'basicAllocator' is not + // argument is of type 'bsl::allocator' and 'allocator' is not // supplied, the currently installed default allocator will be used to // supply memory. + explicit + basic_ostringstream( + BloombergLP::bslmf::MovableRef initialString); + basic_ostringstream( + BloombergLP::bslmf::MovableRef initialString, + const allocator_type& allocator); + basic_ostringstream( + BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask); + basic_ostringstream( + BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator); + // Create a 'basic_ostringstream' object. Use the specified + // 'initialString' indicating the value that will be returned by a call + // to 'str' prior to any streaming operations performed on this object. + // Optionally specify a 'modeBitMask' indicating whether the underlying + // stream-buffer may also be read from ('rdbuf' is created using + // 'modeBitMask | ios_base::out'). If 'modeBitMask' is not supplied, + // 'rdbuf' will be created using 'ios_base::out'. Optionally specify + // the 'allocator' used to supply memory. If 'allocator' is not + // supplied, the allocator in 'initialString' is used. 'initialString' + // is left in a valid but unspecified state. + + template + basic_ostringstream( + const bsl::basic_string& + initialString, + const allocator_type& allocator = allocator_type(), + typename bsl::enable_if< + !bsl::is_same::value, void *>::type = 0) + // Create a 'basic_ostringstream' object. Use the specified + // 'initialString' as the value that will be returned by a call to + // 'str' prior to any streaming operations performed on this object. + // 'rdbuf' is created using 'ios_base::out'. Optionally specify the + // 'allocator' used to supply memory. If 'allocator' is not supplied, + // a default-constructed object of the (template parameter) 'ALLOCATOR' + // type is used. If the 'ALLOCATOR' argument is of type + // 'bsl::allocator' (the default), then 'allocator', if supplied, shall + // be convertible to 'bslma::Allocator *'. If the 'ALLOCATOR' argument + // is of type 'bsl::allocator' and 'allocator' is not supplied, the + // currently installed default allocator will be used to supply memory. + // + // Note: implemented inline due to Sun CC compilation error. + : BaseType(initialString.begin(), + initialString.end(), + ios_base::out, + allocator) + , BaseStream(BaseType::rdbuf()) + { + } + + + template + basic_ostringstream( + const bsl::basic_string& + initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator = allocator_type(), + typename bsl::enable_if< + !bsl::is_same::value, void *>::type = 0) + // Create a 'basic_ostringstream' object. Use the specified + // 'initialString' as the value that will be returned by a call to + // 'str' prior to any streaming operations performed on this object. + // Use the specified 'modeBitMask' to indicate whether the underlying + // stream-buffer may also be read from ('rdbuf' is created using + // 'modeBitMask | ios_base::out'). Optionally specify the 'allocator' + // used to supply memory. If 'allocator' is not supplied, a + // default-constructed object of the (template parameter) 'ALLOCATOR' + // type is used. If the 'ALLOCATOR' argument is of type + // 'bsl::allocator' (the default), then 'allocator', if supplied, shall + // be convertible to 'bslma::Allocator *'. If the 'ALLOCATOR' argument + // is of type 'bsl::allocator' and 'allocator' is not supplied, the + // currently installed default allocator will be used to supply memory. + // + // Note: implemented inline due to Sun CC compilation error. + : BaseType(initialString.begin(), + initialString.end(), + modeBitMask | ios_base::out, + allocator) + , BaseStream(BaseType::rdbuf()) + { + } + #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE basic_ostringstream(basic_ostringstream&& original); basic_ostringstream(basic_ostringstream&& original, @@ -211,9 +301,18 @@ class basic_ostringstream void str(const StringType& value); void str(BloombergLP::bslmf::MovableRef value); + template + typename + bsl::enable_if::value, void>::type + str(const basic_string& value) // Reset the internally buffered sequence of characters maintained by // this stream object to the specified 'value'. If 'value' is passed // by 'MovableRef', it is left in a valid but unspecified state. + // + // Note: implemented inline due to Sun CC compilation error. + { + return this->rdbuf()->str(value); + } #ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS StringType str() &&; @@ -250,6 +349,23 @@ class basic_ostringstream // Return the internally buffered sequence of characters maintained by // this stream object. +#ifndef BSLS_PLATFORM_CMP_SUN + // To be enabled once {DRQS 168075157} is resolved + template + typename bsl::enable_if< + bsl::IsStdAllocator::value, + basic_string >::type + str(const SALLOC& allocator) const + // Return a copy of the internally buffered sequence of characters + // maintained by this stream object in a 'basic_string' that uses the + // specified 'allocator'. + // + // Note: implemented inline due to Sun CC compilation error. + { + return this->rdbuf()->str(allocator); + } +#endif + ViewType view() const BSLS_KEYWORD_NOEXCEPT; // Return a view of the internally buffered sequence of characters // maintained by this stream object. @@ -302,8 +418,8 @@ namespace bsl { template inline basic_ostringstream:: -basic_ostringstream(const allocator_type& basicAllocator) -: BaseType(ios_base::out, basicAllocator) +basic_ostringstream(const allocator_type& allocator) +: BaseType(ios_base::out, allocator) , BaseStream(BaseType::rdbuf()) { } @@ -312,8 +428,8 @@ template inline basic_ostringstream:: basic_ostringstream(ios_base::openmode modeBitMask, - const allocator_type& basicAllocator) -: BaseType(modeBitMask | ios_base::out, basicAllocator) + const allocator_type& allocator) +: BaseType(modeBitMask | ios_base::out, allocator) , BaseStream(BaseType::rdbuf()) { } @@ -322,8 +438,8 @@ template inline basic_ostringstream:: basic_ostringstream(const StringType& initialString, - const allocator_type& basicAllocator) -: BaseType(initialString, ios_base::out, basicAllocator) + const allocator_type& allocator) +: BaseType(initialString, ios_base::out, allocator) , BaseStream(BaseType::rdbuf()) { } @@ -333,8 +449,50 @@ inline basic_ostringstream:: basic_ostringstream(const StringType& initialString, ios_base::openmode modeBitMask, - const allocator_type& basicAllocator) -: BaseType(initialString, modeBitMask | ios_base::out, basicAllocator) + const allocator_type& allocator) +: BaseType(initialString, modeBitMask | ios_base::out, allocator) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_ostringstream:: +basic_ostringstream(BloombergLP::bslmf::MovableRef initialString) +: BaseType(MoveUtil::move(initialString), ios_base::out) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_ostringstream:: +basic_ostringstream(BloombergLP::bslmf::MovableRef initialString, + const allocator_type& allocator) +: BaseType(MoveUtil::move(initialString), ios_base::out, allocator) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_ostringstream:: +basic_ostringstream(BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask) +: BaseType(MoveUtil::move(initialString), modeBitMask | ios_base::out) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_ostringstream:: +basic_ostringstream(BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator) +: BaseType(MoveUtil::move(initialString), + modeBitMask | ios_base::out, + allocator) , BaseStream(BaseType::rdbuf()) { } @@ -390,9 +548,7 @@ inline void basic_ostringstream::str( BloombergLP::bslmf::MovableRef value) { - typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; - StringType& lvalue = value; - this->rdbuf()->str(MoveUtil::move(lvalue)); + this->rdbuf()->str(MoveUtil::move(value)); } #ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS diff --git a/groups/bsl/bslstl/bslstl_ostringstream.t.cpp b/groups/bsl/bslstl/bslstl_ostringstream.t.cpp index 8840eb99e3..49b754ad8e 100644 --- a/groups/bsl/bslstl/bslstl_ostringstream.t.cpp +++ b/groups/bsl/bslstl/bslstl_ostringstream.t.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -49,17 +50,23 @@ // [10] ostringstream(const ostringstream&& original, const A& a); // [ 5] ostringstream(openmode mask, const A& a = A()); // [ 6] ostringstream(const STRING& s, const A& a = A()); +// [ 6] ostringstream(MovableRef s, const A& a = A()); +// [ 6] ostringstream(const OTHER_STRING& s, const A& a = A()); // [ 7] ostringstream(const STRING& s, openmode mask, const A& a = A()); +// [ 7] ostringstream(MovableRef s, openmode mask, const A& a = A()); +// [ 7] ostringstream(const OTHER_STRING& s, openmode mask, const A& a = A()); // // MANIPULATORS // [ 3] operator=(const ostringstream&& original); // [ 4] void str(const StringType& value); // [ 4] void str(MovableRef value); +// [ 4] void str(const StringType& value); // [11] void swap(basic_ostringstream& other); // // ACCESSORS // [ 4] StringType str() const; // [ 4] StringType str() &&; +// [ 4] StringType str(const SALLOC&); // [ 4] ViewType view() const; // [ 2] StreamBufType *rdbuf() const; // [ 9] allocator_type get_allocator() const; @@ -215,6 +222,26 @@ void loadString(StringT *value, int length) static_cast('a' + (i % 26)); } } + +template +bool stringCouldBeMovedFrom(const StringT& s, + const typename StringT::allocator_type& otherAlloc) + // Return 'true' if the specified string 's' could be moved to another + // string that uses the specified allocator 'otherAlloc', and 'false' + // otherwise. +{ + return s.size() >= LENGTH_OF_SUFFICIENTLY_LONG_STRING && + s.get_allocator() == otherAlloc; +} + +template +bool stringWasMovedFrom(const StringT& s) + // Return 'true' if the specified string 's' was possibly moved from, and + // 'false' otherwise. +{ + return 0 == s.size(); +} + template void testCase11() { @@ -922,8 +949,10 @@ void testCase4() // Testing: // void str(const StringType& value); // void str(MovableRef value); + // void str(const StringType& value); // StringType str() const; // StringType str() &&; + // StringType str(const SALLOC&); // ViewType view() const; // ------------------------------------------------------------------------ @@ -963,6 +992,56 @@ void testCase4() ASSERT(X.view() == T); } + for (int tj = 0; tj < NUM_STRLEN_DATA; ++tj) { + const int LENGTH_TJ = STRLEN_DATA[tj].d_length; + + typedef bsl::basic_string< + CharT, + typename StringT::traits_type, + bslma::StdTestAllocator > TestString; + + TestString oS; const TestString& OS = oS; + loadString(&oS, LENGTH_TJ); + + // same string as 'oS'; but easier to compare + StringT mT(&da); const StringT& T = mT; + loadString(&mT, LENGTH_TJ); + + mX.str(OS); + ASSERT(X.str() == T); + ASSERT(X.view() == T); + } + +#ifndef BSLS_PLATFORM_CMP_SUN + // The call 'str(OtherAllocator) is not supported on SunOS, because the + // std::allocator there does not support rebind. This limitation can + // be lifted once we fully support C++20, where rebind is removed, and + // always goes through 'allocator_traits'. See {DRQS 168075157} and + // https://github.com/bloomberg/bde/pull/268 + for (int tj = 0; tj < NUM_STRLEN_DATA; ++tj) { + typedef std::allocator OtherAllocator; + typedef bsl::basic_string OtherString; + + const int LENGTH_TJ = STRLEN_DATA[tj].d_length; + + OtherString oS; const OtherString& OS = oS; + loadString(&oS, LENGTH_TJ); + + // same string as 'oS'; but easier to compare + StringT mT(&da); const StringT& T = mT; + loadString(&mT, LENGTH_TJ); + + mX.str(OS); + ASSERT(X.str() == T); + ASSERT(X.view() == T); + + OtherString oS2 = X.str(OtherAllocator()); + ASSERT(oS2 == oS); + } +#endif + for (int tj = 0; tj < NUM_STRLEN_DATA; ++tj) { const int LENGTH_TJ = STRLEN_DATA[tj].d_length; @@ -1240,6 +1319,8 @@ void testCase6() // // Testing: // ostringstream(const STRING& s, const A& a = A()); + // ostringstream(MovableRef s, const A& a = A()); + // ostringstream(const OTHER_STRING& s, const A& a = A()); // ------------------------------------------------------------------------ if (verbose) printf("\nSTRING CTOR" @@ -1340,6 +1421,176 @@ void testCase6() ASSERTV(CONFIG, oa.numBlocksInUse(), 0 == oa.numBlocksInUse()); ASSERTV(CONFIG, noa.numBlocksInUse(), 0 == noa.numBlocksInUse()); } + + // Once again, only with MovableRef + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf("\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + StringT mS(&da); const StringT& S = mS; + loadString(&mS, LENGTH); + StringT mO(&da); const StringT& O = mO; + loadString(&mO, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(MoveUtil::move(mS)); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, X.str() == O); + if (stringCouldBeMovedFrom(S, objAllocatorPtr)) { + ASSERTV(CONFIG, O.size(), S.size(), stringWasMovedFrom(S)); + } + + mX << 'X'; + + if (O.empty()) { mO.resize(1); } + mO[0] = static_cast('X'); + + ASSERTV(CONFIG, X.str() == O); + + // Reclaim dynamically allocated object under test. + fa.deleteObject(objPtr); + } + + // And with a different string allocator + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef bsl::basic_string< + CharT, + bsl::char_traits, + bslma::StdTestAllocator > SallocString; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf( + "\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + bslma::TestAllocator scratch("scratch", veryVeryVeryVerbose); + + SallocString mSallocStr(&scratch); + const SallocString& sallocStr = mSallocStr; + loadString(&mSallocStr, LENGTH); + + StringT mS(&scratch); const StringT& S = mS; + loadString(&mS, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(sallocStr); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT( + sallocStr, + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(sallocStr, &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + bslma::TestAllocator& oa = *objAllocatorPtr; + bslma::TestAllocator& noa = 'c' != CONFIG ? sa : da; + + // Verify allocations from the object/non-object allocators. + + if ('N' == MEM) { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 == oa.numBlocksTotal()); + ASSERTV(CONFIG, noa.numBlocksTotal(), + 0 == noa.numBlocksTotal()); + } + else { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 != oa.numBlocksTotal()); + } + + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, X.str() == S); + + mX << 'X'; + + if (S.empty()) { mS.resize(1); } + mS[0] = static_cast('X'); + + ASSERTV(CONFIG, X.str() == S); + + // Verify no temporary memory is allocated from the object + // allocator when supplied. + + if ('c' == CONFIG) { + ASSERTV(CONFIG, oa.numBlocksTotal(), oa.numBlocksInUse(), + oa.numBlocksTotal() == oa.numBlocksInUse()); + } + + // Reclaim dynamically allocated object under test. + + fa.deleteObject(objPtr); + + // Verify all memory is released on object destruction. + + ASSERTV(CONFIG, fa.numBlocksInUse(), + 0 == fa.numBlocksInUse()); + ASSERTV(CONFIG, oa.numBlocksInUse(), + 0 == oa.numBlocksInUse()); + ASSERTV(CONFIG, noa.numBlocksInUse(), + 0 == noa.numBlocksInUse()); + } } } @@ -1403,6 +1654,8 @@ void testCase7() // // Testing: // ostringstream(const STRING& s, openmode mask, const A& a = A()); + // ostringstream(MovableRef s, openmode mask, const A& a = A()); + // ostringstream(const OTHER_STRING& s, openmode mask, const A& a = A()); // ------------------------------------------------------------------------ if (verbose) printf("\nSTRING & OPENMODE CTOR" @@ -1517,6 +1770,192 @@ void testCase7() ASSERTV(CONFIG, noa.numBlocksInUse(), 0 == noa.numBlocksInUse()); } + + // Once again, only with MovableRef + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf( + "\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + StringT mS(&da); const StringT& S = mS; + loadString(&mS, LENGTH); + StringT mO(&da); const StringT& O = mO; + loadString(&mO, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), MODE); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT( + MoveUtil::move(mS), + MODE, + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), MODE, &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, X.str() == O); + + if (stringCouldBeMovedFrom(S, objAllocatorPtr)) { + ASSERTV(CONFIG, O.size(), S.size(), stringWasMovedFrom(S)); + } + + + mX << 'X'; + + if (MODE & IosBase::ate) { + mO.push_back(static_cast('X')); + } + else { + if (O.empty()) { mO.resize(1); } + mO[0] = static_cast('X'); + } + + ASSERTV(CONFIG, X.str() == O); + + // Reclaim dynamically allocated object under test. + fa.deleteObject(objPtr); + } + + // And with a different string allocator + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef bsl::basic_string< + CharT, + bsl::char_traits, + bslma::StdTestAllocator > SallocString; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf( + "\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + bslma::TestAllocator scratch("scratch", veryVeryVeryVerbose); + + SallocString mSallocStr(&scratch); + const SallocString& sallocStr = mSallocStr; + loadString(&mSallocStr, LENGTH); + + StringT mS(&scratch); const StringT& S = mS; + loadString(&mS, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(sallocStr, MODE); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT( + sallocStr, + MODE, + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(sallocStr, MODE, &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + bslma::TestAllocator& oa = *objAllocatorPtr; + bslma::TestAllocator& noa = 'c' != CONFIG ? sa : da; + + // Verify allocations from the object/non-object allocators. + + if ('N' == MEM) { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 == oa.numBlocksTotal()); + ASSERTV(CONFIG, noa.numBlocksTotal(), + 0 == noa.numBlocksTotal()); + } + else { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 != oa.numBlocksTotal()); + } + + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, X.str() == S); + + mX << 'X'; + + if (MODE & IosBase::ate) { + mS.push_back(static_cast('X')); + } + else { + if (S.empty()) { mS.resize(1); } + mS[0] = static_cast('X'); + } + + ASSERTV(CONFIG, X.str() == S); + + // Verify no temporary memory is allocated from the object + // allocator when supplied. + + if ('c' == CONFIG && !(MODE & IosBase::ate)) { + ASSERTV(CONFIG, oa.numBlocksTotal(), oa.numBlocksInUse(), + oa.numBlocksTotal() == oa.numBlocksInUse()); + } + + // Reclaim dynamically allocated object under test. + + fa.deleteObject(objPtr); + + // Verify all memory is released on object destruction. + + ASSERTV(CONFIG, fa.numBlocksInUse(), + 0 == fa.numBlocksInUse()); + ASSERTV(CONFIG, oa.numBlocksInUse(), + 0 == oa.numBlocksInUse()); + ASSERTV(CONFIG, noa.numBlocksInUse(), + 0 == noa.numBlocksInUse()); + } } } } diff --git a/groups/bsl/bslstl/bslstl_stringbuf.h b/groups/bsl/bslstl/bslstl_stringbuf.h index 5dc4d21a77..e95589f1e7 100644 --- a/groups/bsl/bslstl/bslstl_stringbuf.h +++ b/groups/bsl/bslstl/bslstl_stringbuf.h @@ -150,15 +150,19 @@ BSL_OVERRIDES_STD mode" #include +#include #include #include +#include +#include #include #include #include #include #include +#include #include #include @@ -208,6 +212,8 @@ class basic_stringbuf typedef bsl::basic_string StringType; typedef bsl::basic_string_view ViewType; + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + public: // TYPES typedef CHAR_TYPE char_type; @@ -448,6 +454,88 @@ class basic_stringbuf // 'bsl::allocator' and 'allocator' is not supplied, the currently // installed default allocator will be used to supply memory. + explicit + basic_stringbuf(BloombergLP::bslmf::MovableRef + initialString); + basic_stringbuf(BloombergLP::bslmf::MovableRef + initialString, + const allocator_type& allocator); + basic_stringbuf(BloombergLP::bslmf::MovableRef + initialString, + ios_base::openmode modeBitMask); + basic_stringbuf(BloombergLP::bslmf::MovableRef + initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator); + // Create a 'basic_stringbuf' object. Use the specified + // 'initialString' indicating the initial sequence of characters that + // this buffer will access or manipulate. Optionally specify a + // 'modeBitMask' indicating whether this buffer may be read from, + // written to, or both. If 'modeBitMask' is not supplied, this buffer + // is created with 'ios_base::in | ios_base::out'. Optionally specify + // the 'allocator' used to supply memory. If 'allocator' is not + // supplied, the allocator in 'initialString' is used. 'initialString' + // is left in a valid but unspecified state. + + template + explicit + basic_stringbuf( + const bsl::basic_string& + initialString, + const allocator_type& allocator = allocator_type(), + typename bsl::enable_if< + !bsl::is_same::value, void *>::type = 0) + + // Create a 'basic_stringbuf' object. Use the specified + // 'initialString' indicating the initial sequence of characters that + // this buffer will access or manipulate. Optionally specify the + // 'allocator' used to supply memory. If 'allocator' is not supplied, + // a default-constructed object of the (template parameter) 'ALLOCATOR' + // type is used. If the 'ALLOCATOR' argument is of type + // 'bsl::allocator' (the default), then 'allocator', if supplied, shall + // be convertible to 'bslma::Allocator *'. If the 'ALLOCATOR' argument + // is of type 'bsl::allocator' and 'allocator' is not supplied, the + // currently installed default allocator will be used to supply memory. + // + // Note: implemented inline due to Sun CC compilation error. + : BaseType() + , d_str(initialString.data(), initialString.size(), allocator) + , d_endHint(initialString.size()) + , d_mode(ios_base::in | ios_base::out) + { + updateStreamPositions(); + } + + template + basic_stringbuf( + const bsl::basic_string& + initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator = allocator_type(), + typename bsl::enable_if< + !bsl::is_same::value, void *>::type = 0) + // Create a 'basic_stringbuf' object. Use the specified + // 'initialString' indicating the initial sequence of characters that + // this buffer will access or manipulate. Use the specified + // 'modeBitMask' to indicate whether this buffer may be read from, + // written to, or both. Optionally specify the 'allocator' used to + // supply memory. If 'allocator' is not supplied, a + // default-constructed object of the (template parameter) 'ALLOCATOR' + // type is used. If the 'ALLOCATOR' argument is of type + // 'bsl::allocator' (the default), then 'allocator', if supplied, shall + // be convertible to 'bslma::Allocator *'. If the 'ALLOCATOR' argument + // is of type 'bsl::allocator' and 'allocator' is not supplied, the + // currently installed default allocator will be used to supply memory. + // + // Note: implemented inline due to Sun CC compilation error. + : BaseType() + , d_str(initialString.data(), initialString.size(), allocator) + , d_endHint(initialString.size()) + , d_mode(modeBitMask) + { + updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0); + } + #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE basic_stringbuf(basic_stringbuf&& original); basic_stringbuf(basic_stringbuf&& original, @@ -473,6 +561,10 @@ class basic_stringbuf void str(const StringType& value); void str(BloombergLP::bslmf::MovableRef value); + template + typename + bsl::enable_if::value, void>::type + str(const basic_string& value) // Reset the internally buffered sequence of characters to the // specified 'value'. Update the beginning and end of both the input // and output sequences to be the beginning and end of the updated @@ -480,6 +572,13 @@ class basic_stringbuf // updated buffer, and update the current output position to be the end // of the updated buffer. If 'value' is passed by 'MovableRef', then // it is left in an unspecified but valid state. + // + // Note: implemented inline due to Sun CC compilation error. + { + d_str.assign(value.data(), value.size()); + d_endHint = d_str.size(); + updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0); + } #ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS StringType str() &&; @@ -526,6 +625,30 @@ class basic_stringbuf // in the buffer. Otherwise this object has been created in neither // input nor output mode and a zero length 'StringType' is returned. +#ifndef BSLS_PLATFORM_CMP_SUN + // To be enabled once {DRQS 168075157} is resolved + template + typename bsl::enable_if< + bsl::IsStdAllocator::value, + basic_string >::type + str(const SALLOC& allocator) const + // Return the currently buffered sequence of characters in a + // 'basic_string' that uses the specified 'allocator'. If this object + // was created only in input mode, the resultant 'basic_string' + // contains the character sequence in the range '[eback(), egptr())'. + // If 'modeBitMask & ios_base::out' specified at construction is + // nonzero then the resultant 'basic_string' contains the character + // sequence in the range '[pbase(), high_mark)', where 'high_mark' + // represents the position one past the highest initialized character + // in the buffer. Otherwise this object has been created in neither + // input nor output mode and a zero length 'basic_string' is returned. + // + // Note: implemented inline due to Sun CC compilation error. + { + return basic_string(view(), allocator); + } +#endif + ViewType view() const BSLS_KEYWORD_NOEXCEPT; // Return a 'string_view' containing the currently buffered sequence of // characters. If this object was created only in input mode, the @@ -583,6 +706,8 @@ class StringBufContainer { typedef basic_stringbuf StreamBufType; typedef bsl::basic_string StringType; + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + // DATA StreamBufType d_bufObj; // contained 'basic_stringbuf' @@ -618,6 +743,32 @@ class StringBufContainer { { } + StringBufContainer( + BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask, + const ALLOCATOR& allocator) + : d_bufObj(MoveUtil::move(initialString), modeBitMask, allocator) + { + } + + StringBufContainer( + BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask) + : d_bufObj(MoveUtil::move(initialString), modeBitMask) + { + } + + template + StringBufContainer(STRING_ITER first, + STRING_ITER last, + ios_base::openmode modeBitMask, + const ALLOCATOR& allocator) + : d_bufObj(modeBitMask, allocator) + { + StringType tempStr(first, last, allocator); + d_bufObj.str(MoveUtil::move(tempStr)); + } + #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE StringBufContainer(StringBufContainer&& original) // Create a 'StringBufContainer' object having the same value as the @@ -1165,6 +1316,58 @@ basic_stringbuf:: updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0); } +template +inline +basic_stringbuf:: + basic_stringbuf(BloombergLP::bslmf::MovableRef initialString) +: BaseType() +, d_str(MoveUtil::move(initialString)) +, d_endHint(d_str.size()) +, d_mode(ios_base::in | ios_base::out) +{ + updateStreamPositions(); +} + +template +inline +basic_stringbuf:: + basic_stringbuf(BloombergLP::bslmf::MovableRef initialString, + const allocator_type& allocator) +: BaseType() +, d_str(MoveUtil::move(initialString), allocator) +, d_endHint(d_str.size()) +, d_mode(ios_base::in | ios_base::out) +{ + updateStreamPositions(); +} + +template +inline +basic_stringbuf:: + basic_stringbuf(BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask) +: BaseType() +, d_str(MoveUtil::move(initialString)) +, d_endHint(d_str.size()) +, d_mode(modeBitMask) +{ + updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0); +} + +template +inline +basic_stringbuf:: + basic_stringbuf(BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator) +: BaseType() +, d_str(MoveUtil::move(initialString), allocator) +, d_endHint(d_str.size()) +, d_mode(modeBitMask) +{ + updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0); +} + #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE template inline @@ -1300,7 +1503,6 @@ template void basic_stringbuf::str( BloombergLP::bslmf::MovableRef value) { - typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; StringType& lvalue = value; d_str = MoveUtil::move(lvalue); @@ -1330,7 +1532,7 @@ basic_stringbuf::str() && StringType ret = std::move(d_str); d_endHint = 0; updateStreamPositions(); - return std::move(ret); + return ret; } #endif diff --git a/groups/bsl/bslstl/bslstl_stringbuf.t.cpp b/groups/bsl/bslstl/bslstl_stringbuf.t.cpp index ac761d7020..b0550eff65 100644 --- a/groups/bsl/bslstl/bslstl_stringbuf.t.cpp +++ b/groups/bsl/bslstl/bslstl_stringbuf.t.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,8 @@ using namespace BloombergLP; // [ 2] stringbuf(ios_base::openmode, const ALLOCATOR&) // [ 2] stringbuf(const string&, const ALLOCATOR&) // [ 2] stringbuf(const string&, ios_base::openmode, const ALLOCATOR&) +// [ 2] stringbuf(const SALLOCSTR&, const ALLOCATOR&) +// [ 2] stringbuf(const SALLOCSTR&, ios_base::openmode, const ALLOCATOR&) // [ 4] stringbuf(stringbuf&&) // [17] stringbuf(stringbuf&&, const ALLOCATOR&); // [ 4] operator=(stringbuf&&) @@ -62,8 +65,10 @@ using namespace BloombergLP; // [ 3] allocator_type get_allocator() const; // [19] void str(const StringType& value); // [19] void str(BloombergLP::bslmf::MovableRef value); +// [19] void str(const basic_string& value); // [ 3] StringType str() const; // [ 3] StringType str() &&; +// [ 3] StringType str(const SALLOC&); // [ 3] ViewType view() const; // [18] void swap(basic_stringbuf& other); // @@ -205,6 +210,25 @@ void loadString(STRING *value, int length) } } +template +bool stringCouldBeMovedFrom(const StringT& s, + const typename StringT::allocator_type& otherAlloc) + // Return 'true' if the specified string 's' could be moved to another + // string that uses the specified allocator 'otherAlloc', and 'false' + // otherwise. +{ + return s.size() >= LENGTH_OF_SUFFICIENTLY_LONG_STRING && + s.get_allocator() == otherAlloc; +} + +template +bool stringWasMovedFrom(const StringT& s) + // Return 'true' if the specified string 's' was possibly moved from, and + // 'false' otherwise. +{ + return 0 == s.size(); +} + template class StringBufTest : public bsl::basic_stringbuf { @@ -649,6 +673,8 @@ class StringBufTest : public bsl::basic_stringbuf { L_, "ABCDEFGHIJKLABCDEFGHIJKLABCDEFGHIJKLABCDEFGHIJKLABCDEFG" }, }; const std::size_t NUM_DATA = sizeof DATA / sizeof *DATA; + typedef std::allocator OtherAllocator; + OtherAllocator otherAlloc; for (std::size_t i = 0; i != NUM_DATA; ++i) { const int LINE = DATA[i].d_line; @@ -704,11 +730,81 @@ class StringBufTest : public bsl::basic_stringbuf &da == RESULT_S.get_allocator()); ASSERTV(LINE, CONFIG, initialString == RESULT_V); +#ifndef BSLS_PLATFORM_CMP_SUN + // These calls are not supported on SunOS, because the std::allocator + // there does not support rebind. This limitation can be lifted once + // we fully support C++20, where rebind is removed, and always goes + // through 'allocator_traits'. See {DRQS 168075157} and + // https://github.com/bloomberg/bde/pull/268 + // test 'str(otherAllocator)' + const bsl::basic_string< + TYPE, + bsl::char_traits, + OtherAllocator> otherAllocString = X.str(otherAlloc); + + ASSERTV(LINE, CONFIG, X.view() == otherAllocString); + ASSERTV(otherAllocString.get_allocator() == otherAlloc); +#endif + fa.deleteObject(objPtr); } } +#ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS + { + // test 'str() &&' + for (std::size_t i = 0; i != NUM_DATA; ++i) { + const int LINE = DATA[i].d_line; + const char *SPEC = DATA[i].d_spec_p; + + StringType initialString; + populateString(&initialString, SPEC); + + for (char cfg = 'a'; cfg <= 'b'; ++cfg) { + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default"); + bslma::TestAllocator fa("footprint"); + bslma::TestAllocator sa("supplied"); + + bslma::DefaultAllocatorGuard dag(&da); + + Obj *objPtr = 0; + bslma::TestAllocator *objAllocatorPtr = 0; + + switch (CONFIG) { + case 'a': { + objAllocatorPtr = &da; + objPtr = new (fa) Obj(initialString); + } break; + case 'b': { + objAllocatorPtr = &sa; + objPtr = new (fa) Obj(initialString, objAllocatorPtr); + } break; + default: { + BSLS_ASSERT_OPT(!"Bad allocator config."); + } break; + } + + Obj& mX = *objPtr; + const Obj& X = mX; + + ASSERTV(LINE, CONFIG, initialString == X.str()); + ASSERTV(LINE, CONFIG, initialString == X.view()); + + const StringType movedStr = std::move(mX).str(); + ASSERTV(LINE, CONFIG, initialString == movedStr); + ASSERTV(LINE, CONFIG, X.view().empty()); + + fa.deleteObject(objPtr); + } + } + } +#endif + { + // test 'str(StringType)' bslma::TestAllocator ta("test allocator"); static const struct { @@ -773,54 +869,153 @@ class StringBufTest : public bsl::basic_stringbuf } } -#ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS { - // test 'str() &&' - for (std::size_t i = 0; i != NUM_DATA; ++i) { - const int LINE = DATA[i].d_line; - const char *SPEC = DATA[i].d_spec_p; + // test 'str(MoveableRef)' + bslma::TestAllocator ta("test allocator"); - StringType initialString; - populateString(&initialString, SPEC); + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; - for (char cfg = 'a'; cfg <= 'b'; ++cfg) { + static const struct { + int d_line; + Mode d_mode; + } OPENMODE_DATA[] = + { + { L_, IosBase::in }, + { L_, IosBase::out }, + { L_, IosBase::in | IosBase::out }, + { L_, IosBase::ate }, + { L_, IosBase::in | IosBase::ate }, + { L_, IosBase::out | IosBase::ate }, + { L_, IosBase::in | IosBase::out | IosBase::ate } + }; + const int NUM_OPENMODE_DATA = sizeof OPENMODE_DATA / + sizeof *OPENMODE_DATA; - const char CONFIG = cfg; // how we specify the allocator + for (int i = 0; i != NUM_OPENMODE_DATA; ++i) { + const int LINE = OPENMODE_DATA[i].d_line; + const Mode MODE = OPENMODE_DATA[i].d_mode; + const char SPEC1[] = "ABCDEFGHIJKLABCDEFGHIJKLABCDEFGHIJKL"; + const char SPEC2[] = "ABCDEFGHIJKLABCDEF"; - bslma::TestAllocator da("default"); - bslma::TestAllocator fa("footprint"); - bslma::TestAllocator sa("supplied"); + StringType initialString1; + populateString(&initialString1, SPEC1); - bslma::DefaultAllocatorGuard dag(&da); + StringBufTest mX(initialString1, MODE); - Obj *objPtr = 0; - bslma::TestAllocator *objAllocatorPtr = 0; + if (MODE & IosBase::out) { + ASSERTV(LINE, mX.pbase() && mX.epptr()); + if (MODE & IosBase::in) { + ASSERTV(LINE, mX.eback() && mX.egptr()); + } + else { + ASSERTV(LINE, (0 == mX.eback()) && + (0 == mX.egptr())); + } + StringType STR1(mX.pbase(), mX.epptr(), &ta); + ASSERTV(LINE, mX.str() == STR1); - switch (CONFIG) { - case 'a': { - objAllocatorPtr = &da; - objPtr = new (fa) Obj(initialString); - } break; - case 'b': { - objAllocatorPtr = &sa; - objPtr = new (fa) Obj(initialString, objAllocatorPtr); - } break; - default: { - BSLS_ASSERT_OPT(!"Bad allocator config."); - } break; + StringType initialString2; + populateString(&initialString2, SPEC2); + StringType sourceString = initialString2; + mX.str(MoveUtil::move(sourceString)); + + ASSERTV(LINE, mX.str() != STR1); + // test strings that will be changed by a move + if (initialString2.size() >= + LENGTH_OF_SUFFICIENTLY_LONG_STRING) { + ASSERT(sourceString.empty()); } + ASSERTV(LINE, mX.str() == initialString2); + ASSERTV(LINE, mX.view() == initialString2); + } + else if (MODE & IosBase::in) { + ASSERTV(LINE, mX.eback(), mX.eback() && mX.egptr()); + StringType STR(mX.eback(), mX.egptr(), &ta); + ASSERTV(LINE, mX.str() == STR); + ASSERTV(LINE, mX.view() == STR); + } + else { + ASSERTV(LINE, mX.str().empty()); + ASSERTV(LINE, mX.view().empty()); + } + } + } - Obj& mX = *objPtr; - const Obj& X = mX; +#ifndef BSLS_PLATFORM_CMP_SUN + // These calls are not supported on SunOS, because the std::allocator + // there does not support rebind. This limitation can be lifted once + // we fully support C++20, where rebind is removed, and always goes + // through 'allocator_traits'. See {DRQS 168075157} and + // https://github.com/bloomberg/bde/pull/268 + { + // test 'str(String with different Allocator)' + bslma::TestAllocator ta("test allocator"); - ASSERTV(LINE, CONFIG, initialString == X.str()); - ASSERTV(LINE, CONFIG, initialString == X.view()); + static const struct { + int d_line; + Mode d_mode; + } OPENMODE_DATA[] = + { + { L_, IosBase::in }, + { L_, IosBase::out }, + { L_, IosBase::in | IosBase::out }, + { L_, IosBase::ate }, + { L_, IosBase::in | IosBase::ate }, + { L_, IosBase::out | IosBase::ate }, + { L_, IosBase::in | IosBase::out | IosBase::ate } + }; + const int NUM_OPENMODE_DATA = sizeof OPENMODE_DATA / + sizeof *OPENMODE_DATA; - const StringType movedStr = std::move(mX).str(); - ASSERTV(LINE, CONFIG, initialString == movedStr); - ASSERTV(LINE, CONFIG, X.view().empty()); + for (int i = 0; i != NUM_OPENMODE_DATA; ++i) { + const int LINE = OPENMODE_DATA[i].d_line; + const Mode MODE = OPENMODE_DATA[i].d_mode; + const char SPEC1[] = "ABCDEFGHIJKLABCDEFGHIJKLABCDEFGHIJKL"; + const char SPEC2[] = "ABCDEFGHIJKLABCDEF"; - fa.deleteObject(objPtr); + StringType initialString1; + populateString(&initialString1, SPEC1); + + StringBufTest mX(initialString1, MODE); + + if (MODE & IosBase::out) { + ASSERTV(LINE, mX.pbase() && mX.epptr()); + if (MODE & IosBase::in) { + ASSERTV(LINE, mX.eback() && mX.egptr()); + } + else { + ASSERTV(LINE, (0 == mX.eback()) && + (0 == mX.egptr())); + } + StringType STR1(mX.pbase(), mX.epptr(), &ta); + ASSERTV(LINE, mX.str() == STR1); + + StringType initialString2; + populateString(&initialString2, SPEC2); + + bsl::basic_string, + OtherAllocator> + otherAllocString(initialString2.data(), + initialString2.size()); + + mX.str(otherAllocString); + + StringType STR2(mX.pbase(), mX.epptr(), &ta); + ASSERTV(LINE, mX.str() != STR2); + ASSERTV(LINE, mX.str() == initialString2); + ASSERTV(LINE, mX.view() == otherAllocString); + ASSERTV(LINE, mX.view() == initialString2); + } + else if (MODE & IosBase::in) { + ASSERTV(LINE, mX.eback(), mX.eback() && mX.egptr()); + StringType STR(mX.eback(), mX.egptr(), &ta); + ASSERTV(LINE, mX.str() == STR); + ASSERTV(LINE, mX.view() == STR); + } + else { + ASSERTV(LINE, mX.str().empty()); + ASSERTV(LINE, mX.view().empty()); } } } @@ -1716,6 +1911,7 @@ int main(int argc, char *argv[]) // Testing: // void str(const StringType& value); // void str(BloombergLP::bslmf::MovableRef value); + // void str(const basic_string& value); // -------------------------------------------------------------------- if (verbose) printf("\nTESTING 'str' MANIPULATOR" @@ -2703,6 +2899,7 @@ int main(int argc, char *argv[]) // allocator_type get_allocator() const; // StringType str() const; // StringType str() &&; + // StringType str(const SALLOC&); // ViewType view() const; // -------------------------------------------------------------------- @@ -2787,6 +2984,81 @@ int main(int argc, char *argv[]) { bsl::stringbuf buf(bsl::string("something")); ASSERT(buf.str() == "something"); + + typedef bslmf::MovableRefUtil MoveUtil; + bslma::TestAllocator ta1("Test Allocator #1"); + bslma::TestAllocator ta2("Test Allocator #2"); + const bsl::ios_base::openmode mode = std::ios_base::in; + + for (int tj = 0; tj < NUM_STRLEN_DATA; ++tj) { + const int LENGTH_TJ = STRLEN_DATA[tj].d_length; + + // string used for comparisons + bsl::string oT; bsl::string& O = oT; + loadString(&oT, LENGTH_TJ); + + // strings used for constructions + bsl::string str1(&ta1); + bsl::string str2(&ta2); + ASSERT(str1.get_allocator() != str2.get_allocator()); + + // test bsl::stringbuf(MovableRef) + { + loadString(&str1, LENGTH_TJ); + bsl::stringbuf buf1(MoveUtil::move(str1)); + ASSERT(buf1.str() == O); + ASSERT(buf1.get_allocator() == str1.get_allocator()); + if (stringCouldBeMovedFrom(str1, &ta1)) { + ASSERT(stringWasMovedFrom(str1)); + } + } + + // test bsl::stringbuf(MovableRef, mode) + { + loadString(&str1, LENGTH_TJ); + bsl::stringbuf buf1(MoveUtil::move(str1), mode); + ASSERT(buf1.str() == O); + ASSERT(buf1.get_allocator() == str1.get_allocator()); + if (stringCouldBeMovedFrom(str1, &ta1)) { + ASSERT(stringWasMovedFrom(str1)); + } + } + // test bsl::stringbuf(MovableRef, allocator) + { + loadString(&str1, LENGTH_TJ); + loadString(&str2, LENGTH_TJ); + bsl::stringbuf buf1(MoveUtil::move(str1), &ta1); + bsl::stringbuf buf2(MoveUtil::move(str2), &ta1); // diff + ASSERT(buf1.str() == O); + ASSERT(buf1.get_allocator() == str1.get_allocator()); + if (stringCouldBeMovedFrom(str1, &ta1)) { + ASSERT(stringWasMovedFrom(str1)); + } + + ASSERT(buf2.str() == O); + ASSERT(buf2.get_allocator() == bsl::allocator(&ta1)); + ASSERT(0 == LENGTH_TJ || !stringWasMovedFrom(str2)); + } + + // test bsl::stringbuf(MovableRef, mode, allocator) + { + loadString(&str1, LENGTH_TJ); + loadString(&str2, LENGTH_TJ); + bsl::stringbuf buf1(MoveUtil::move(str1), mode, &ta1); + bsl::stringbuf buf2(MoveUtil::move(str2), mode, &ta1); + ASSERT(buf1.str() == O); + ASSERT(buf1.get_allocator() == str1.get_allocator()); + if (stringCouldBeMovedFrom(str1, &ta1)) { + ASSERT(stringWasMovedFrom(str1)); + } + + ASSERT(buf2.str() == O); + ASSERT(buf2.get_allocator() == bsl::allocator(&ta1)); + ASSERT(0 == LENGTH_TJ || !stringWasMovedFrom(str2)); + } + + + } } if (veryVerbose) @@ -2799,6 +3071,22 @@ int main(int argc, char *argv[]) bsl::stringbuf buf3(bsl::string("something"), std::ios_base::in, bslma::Default::allocator()); + + bsl::basic_string, + bslma::StdTestAllocator > s1("something"); + + bsl::stringbuf buf4(s1); + ASSERT(buf4.str() == "something"); + + bsl::stringbuf buf5(s1, bsl::allocator()); + ASSERT(buf5.str() == "something"); + + bsl::stringbuf buf6(s1, std::ios_base::in); + ASSERT(buf6.str() == "something"); + + bsl::stringbuf buf7(s1, std::ios_base::in, bsl::allocator()); + ASSERT(buf7.str() == "something"); } #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE diff --git a/groups/bsl/bslstl/bslstl_stringstream.h b/groups/bsl/bslstl/bslstl_stringstream.h index 03d7e3f76a..39ae5fedac 100644 --- a/groups/bsl/bslstl/bslstl_stringstream.h +++ b/groups/bsl/bslstl/bslstl_stringstream.h @@ -107,18 +107,22 @@ BSL_OVERRIDES_STD mode" #endif #include +#include +#include + +#include +#include #include #include #include #include +#include #include #include #include -#include - #include #include @@ -151,6 +155,8 @@ class basic_stringstream typedef native_std::basic_iostream BaseStream; typedef native_std::ios_base ios_base; + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + private: // NOT IMPLEMENTED basic_stringstream(const basic_stringstream&); // = delete @@ -167,20 +173,16 @@ class basic_stringstream // CREATORS explicit - basic_stringstream( - const allocator_type& basicAllocator = allocator_type()); + basic_stringstream(const allocator_type& allocator = allocator_type()); explicit - basic_stringstream( - ios_base::openmode modeBitMask, - const allocator_type& basicAllocator = allocator_type()); + basic_stringstream(ios_base::openmode modeBitMask, + const allocator_type& allocator = allocator_type()); explicit - basic_stringstream( - const StringType& initialString, - const allocator_type& basicAllocator = allocator_type()); - basic_stringstream( - const StringType& initialString, - ios_base::openmode modeBitMask, - const allocator_type& basicAllocator = allocator_type()); + basic_stringstream(const StringType& initialString, + const allocator_type& allocator = allocator_type()); + basic_stringstream(const StringType& initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator = allocator_type()); // Create a 'basic_stringstream' object. Optionally specify a // 'modeBitMask' indicating whether the underlying stream-buffer may be // read from, written to, or both ('rdbuf' is created using @@ -189,14 +191,98 @@ class basic_stringstream // 'initialString' indicating the initial sequence of characters that // this stream may access or manipulate. If 'initialString' is not // supplied, the initial sequence of characters will be empty. - // Optionally specify the 'basicAllocator' used to supply memory. If - // 'basicAllocator' is not supplied, a default-constructed object of - // the (template parameter) 'ALLOCATOR' type is used. If the - // 'ALLOCATOR' argument is of type 'bsl::allocator' (the default), then - // 'basicAllocator', if supplied, shall be convertible to + // Optionally specify the 'allocator' used to supply memory. If + // 'allocator' is not supplied, a default-constructed object of the + // (template parameter) 'ALLOCATOR' type is used. If the 'ALLOCATOR' + // argument is of type 'bsl::allocator' (the default), then + // 'allocator', if supplied, shall be convertible to // 'bslma::Allocator *'. If the 'ALLOCATOR' argument is of type - // 'bsl::allocator' and 'basicAllocator' is not supplied, the currently - // installed default allocator will be used to supply memory. + // 'bsl::allocator' and 'allocator' is not supplied, the currently + // installed default allocator will be used to supply memory. If + // 'initialString' is passed by 'MovableRef', it is left in a valid but + // unspecified state. + + explicit + basic_stringstream( + BloombergLP::bslmf::MovableRef initialString); + basic_stringstream( + BloombergLP::bslmf::MovableRef initialString, + const allocator_type& allocator); + basic_stringstream( + BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask); + basic_stringstream( + BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator); + // Create a 'basic_stringstream' object. Use the specified + // 'initialString' indicating the initial sequence of characters that + // this buffer will access or manipulate. Optionally specify a + // 'modeBitMask' indicating whether this buffer may be read from, + // written to, or both. If 'modeBitMask' is not supplied, this buffer + // is created with 'ios_base::in | ios_base::out'. Optionally specify + // the 'allocator' used to supply memory. If 'allocator' is not + // supplied, the allocator in 'initialString' is used. 'initialString' + // is left in a valid but unspecified state. + + template + basic_stringstream( + const bsl::basic_string& + initialString, + const allocator_type& allocator = allocator_type(), + typename bsl::enable_if< + !bsl::is_same::value, void *>::type = 0) + // Create a 'basic_stringstream' object. Use the specified + // 'initialString' indicating the initial sequence of characters that + // this stream will access or manipulate. 'rdbuf' is created using + // 'ios_base::in | ios_base::out'. Optionally specify the 'allocator' + // used to supply memory. If 'allocator' is not supplied, a + // default-constructed object of the (template parameter) 'ALLOCATOR' + // type is used. If the 'ALLOCATOR' argument is of type + // 'bsl::allocator' (the default), then 'allocator', if supplied, shall + // be convertible to 'bslma::Allocator *'. If the 'ALLOCATOR' argument + // is of type 'bsl::allocator' and 'allocator' is not supplied, the + // currently installed default allocator will be used to supply memory. + // + // Note: implemented inline due to Sun CC compilation error. + : BaseType(initialString.begin(), + initialString.end(), + ios_base::in | ios_base::out, + allocator) + , BaseStream(BaseType::rdbuf()) + { + } + + template + basic_stringstream( + const bsl::basic_string& + initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator = allocator_type(), + typename bsl::enable_if< + !bsl::is_same::value, void *>::type = 0) + // Create a 'basic_stringstream' object. Use the specified + // 'initialString' indicating the initial sequence of characters that + // this stream will access or manipulate. Use the specified + // 'modeBitMask' to indicate whether this stream may be read from, + // written to, or both. Optionally specify the 'allocator' used to + // supply memory. If 'allocator' is not supplied, a + // default-constructed object of the (template parameter) 'ALLOCATOR' + // type is used. If the 'ALLOCATOR' argument is of type + // 'bsl::allocator' (the default), then 'allocator', if supplied, shall + // be convertible to 'bslma::Allocator *'. If the 'ALLOCATOR' argument + // is of type 'bsl::allocator' and 'allocator' is not supplied, the + // currently installed default allocator will be used to supply memory. + // + // Note: implemented inline due to Sun CC compilation error. + : BaseType(initialString.begin(), + initialString.end(), + modeBitMask, + allocator) + , BaseStream(BaseType::rdbuf()) + { + } + #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE basic_stringstream(basic_stringstream&& original); @@ -220,9 +306,18 @@ class basic_stringstream void str(const StringType& value); void str(BloombergLP::bslmf::MovableRef value); + template + typename + bsl::enable_if::value, void>::type + str(const basic_string& value) // Reset the internally buffered sequence of characters maintained by // this stream to the specified 'value'. If 'value' is passed by // 'MovableRef', it is left in a valid but unspecified state. + // + // Note: implemented inline due to Sun CC compilation error. + { + return this->rdbuf()->str(value); + } #ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS StringType str() &&; @@ -244,6 +339,23 @@ class basic_stringstream // Return the internally buffered sequence of characters maintained by // this stream object. +#ifndef BSLS_PLATFORM_CMP_SUN + // To be enabled once {DRQS 168075157} is resolved + template + typename bsl::enable_if< + bsl::IsStdAllocator::value, + basic_string >::type + str(const SALLOC& allocator) const + // Return a copy of the internally buffered sequence of characters + // maintained by this stream object in a 'basic_string' that uses the + // specified 'allocator'. + // + // Note: implemented inline due to Sun CC compilation error. + { + return this->rdbuf()->str(allocator); + } +#endif + ViewType view() const BSLS_KEYWORD_NOEXCEPT; // Return a view of the internally buffered sequence of characters // maintained by this stream object. @@ -284,8 +396,8 @@ namespace bsl { template inline basic_stringstream:: -basic_stringstream(const allocator_type& basicAllocator) -: BaseType(ios_base::in | ios_base::out, basicAllocator) +basic_stringstream(const allocator_type& allocator) +: BaseType(ios_base::in | ios_base::out, allocator) , BaseStream(BaseType::rdbuf()) { } @@ -294,8 +406,8 @@ template inline basic_stringstream:: basic_stringstream(ios_base::openmode modeBitMask, - const allocator_type& basicAllocator) -: BaseType(modeBitMask, basicAllocator) + const allocator_type& allocator) +: BaseType(modeBitMask, allocator) , BaseStream(BaseType::rdbuf()) { } @@ -304,8 +416,8 @@ template inline basic_stringstream:: basic_stringstream(const StringType& initialString, - const allocator_type& basicAllocator) -: BaseType(initialString, ios_base::in | ios_base::out, basicAllocator) + const allocator_type& allocator) +: BaseType(initialString, ios_base::in | ios_base::out, allocator) , BaseStream(BaseType::rdbuf()) { } @@ -315,8 +427,50 @@ inline basic_stringstream:: basic_stringstream(const StringType& initialString, ios_base::openmode modeBitMask, - const allocator_type& basicAllocator) -: BaseType(initialString, modeBitMask, basicAllocator) + const allocator_type& allocator) +: BaseType(initialString, modeBitMask, allocator) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_stringstream:: +basic_stringstream(BloombergLP::bslmf::MovableRef initialString) +: BaseType(MoveUtil::move(initialString), ios_base::in | ios_base::out) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_stringstream:: +basic_stringstream(BloombergLP::bslmf::MovableRef initialString, + const allocator_type& allocator) +: BaseType(MoveUtil::move(initialString), + ios_base::in | ios_base::out, + allocator) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_stringstream:: +basic_stringstream(BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask) +: BaseType(MoveUtil::move(initialString), modeBitMask) +, BaseStream(BaseType::rdbuf()) +{ +} + +template +inline +basic_stringstream:: +basic_stringstream(BloombergLP::bslmf::MovableRef initialString, + ios_base::openmode modeBitMask, + const allocator_type& allocator) +: BaseType(MoveUtil::move(initialString), modeBitMask, allocator) , BaseStream(BaseType::rdbuf()) { } @@ -361,9 +515,7 @@ inline void basic_stringstream::str( BloombergLP::bslmf::MovableRef value) { - typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; - StringType& lvalue = value; - this->rdbuf()->str(MoveUtil::move(lvalue)); + this->rdbuf()->str(MoveUtil::move(value)); } #ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS diff --git a/groups/bsl/bslstl/bslstl_stringstream.t.cpp b/groups/bsl/bslstl/bslstl_stringstream.t.cpp index ae81073afa..54530575bc 100644 --- a/groups/bsl/bslstl/bslstl_stringstream.t.cpp +++ b/groups/bsl/bslstl/bslstl_stringstream.t.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -46,16 +47,22 @@ // [ 3] stringstream(const A&& a); // [ 5] stringstream(openmode mask, const A& a = A()); // [ 6] stringstream(const STRING& s, const A& a = A()); +// [ 6] stringstream(MovableRef s, const A& a = A()); // [ 7] stringstream(const STRING& s, openmode mask, const A& a = A()); +// [ 7] stringstream(MovableRef s, openmode mask, const A& a = A()); +// [ 7] stringstream(const OTHER_STRING& s, const A& a = A()); +// [ 7] stringstream(const OTHER_STRING& s, openmode mask, const A& a = A()); // // MANIPULATORS // [ 3] operator=(const A&& a); // [ 4] void str(const StringType& value); // [ 4] void str(MovableRef value); +// [ 4] void str(const StringType& value); // // ACCESSORS // [ 4] StringType str() const; // [ 4] StringType str() &&; +// [ 4] StringType str(const SALLOC&); // [ 4] ViewType view() const; // [ 2] StreamBufType *rdbuf() const; // ---------------------------------------------------------------------------- @@ -208,6 +215,25 @@ void loadString(StringT *value, int length) } } +template +bool stringCouldBeMovedFrom(const StringT& s, + const typename StringT::allocator_type& otherAlloc) + // Return 'true' if the specified string 's' could be moved to another + // string that uses the specified allocator 'otherAlloc', and 'false' + // otherwise. +{ + return s.size() >= LENGTH_OF_SUFFICIENTLY_LONG_STRING && + s.get_allocator() == otherAlloc; +} + +template +bool stringWasMovedFrom(const StringT& s) + // Return 'true' if the specified string 's' was possibly moved from, and + // 'false' otherwise. +{ + return 0 == s.size(); +} + template void testCase2() { @@ -741,8 +767,10 @@ void testCase4() // Testing: // void str(const StringType& value); // void str(MovableRef value); + // void str(const StringType& value); // StringType str() const; // StringType str() &&; + // StringType str(const SALLOC&); // ViewType view() const; // ------------------------------------------------------------------------ @@ -779,6 +807,56 @@ void testCase4() ASSERT(X.view() == T); } + for (int tj = 0; tj < NUM_STRLEN_DATA; ++tj) { + const int LENGTH_TJ = STRLEN_DATA[tj].d_length; + + typedef bsl::basic_string< + CharT, + typename StringT::traits_type, + bslma::StdTestAllocator > TestString; + + TestString oS; const TestString& OS = oS; + loadString(&oS, LENGTH_TJ); + + // same string as 'oS'; but easier to compare + StringT mT(&da); const StringT& T = mT; + loadString(&mT, LENGTH_TJ); + + mX.str(OS); + ASSERT(X.str() == T); + ASSERT(X.view() == T); + } + +#ifndef BSLS_PLATFORM_CMP_SUN + // The call 'str(OtherAllocator) is not supported on SunOS, because the + // std::allocator there does not support rebind. This limitation can + // be lifted once we fully support C++20, where rebind is removed, and + // always goes through 'allocator_traits'. See {DRQS 168075157} and + // https://github.com/bloomberg/bde/pull/268 + for (int tj = 0; tj < NUM_STRLEN_DATA; ++tj) { + typedef std::allocator OtherAllocator; + typedef bsl::basic_string OtherString; + + const int LENGTH_TJ = STRLEN_DATA[tj].d_length; + + OtherString oS; const OtherString& OS = oS; + loadString(&oS, LENGTH_TJ); + + // same string as 'oS'; but easier to compare + StringT mT(&da); const StringT& T = mT; + loadString(&mT, LENGTH_TJ); + + mX.str(OS); + ASSERT(X.str() == T); + ASSERT(X.view() == T); + + OtherString oS2 = X.str(OtherAllocator()); + ASSERT(oS2 == oS); + } +#endif + for (int tj = 0; tj < NUM_STRLEN_DATA; ++tj) { const int LENGTH_TJ = STRLEN_DATA[tj].d_length; @@ -1081,6 +1159,8 @@ void testCase6() // // Testing: // stringstream(const STRING& s, const A& a = A()); + // stringstream(MovableRef s, const A& a = A()); + // stringstream(const OTHER_STRING& s, const A& a = A()); // ------------------------------------------------------------------------ if (verbose) printf("\nSTRING CTOR" @@ -1187,6 +1267,189 @@ void testCase6() ASSERTV(CONFIG, oa.numBlocksInUse(), 0 == oa.numBlocksInUse()); ASSERTV(CONFIG, noa.numBlocksInUse(), 0 == noa.numBlocksInUse()); } + + // Once again, only with MovableRef + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf("\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + StringT mS(&da); const StringT& S = mS; + loadString(&mS, LENGTH); + StringT mO(&da); const StringT& O = mO; + loadString(&mO, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(MoveUtil::move(mS)); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, X.str() == O); + + if (stringCouldBeMovedFrom(S, objAllocatorPtr)) { + ASSERTV(CONFIG, O.size(), S.size(), stringWasMovedFrom(S)); + } + + mX << 'X'; + + if (O.empty()) { mO.resize(1); } + mO[0] = static_cast('X'); + + ASSERTV(CONFIG, X.str() == O); + + CharT c = 'Z'; + mX >> c; + + ASSERTV(CONFIG, 'X' == c); + ASSERTV(CONFIG, X.str() == O); + + // Reclaim dynamically allocated object under test. + fa.deleteObject(objPtr); + } + + // And with a different string allocator + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef bsl::basic_string< + CharT, + bsl::char_traits, + bslma::StdTestAllocator > SallocString; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf( + "\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + bslma::TestAllocator scratch("scratch", veryVeryVeryVerbose); + + SallocString mSallocStr(&scratch); + const SallocString& sallocStr = mSallocStr; + loadString(&mSallocStr, LENGTH); + + StringT mS(&scratch); const StringT& S = mS; + loadString(&mS, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(sallocStr); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT( + sallocStr, + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(sallocStr, &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + bslma::TestAllocator& oa = *objAllocatorPtr; + bslma::TestAllocator& noa = 'c' != CONFIG ? sa : da; + + // Verify allocations from the object/non-object allocators. + + if ('N' == MEM) { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 == oa.numBlocksTotal()); + ASSERTV(CONFIG, noa.numBlocksTotal(), + 0 == noa.numBlocksTotal()); + } + else { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 != oa.numBlocksTotal()); + } + + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, X.str() == S); + + mX << 'X'; + + if (S.empty()) { mS.resize(1); } + mS[0] = static_cast('X'); + + ASSERTV(CONFIG, X.str() == S); + + CharT c = 'Z'; + mX >> c; + + ASSERTV(CONFIG, 'X' == c); + ASSERTV(CONFIG, X.str() == S); + + // Verify no temporary memory is allocated from the object + // allocator when supplied. + + if ('c' == CONFIG) { + ASSERTV(CONFIG, oa.numBlocksTotal(), oa.numBlocksInUse(), + oa.numBlocksTotal() == oa.numBlocksInUse()); + } + + // Reclaim dynamically allocated object under test. + + fa.deleteObject(objPtr); + + // Verify all memory is released on object destruction. + + ASSERTV(CONFIG, fa.numBlocksInUse(), + 0 == fa.numBlocksInUse()); + ASSERTV(CONFIG, oa.numBlocksInUse(), + 0 == oa.numBlocksInUse()); + ASSERTV(CONFIG, noa.numBlocksInUse(), + 0 == noa.numBlocksInUse()); + } } } @@ -1251,6 +1514,8 @@ void testCase7() // // Testing: // stringstream(const STRING& s, openmode mask, const A& a = A()); + // stringstream(MovableRef s, openmode mask, const A& a = A()); + // stringstream(const OTHER_STRING& s, openmode mask, const A& a = A()); // ------------------------------------------------------------------------ if (verbose) printf("\nSTRING & OPENMODE CTOR" @@ -1386,6 +1651,232 @@ void testCase7() ASSERTV(CONFIG, noa.numBlocksInUse(), 0 == noa.numBlocksInUse()); } + + // Once again, only with MovableRef + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef BloombergLP::bslmf::MovableRefUtil MoveUtil; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf( + "\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + StringT mS(&da); const StringT& S = mS; + loadString(&mS, LENGTH); + StringT mO(&da); const StringT& O = mO; + loadString(&mO, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), MODE); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT( + MoveUtil::move(mS), + MODE, + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(MoveUtil::move(mS), MODE, &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, ti, tj, !IN_OUT || X.str() == O); + + if (stringCouldBeMovedFrom(S, objAllocatorPtr)) { + ASSERTV(CONFIG, O.size(), S.size(), stringWasMovedFrom(S)); + } + + if ((MODE & IosBase::out) || !(MODE & IosBase::in)) { + mX << 'X'; // '>>' test is perturbed if 'out' not set + } + + if (MODE & IosBase::out) { + if (MODE & IosBase::ate) { + mO.push_back(static_cast('X')); + } + else { + if (O.empty()) { mO.resize(1); } + mO[0] = static_cast('X'); + } + } + + ASSERTV(CONFIG, !IN_OUT || X.str() == O); + + CharT c = 'Z'; + mX >> c; + + CharT EXPECTED; + if (MODE & IosBase::in) { + EXPECTED = (0 == LENGTH) && !(MODE & IosBase::out) + ? 'Z' + : O[0]; + } + else { + EXPECTED = 'Z'; + } + + ASSERTV(CONFIG, EXPECTED == c); + ASSERTV(CONFIG, !IN_OUT || X.str() == O); + + // Reclaim dynamically allocated object under test. + + fa.deleteObject(objPtr); + } + + // And with a different string allocator + for (char cfg = 'a'; cfg <= 'c'; ++cfg) { + typedef bsl::basic_string< + CharT, + bsl::char_traits, + bslma::StdTestAllocator > SallocString; + + const char CONFIG = cfg; // how we specify the allocator + + bslma::TestAllocator da("default", veryVeryVeryVerbose); + bslma::TestAllocator fa("footprint", veryVeryVeryVerbose); + bslma::TestAllocator sa("supplied", veryVeryVeryVerbose); + + bslma::DefaultAllocatorGuard dag(&da); + + if (veryVerbose) { + printf( + "\nTesting with various allocator configurations.\n"); + } + + StreamT *objPtr; + bslma::TestAllocator *objAllocatorPtr; + + bslma::TestAllocator scratch("scratch", veryVeryVeryVerbose); + + SallocString mSallocStr(&scratch); + const SallocString& sallocStr = mSallocStr; + loadString(&mSallocStr, LENGTH); + + StringT mS(&scratch); const StringT& S = mS; + loadString(&mS, LENGTH); + + switch (CONFIG) { + case 'a': { + objPtr = new (fa) StreamT(sallocStr, MODE); + objAllocatorPtr = &da; + } break; + case 'b': { + objPtr = new (fa) StreamT( + sallocStr, + MODE, + typename StreamT::allocator_type(0)); + objAllocatorPtr = &da; + } break; + case 'c': { + objPtr = new (fa) StreamT(sallocStr, MODE, &sa); + objAllocatorPtr = &sa; + } break; + default: { + ASSERTV(CONFIG, !"Bad allocator config."); + return; // RETURN + } break; + } + + StreamT& mX = *objPtr; const StreamT& X = mX; + const BaseT& B = X; + + bslma::TestAllocator& oa = *objAllocatorPtr; + bslma::TestAllocator& noa = 'c' != CONFIG ? sa : da; + + // Verify allocations from the object/non-object allocators. + + if ('N' == MEM) { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 == oa.numBlocksTotal()); + ASSERTV(CONFIG, noa.numBlocksTotal(), + 0 == noa.numBlocksTotal()); + } + else { + ASSERTV(CONFIG, oa.numBlocksTotal(), + 0 != oa.numBlocksTotal()); + } + + ASSERTV(CONFIG, X.rdbuf()); + ASSERTV(CONFIG, X.rdbuf() == B.rdbuf()); + ASSERTV(CONFIG, ti, tj, !IN_OUT || X.str() == S); + + if ((MODE & IosBase::out) || !(MODE & IosBase::in)) { + mX << 'X'; // '>>' test is perturbed if 'out' not set + } + + if (MODE & IosBase::out) { + if (MODE & IosBase::ate) { + mS.push_back(static_cast('X')); + } + else { + if (S.empty()) { mS.resize(1); } + mS[0] = static_cast('X'); + } + } + + ASSERTV(CONFIG, !IN_OUT || X.str() == S); + + CharT c = 'Z'; + mX >> c; + + CharT EXPECTED; + if (MODE & IosBase::in) { + EXPECTED = (0 == LENGTH) && !(MODE & IosBase::out) + ? 'Z' + : S[0]; + } + else { + EXPECTED = 'Z'; + } + + ASSERTV(CONFIG, EXPECTED == c); + ASSERTV(CONFIG, !IN_OUT || X.str() == S); + + // Verify no temporary memory is allocated from the object + // allocator when supplied. + + if ('c' == CONFIG && !(MODE & IosBase::ate)) { + ASSERTV(CONFIG, oa.numBlocksTotal(), oa.numBlocksInUse(), + oa.numBlocksTotal() == oa.numBlocksInUse()); + } + + // Reclaim dynamically allocated object under test. + + fa.deleteObject(objPtr); + + // Verify all memory is released on object destruction. + + ASSERTV(CONFIG, fa.numBlocksInUse(), + 0 == fa.numBlocksInUse()); + ASSERTV(CONFIG, oa.numBlocksInUse(), + 0 == oa.numBlocksInUse()); + ASSERTV(CONFIG, noa.numBlocksInUse(), + 0 == noa.numBlocksInUse()); + } } } }