ICU-10086 move & swap for UnicodeString, LocalPointer, LocalArray

X-SVN-Rev: 37469
This commit is contained in:
Markus Scherer 2015-05-28 17:03:45 +00:00
parent 475ba73f13
commit f7e715daee
10 changed files with 733 additions and 53 deletions

View file

@ -158,12 +158,55 @@ public:
* @param p simple pointer to an array of T items that is adopted
*/
explicit LocalMemory(T *p=NULL) : LocalPointerBase<T>(p) {}
#if U_HAVE_RVALUE_REFERENCES
/**
* Move constructor, leaves src with isNull().
* @param src source smart pointer
*/
LocalMemory(LocalMemory<T> &&src) U_NOEXCEPT : LocalPointerBase<T>(src.ptr) {
src.ptr=NULL;
}
#endif
/**
* Destructor deletes the memory it owns.
*/
~LocalMemory() {
uprv_free(LocalPointerBase<T>::ptr);
}
#if U_HAVE_RVALUE_REFERENCES
/**
* Move assignment operator, leaves src with isNull().
* The behavior is undefined if *this and src are the same object.
* @param src source smart pointer
* @return *this
*/
LocalMemory<T> &operator=(LocalMemory<T> &&src) U_NOEXCEPT {
return moveFrom(src);
}
#endif
/**
* Move assignment, leaves src with isNull().
* The behavior is undefined if *this and src are the same object.
*
* Can be called explicitly, does not need C++11 support.
* @param src source smart pointer
* @return *this
*/
LocalMemory<T> &moveFrom(LocalMemory<T> &src) U_NOEXCEPT {
delete[] LocalPointerBase<T>::ptr;
LocalPointerBase<T>::ptr=src.ptr;
src.ptr=NULL;
return *this;
}
/**
* Swap pointers.
* @param other other smart pointer
*/
void swap(LocalMemory<T> &other) U_NOEXCEPT {
T *temp=LocalPointerBase<T>::ptr;
LocalPointerBase<T>::ptr=other.ptr;
other.ptr=temp;
}
/**
* Deletes the array it owns,
* and adopts (takes ownership of) the one passed in.
@ -203,6 +246,16 @@ public:
T &operator[](ptrdiff_t i) const { return LocalPointerBase<T>::ptr[i]; }
};
/**
* Non-member LocalMemory swap function.
* @param p1 will get p2's pointer
* @param p2 will get p1's pointer
*/
template<typename T>
inline void swap(LocalMemory<T> &p1, LocalMemory<T> &p2) U_NOEXCEPT {
p1.swap(p2);
}
template<typename T>
inline T *LocalMemory<T>::allocateInsteadAndReset(int32_t newCapacity) {
if(newCapacity>0) {

View file

@ -1,7 +1,7 @@
/*
*******************************************************************************
*
* Copyright (C) 2009-2014, International Business Machines
* Copyright (C) 2009-2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
@ -151,11 +151,11 @@ protected:
T *ptr;
private:
// No comparison operators with other LocalPointerBases.
bool operator==(const LocalPointerBase &other);
bool operator!=(const LocalPointerBase &other);
// No ownership transfer: No copy constructor, no assignment operator.
LocalPointerBase(const LocalPointerBase &other);
void operator=(const LocalPointerBase &other);
bool operator==(const LocalPointerBase<T> &other);
bool operator!=(const LocalPointerBase<T> &other);
// No ownership sharing: No copy constructor, no assignment operator.
LocalPointerBase(const LocalPointerBase<T> &other);
void operator=(const LocalPointerBase<T> &other);
// No heap allocation. Use only on the stack.
static void * U_EXPORT2 operator new(size_t size);
static void * U_EXPORT2 operator new[](size_t size);
@ -210,6 +210,16 @@ public:
errorCode=U_MEMORY_ALLOCATION_ERROR;
}
}
#if U_HAVE_RVALUE_REFERENCES
/**
* Move constructor, leaves src with isNull().
* @param src source smart pointer
* @draft ICU 56
*/
LocalPointer(LocalPointer<T> &&src) U_NOEXCEPT : LocalPointerBase<T>(src.ptr) {
src.ptr=NULL;
}
#endif
#endif /* U_HIDE_DRAFT_API */
/**
* Destructor deletes the object it owns.
@ -218,6 +228,45 @@ public:
~LocalPointer() {
delete LocalPointerBase<T>::ptr;
}
#ifndef U_HIDE_DRAFT_API
#if U_HAVE_RVALUE_REFERENCES
/**
* Move assignment operator, leaves src with isNull().
* The behavior is undefined if *this and src are the same object.
* @param src source smart pointer
* @return *this
* @draft ICU 56
*/
LocalPointer<T> &operator=(LocalPointer<T> &&src) U_NOEXCEPT {
return moveFrom(src);
}
#endif
/**
* Move assignment, leaves src with isNull().
* The behavior is undefined if *this and src are the same object.
*
* Can be called explicitly, does not need C++11 support.
* @param src source smart pointer
* @return *this
* @draft ICU 56
*/
LocalPointer<T> &moveFrom(LocalPointer<T> &src) U_NOEXCEPT {
delete LocalPointerBase<T>::ptr;
LocalPointerBase<T>::ptr=src.ptr;
src.ptr=NULL;
return *this;
}
/**
* Swap pointers.
* @param other other smart pointer
* @draft ICU 56
*/
void swap(LocalPointer<T> &other) U_NOEXCEPT {
T *temp=LocalPointerBase<T>::ptr;
LocalPointerBase<T>::ptr=other.ptr;
other.ptr=temp;
}
#endif /* U_HIDE_DRAFT_API */
/**
* Deletes the object it owns,
* and adopts (takes ownership of) the one passed in.
@ -258,6 +307,19 @@ public:
#endif /* U_HIDE_DRAFT_API */
};
#ifndef U_HIDE_DRAFT_API
/**
* Non-member LocalPointer swap function.
* @param p1 will get p2's pointer
* @param p2 will get p1's pointer
* @draft ICU 56
*/
template<typename T>
inline void swap(LocalPointer<T> &p1, LocalPointer<T> &p2) U_NOEXCEPT {
p1.swap(p2);
}
#endif /* U_HIDE_DRAFT_API */
/**
* "Smart pointer" class, deletes objects via the C++ array delete[] operator.
* For most methods see the LocalPointerBase base class.
@ -285,6 +347,36 @@ public:
* @stable ICU 4.4
*/
explicit LocalArray(T *p=NULL) : LocalPointerBase<T>(p) {}
#ifndef U_HIDE_DRAFT_API
/**
* Constructor takes ownership and reports an error if NULL.
*
* This constructor is intended to be used with other-class constructors
* that may report a failure UErrorCode,
* so that callers need to check only for U_FAILURE(errorCode)
* and not also separately for isNull().
*
* @param p simple pointer to an array of T objects that is adopted
* @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR
* if p==NULL and no other failure code had been set
* @draft ICU 56
*/
LocalArray(T *p, UErrorCode &errorCode) : LocalPointerBase<T>(p) {
if(p==NULL && U_SUCCESS(errorCode)) {
errorCode=U_MEMORY_ALLOCATION_ERROR;
}
}
#if U_HAVE_RVALUE_REFERENCES
/**
* Move constructor, leaves src with isNull().
* @param src source smart pointer
* @draft ICU 56
*/
LocalArray(LocalArray<T> &&src) U_NOEXCEPT : LocalPointerBase<T>(src.ptr) {
src.ptr=NULL;
}
#endif
#endif /* U_HIDE_DRAFT_API */
/**
* Destructor deletes the array it owns.
* @stable ICU 4.4
@ -292,6 +384,45 @@ public:
~LocalArray() {
delete[] LocalPointerBase<T>::ptr;
}
#ifndef U_HIDE_DRAFT_API
#if U_HAVE_RVALUE_REFERENCES
/**
* Move assignment operator, leaves src with isNull().
* The behavior is undefined if *this and src are the same object.
* @param src source smart pointer
* @return *this
* @draft ICU 56
*/
LocalArray<T> &operator=(LocalArray<T> &&src) U_NOEXCEPT {
return moveFrom(src);
}
#endif
/**
* Move assignment, leaves src with isNull().
* The behavior is undefined if *this and src are the same object.
*
* Can be called explicitly, does not need C++11 support.
* @param src source smart pointer
* @return *this
* @draft ICU 56
*/
LocalArray<T> &moveFrom(LocalArray<T> &src) U_NOEXCEPT {
delete[] LocalPointerBase<T>::ptr;
LocalPointerBase<T>::ptr=src.ptr;
src.ptr=NULL;
return *this;
}
/**
* Swap pointers.
* @param other other smart pointer
* @draft ICU 56
*/
void swap(LocalArray<T> &other) U_NOEXCEPT {
T *temp=LocalPointerBase<T>::ptr;
LocalPointerBase<T>::ptr=other.ptr;
other.ptr=temp;
}
#endif /* U_HIDE_DRAFT_API */
/**
* Deletes the array it owns,
* and adopts (takes ownership of) the one passed in.
@ -302,6 +433,34 @@ public:
delete[] LocalPointerBase<T>::ptr;
LocalPointerBase<T>::ptr=p;
}
#ifndef U_HIDE_DRAFT_API
/**
* Deletes the array it owns,
* and adopts (takes ownership of) the one passed in.
*
* If U_FAILURE(errorCode), then the current array is retained and the new one deleted.
*
* If U_SUCCESS(errorCode) but the input pointer is NULL,
* then U_MEMORY_ALLOCATION_ERROR is set,
* the current array is deleted, and NULL is set.
*
* @param p simple pointer to an array of T objects that is adopted
* @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR
* if p==NULL and no other failure code had been set
* @draft ICU 56
*/
void adoptInsteadAndCheckErrorCode(T *p, UErrorCode &errorCode) {
if(U_SUCCESS(errorCode)) {
delete[] LocalPointerBase<T>::ptr;
LocalPointerBase<T>::ptr=p;
if(p==NULL) {
errorCode=U_MEMORY_ALLOCATION_ERROR;
}
} else {
delete[] p;
}
}
#endif /* U_HIDE_DRAFT_API */
/**
* Array item access (writable).
* No index bounds check.
@ -312,6 +471,19 @@ public:
T &operator[](ptrdiff_t i) const { return LocalPointerBase<T>::ptr[i]; }
};
#ifndef U_HIDE_DRAFT_API
/**
* Non-member LocalArray swap function.
* @param p1 will get p2's pointer
* @param p2 will get p1's pointer
* @draft ICU 56
*/
template<typename T>
inline void swap(LocalArray<T> &p1, LocalArray<T> &p2) U_NOEXCEPT {
p1.swap(p2);
}
#endif /* U_HIDE_DRAFT_API */
/**
* \def U_DEFINE_LOCAL_OPEN_POINTER
* "Smart pointer" definition macro, deletes objects via the closeFunction.
@ -335,16 +507,64 @@ public:
* @see LocalPointer
* @stable ICU 4.4
*/
#if U_HAVE_RVALUE_REFERENCES
#define U_DEFINE_LOCAL_OPEN_POINTER(LocalPointerClassName, Type, closeFunction) \
class LocalPointerClassName : public LocalPointerBase<Type> { \
public: \
explicit LocalPointerClassName(Type *p=NULL) : LocalPointerBase<Type>(p) {} \
LocalPointerClassName(LocalPointerClassName &&src) U_NOEXCEPT \
: LocalPointerBase<Type>(src.ptr) { \
src.ptr=NULL; \
} \
~LocalPointerClassName() { closeFunction(ptr); } \
LocalPointerClassName &operator=(LocalPointerClassName &&src) U_NOEXCEPT { \
return moveFrom(src); \
} \
LocalPointerClassName &moveFrom(LocalPointerClassName &src) U_NOEXCEPT { \
closeFunction(ptr); \
LocalPointerBase<Type>::ptr=src.ptr; \
src.ptr=NULL; \
return *this; \
} \
void swap(LocalPointerClassName &other) U_NOEXCEPT { \
Type *temp=LocalPointerBase<Type>::ptr; \
LocalPointerBase<Type>::ptr=other.ptr; \
other.ptr=temp; \
} \
void adoptInstead(Type *p) { \
closeFunction(ptr); \
ptr=p; \
} \
}; \
inline void swap(LocalPointerClassName &p1, LocalPointerClassName &p2) U_NOEXCEPT { \
p1.swap(p2); \
}
#else
#define U_DEFINE_LOCAL_OPEN_POINTER(LocalPointerClassName, Type, closeFunction) \
class LocalPointerClassName : public LocalPointerBase<Type> { \
public: \
explicit LocalPointerClassName(Type *p=NULL) : LocalPointerBase<Type>(p) {} \
~LocalPointerClassName() { closeFunction(ptr); } \
LocalPointerClassName &moveFrom(LocalPointerClassName &src) U_NOEXCEPT { \
closeFunction(ptr); \
LocalPointerBase<Type>::ptr=src.ptr; \
src.ptr=NULL; \
return *this; \
} \
void swap(LocalPointerClassName &other) U_NOEXCEPT { \
Type *temp=LocalPointerBase<Type>::ptr; \
LocalPointerBase<Type>::ptr=other.ptr; \
other.ptr=temp; \
} \
void adoptInstead(Type *p) { \
closeFunction(ptr); \
ptr=p; \
} \
}; \
inline void swap(LocalPointerClassName &p1, LocalPointerClassName &p2) U_NOEXCEPT { \
p1.swap(p2); \
}
#endif
U_NAMESPACE_END

View file

@ -429,6 +429,12 @@
#ifndef __has_attribute
# define __has_attribute(x) 0
#endif
#ifndef __has_feature
# define __has_feature(x) 0
#endif
#ifndef __has_extension
# define __has_extension(x) 0
#endif
/**
* \def U_MALLOC_ATTR
@ -454,6 +460,62 @@
# define U_ALLOC_SIZE_ATTR2(X,Y)
#endif
/**
* \def U_CPLUSPLUS_VERSION
* 0 if no C++; 1, 11, 14, ... if C++.
* Support for specific features cannot always be determined by the C++ version alone.
* @internal
*/
#ifdef U_CPLUSPLUS_VERSION
# if U_CPLUSPLUS_VERSION != 0 && !defined(__cplusplus)
# undef U_CPLUSPLUS_VERSION
# define U_CPLUSPLUS_VERSION 0
# endif
/* Otherwise use the predefined value. */
#elif !defined(__cplusplus)
# define U_CPLUSPLUS_VERSION 0
#elif __cplusplus >= 201402L
# define U_CPLUSPLUS_VERSION 14
#elif __cplusplus >= 201103L
# define U_CPLUSPLUS_VERSION 11
#else
// C++98 or C++03
# define U_CPLUSPLUS_VERSION 1
#endif
/**
* \def U_HAVE_RVALUE_REFERENCES
* Set to 1 if the compiler supports rvalue references.
* C++11 feature, necessary for move constructor & move assignment.
* @internal
*/
#ifdef U_HAVE_RVALUE_REFERENCES
/* Use the predefined value. */
#elif U_CPLUSPLUS_VERSION >= 11 || __has_feature(cxx_rvalue_references) || __has_extension(cxx_rvalue_references) \
|| defined(__GXX_EXPERIMENTAL_CXX0X__) \
|| (defined(_MSC_VER) && _MSC_VER >= 1600) /* Visual Studio 2010 */
# define U_HAVE_RVALUE_REFERENCES 1
#else
# define U_HAVE_RVALUE_REFERENCES 0
#endif
/**
* \def U_NOEXCEPT
* "noexcept" if supported, otherwise empty.
* Some code, especially STL containers, uses move semantics of objects only
* if the move constructor and the move operator are declared as not throwing exceptions.
* @internal
*/
#ifdef U_NOEXCEPT
/* Use the predefined value. */
#elif U_CPLUSPLUS_VERSION >= 11 || __has_feature(cxx_noexcept) || __has_extension(cxx_noexcept) \
|| U_GCC_MAJOR_MINOR >= 406 \
|| (defined(_MSC_VER) && _MSC_VER >= 1900) /* Visual Studio 2015 */
# define U_NOEXCEPT noexcept
#else
# define U_NOEXCEPT
#endif
/** @} */
/*===========================================================================*/
@ -670,7 +732,7 @@
* does not support u"abc" string literals.
* C++11 and C11 require support for UTF-16 literals
*/
# if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
# if U_CPLUSPLUS_VERSION >= 11 || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
# define U_HAVE_CHAR16_T 1
# else
# define U_HAVE_CHAR16_T 0

View file

@ -1,7 +1,7 @@
/*
******************************************************************************
*
* Copyright (C) 1999-2014, International Business Machines
* Copyright (C) 1999-2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
******************************************************************************
@ -132,7 +132,7 @@
* @internal
*/
#if defined(__cplusplus) && __cplusplus>=201103L
#if U_CPLUSPLUS_VERSION >= 11
/* C++11 */
#ifndef U_OVERRIDE
#define U_OVERRIDE override

View file

@ -1877,6 +1877,40 @@ public:
*/
UnicodeString &fastCopyFrom(const UnicodeString &src);
#ifndef U_HIDE_DRAFT_API
#if U_HAVE_RVALUE_REFERENCES
/**
* Move assignment operator, might leave src in bogus state.
* This string will have the same contents and state that the source string had.
* The behavior is undefined if *this and src are the same object.
* @param src source string
* @return *this
* @draft ICU 56
*/
UnicodeString &operator=(UnicodeString &&src) U_NOEXCEPT {
return moveFrom(src);
}
#endif
/**
* Move assignment, might leave src in bogus state.
* This string will have the same contents and state that the source string had.
* The behavior is undefined if *this and src are the same object.
*
* Can be called explicitly, does not need C++11 support.
* @param src source string
* @return *this
* @draft ICU 56
*/
UnicodeString &moveFrom(UnicodeString &src) U_NOEXCEPT;
/**
* Swap strings.
* @param other other string
* @draft ICU 56
*/
void swap(UnicodeString &other) U_NOEXCEPT;
#endif /* U_HIDE_DRAFT_API */
/**
* Assignment operator. Replace the characters in this UnicodeString
* with the code unit <TT>ch</TT>.
@ -3131,6 +3165,18 @@ public:
*/
UnicodeString(const UnicodeString& that);
#ifndef U_HIDE_DRAFT_API
#if U_HAVE_RVALUE_REFERENCES
/**
* Move constructor, might leave src in bogus state.
* This string will have the same contents and state that the source string had.
* @param src source string
* @draft ICU 56
*/
UnicodeString(UnicodeString &&src) U_NOEXCEPT;
#endif
#endif /* U_HIDE_DRAFT_API */
/**
* 'Substring' constructor from tail of source string.
* @param src The UnicodeString object to copy.
@ -3440,6 +3486,9 @@ private:
// implements assigment operator, copy constructor, and fastCopyFrom()
UnicodeString &copyFrom(const UnicodeString &src, UBool fastCopy=FALSE);
// Copies just the fields without memory management.
void copyFieldsFrom(UnicodeString &src, UBool setSrcToBogus) U_NOEXCEPT;
// Pin start and limit to acceptable values.
inline void pinIndex(int32_t& start) const;
inline void pinIndices(int32_t& start,
@ -3619,6 +3668,19 @@ private:
U_COMMON_API UnicodeString U_EXPORT2
operator+ (const UnicodeString &s1, const UnicodeString &s2);
#ifndef U_HIDE_DRAFT_API
/**
* Non-member UnicodeString swap function.
* @param s1 will get s2's contents and state
* @param s2 will get s1's contents and state
* @draft ICU 56
*/
U_COMMON_API inline void U_EXPORT2
swap(UnicodeString &s1, UnicodeString &s2) U_NOEXCEPT {
s1.swap(s2);
}
#endif /* U_HIDE_DRAFT_API */
//========================================
// Inline members
//========================================

View file

@ -306,6 +306,13 @@ UnicodeString::UnicodeString(const UnicodeString& that) {
copyFrom(that);
}
#if U_HAVE_RVALUE_REFERENCES
UnicodeString::UnicodeString(UnicodeString &&src) U_NOEXCEPT {
fUnion.fFields.fLengthAndFlags = kShortString;
moveFrom(src);
}
#endif
UnicodeString::UnicodeString(const UnicodeString& that,
int32_t srcStart) {
fUnion.fFields.fLengthAndFlags = kShortString;
@ -536,6 +543,47 @@ UnicodeString::copyFrom(const UnicodeString &src, UBool fastCopy) {
return *this;
}
UnicodeString &UnicodeString::moveFrom(UnicodeString &src) U_NOEXCEPT {
// No explicit check for self move assignment, consistent with standard library.
// Self move assignment causes no crash nor leak but might make the object bogus.
releaseArray();
copyFieldsFrom(src, TRUE);
return *this;
}
// Same as moveFrom() except without memory management.
void UnicodeString::copyFieldsFrom(UnicodeString &src, UBool setSrcToBogus) U_NOEXCEPT {
int16_t lengthAndFlags = fUnion.fFields.fLengthAndFlags = src.fUnion.fFields.fLengthAndFlags;
if(lengthAndFlags & kUsingStackBuffer) {
// Short string using the stack buffer, copy the contents.
uprv_memcpy(fUnion.fStackFields.fBuffer, src.fUnion.fStackFields.fBuffer,
getShortLength() * U_SIZEOF_UCHAR);
} else {
// In all other cases, copy all fields.
fUnion.fFields.fArray = src.fUnion.fFields.fArray;
fUnion.fFields.fCapacity = src.fUnion.fFields.fCapacity;
if(!hasShortLength()) {
fUnion.fFields.fLength = src.fUnion.fFields.fLength;
}
if(setSrcToBogus) {
// Set src to bogus without releasing any memory.
src.fUnion.fFields.fLengthAndFlags = kIsBogus;
src.fUnion.fFields.fArray = NULL;
src.fUnion.fFields.fCapacity = 0;
}
}
}
void UnicodeString::swap(UnicodeString &other) U_NOEXCEPT {
UnicodeString temp; // Empty short string: Known not to need releaseArray().
// Copy fields without resetting source values in between.
temp.copyFieldsFrom(*this, FALSE);
this->copyFieldsFrom(other, FALSE);
other.copyFieldsFrom(temp, FALSE);
// Set temp to an empty string so that other's memory is not released twice.
temp.fUnion.fFields.fLengthAndFlags = kShortString;
}
//========================================
// Miscellaneous operations
//========================================

View file

@ -77,8 +77,8 @@ void BreakTransliterator::handleTransliterate(Replaceable& text, UTransPosition&
{
Mutex m;
BreakTransliterator *nonConstThis = const_cast<BreakTransliterator *>(this);
boundaries.adoptInstead(nonConstThis->cachedBoundaries.orphan());
bi.adoptInstead(nonConstThis->cachedBI.orphan());
boundaries.moveFrom(nonConstThis->cachedBoundaries);
bi.moveFrom(nonConstThis->cachedBI);
}
if (bi.isNull()) {
bi.adoptInstead(BreakIterator::createWordInstance(Locale::getEnglish(), status));
@ -143,10 +143,10 @@ void BreakTransliterator::handleTransliterate(Replaceable& text, UTransPosition&
Mutex m;
BreakTransliterator *nonConstThis = const_cast<BreakTransliterator *>(this);
if (nonConstThis->cachedBI.isNull()) {
nonConstThis->cachedBI.adoptInstead(bi.orphan());
nonConstThis->cachedBI.moveFrom(bi);
}
if (nonConstThis->cachedBoundaries.isNull()) {
nonConstThis->cachedBoundaries.adoptInstead(boundaries.orphan());
nonConstThis->cachedBoundaries.moveFrom(boundaries);
}
}

View file

@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2014, International Business Machines Corporation and
* Copyright (c) 1997-2015, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
@ -250,8 +250,11 @@ public:
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=NULL);
void TestLocalPointer();
void TestLocalPointerMoveSwap();
void TestLocalArray();
void TestLocalArrayMoveSwap();
void TestLocalXyzPointer();
void TestLocalXyzPointerMoveSwap();
void TestLocalXyzPointerNull();
};
@ -265,13 +268,16 @@ void LocalPointerTest::runIndexedTest(int32_t index, UBool exec, const char *&na
}
TESTCASE_AUTO_BEGIN;
TESTCASE_AUTO(TestLocalPointer);
TESTCASE_AUTO(TestLocalPointerMoveSwap);
TESTCASE_AUTO(TestLocalArray);
TESTCASE_AUTO(TestLocalArrayMoveSwap);
TESTCASE_AUTO(TestLocalXyzPointer);
TESTCASE_AUTO(TestLocalXyzPointerMoveSwap);
TESTCASE_AUTO(TestLocalXyzPointerNull);
TESTCASE_AUTO_END;
}
// Exercise every LocalPointer and LocalPointerBase method.
// Exercise almost every LocalPointer and LocalPointerBase method.
void LocalPointerTest::TestLocalPointer() {
// constructor
LocalPointer<UnicodeString> s(new UnicodeString((UChar32)0x50005));
@ -294,7 +300,6 @@ void LocalPointerTest::TestLocalPointer() {
errln("LocalPointer orphan() failure");
}
delete orphan;
// destructor
s.adoptInstead(new UnicodeString());
if(s->length()!=0) {
errln("LocalPointer adoptInstead(empty) failure");
@ -336,9 +341,50 @@ void LocalPointerTest::TestLocalPointer() {
errln("LocalPointer(NULL, errorCode) did not set U_MEMORY_ALLOCATION_ERROR");
return;
}
// destructor
}
// Exercise every LocalArray method (but not LocalPointerBase).
void LocalPointerTest::TestLocalPointerMoveSwap() {
UnicodeString *p1 = new UnicodeString((UChar)0x61);
UnicodeString *p2 = new UnicodeString((UChar)0x62);
LocalPointer<UnicodeString> s1(p1);
LocalPointer<UnicodeString> s2(p2);
s1.swap(s2);
if(s1.getAlias() != p2 || s2.getAlias() != p1) {
errln("LocalPointer.swap() did not swap");
}
swap(s1, s2);
if(s1.getAlias() != p1 || s2.getAlias() != p2) {
errln("swap(LocalPointer) did not swap back");
}
LocalPointer<UnicodeString> s3;
s3.moveFrom(s1);
if(s3.getAlias() != p1 || s1.isValid()) {
errln("LocalPointer.moveFrom() did not move");
}
#if U_HAVE_RVALUE_REFERENCES
infoln("TestLocalPointerMoveSwap() with rvalue references");
s1 = static_cast<LocalPointer<UnicodeString> &&>(s3);
if(s1.getAlias() != p1 || s3.isValid()) {
errln("LocalPointer move assignment operator did not move");
}
LocalPointer<UnicodeString> s4(static_cast<LocalPointer<UnicodeString> &&>(s2));
if(s4.getAlias() != p2 || s2.isValid()) {
errln("LocalPointer move constructor did not move");
}
#else
infoln("TestLocalPointerMoveSwap() without rvalue references");
#endif
// Move self assignment leaves the object valid but in an undefined state.
// Do it to make sure there is no crash,
// but do not check for any particular resulting value.
s1.moveFrom(s1);
s3.moveFrom(s3);
}
// Exercise almost every LocalArray method (but not LocalPointerBase).
void LocalPointerTest::TestLocalArray() {
// constructor
LocalArray<UnicodeString> a(new UnicodeString[2]);
@ -354,9 +400,87 @@ void LocalPointerTest::TestLocalArray() {
if(a[3].length()!=2 || a[3][1]!=0x62) {
errln("LocalArray adoptInstead() failure");
}
// LocalArray(p, errorCode) sets U_MEMORY_ALLOCATION_ERROR if p==NULL.
UErrorCode errorCode = U_ZERO_ERROR;
LocalArray<UnicodeString> ua(new UnicodeString[3], errorCode);
if(ua.isNull() && U_SUCCESS(errorCode)) {
errln("LocalArray(p, errorCode) failure");
return;
}
errorCode = U_ZERO_ERROR;
UnicodeString *u4 = new UnicodeString[4];
ua.adoptInsteadAndCheckErrorCode(u4, errorCode);
if(ua.isNull() && U_SUCCESS(errorCode)) {
errln("adoptInsteadAndCheckErrorCode(p, errorCode) failure");
return;
}
// Incoming failure: Keep the current object and delete the input object.
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
ua.adoptInsteadAndCheckErrorCode(new UnicodeString[5], errorCode);
if(ua.isValid() && ua.getAlias() != u4) {
errln("adoptInsteadAndCheckErrorCode(p, U_FAILURE) did not retain the old array");
return;
}
errorCode = U_ZERO_ERROR;
ua.adoptInsteadAndCheckErrorCode(NULL, errorCode);
if(errorCode != U_MEMORY_ALLOCATION_ERROR) {
errln("adoptInsteadAndCheckErrorCode(NULL, errorCode) did not set U_MEMORY_ALLOCATION_ERROR");
return;
}
if(ua.isValid()) {
errln("adoptInsteadAndCheckErrorCode(NULL, errorCode) kept the array");
return;
}
errorCode = U_ZERO_ERROR;
LocalArray<UnicodeString> null(NULL, errorCode);
if(errorCode != U_MEMORY_ALLOCATION_ERROR) {
errln("LocalArray(NULL, errorCode) did not set U_MEMORY_ALLOCATION_ERROR");
return;
}
// destructor
}
void LocalPointerTest::TestLocalArrayMoveSwap() {
UnicodeString *p1 = new UnicodeString[2];
UnicodeString *p2 = new UnicodeString[3];
LocalArray<UnicodeString> a1(p1);
LocalArray<UnicodeString> a2(p2);
a1.swap(a2);
if(a1.getAlias() != p2 || a2.getAlias() != p1) {
errln("LocalArray.swap() did not swap");
}
swap(a1, a2);
if(a1.getAlias() != p1 || a2.getAlias() != p2) {
errln("swap(LocalArray) did not swap back");
}
LocalArray<UnicodeString> a3;
a3.moveFrom(a1);
if(a3.getAlias() != p1 || a1.isValid()) {
errln("LocalArray.moveFrom() did not move");
}
#if U_HAVE_RVALUE_REFERENCES
infoln("TestLocalArrayMoveSwap() with rvalue references");
a1 = static_cast<LocalArray<UnicodeString> &&>(a3);
if(a1.getAlias() != p1 || a3.isValid()) {
errln("LocalArray move assignment operator did not move");
}
LocalArray<UnicodeString> a4(static_cast<LocalArray<UnicodeString> &&>(a2));
if(a4.getAlias() != p2 || a2.isValid()) {
errln("LocalArray move constructor did not move");
}
#else
infoln("TestLocalArrayMoveSwap() without rvalue references");
#endif
// Move self assignment leaves the object valid but in an undefined state.
// Do it to make sure there is no crash,
// but do not check for any particular resulting value.
a1.moveFrom(a1);
a3.moveFrom(a3);
}
#include "unicode/ucnvsel.h"
#include "unicode/ucal.h"
#include "unicode/udatpg.h"
@ -475,6 +599,61 @@ void LocalPointerTest::TestLocalXyzPointer() {
// destructors
}
void LocalPointerTest::TestLocalXyzPointerMoveSwap() {
#if !UCONFIG_NO_NORMALIZATION
IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerMoveSwap");
const UNormalizer2 *nfc=unorm2_getNFCInstance(errorCode);
const UNormalizer2 *nfd=unorm2_getNFDInstance(errorCode);
if(errorCode.logIfFailureAndReset("unorm2_getNF[CD]Instance()")) {
return;
}
UnicodeSet emptySet;
UNormalizer2 *p1 = unorm2_openFiltered(nfc, emptySet.toUSet(), errorCode);
UNormalizer2 *p2 = unorm2_openFiltered(nfd, emptySet.toUSet(), errorCode);
LocalUNormalizer2Pointer f1(p1);
LocalUNormalizer2Pointer f2(p2);
if(errorCode.logIfFailureAndReset("unorm2_openFiltered()")) {
return;
}
if(f1.isNull() || f2.isNull()) {
errln("LocalUNormalizer2Pointer failure");
return;
}
f1.swap(f2);
if(f1.getAlias() != p2 || f2.getAlias() != p1) {
errln("LocalUNormalizer2Pointer.swap() did not swap");
}
swap(f1, f2);
if(f1.getAlias() != p1 || f2.getAlias() != p2) {
errln("swap(LocalUNormalizer2Pointer) did not swap back");
}
LocalUNormalizer2Pointer f3;
f3.moveFrom(f1);
if(f3.getAlias() != p1 || f1.isValid()) {
errln("LocalUNormalizer2Pointer.moveFrom() did not move");
}
#if U_HAVE_RVALUE_REFERENCES
infoln("TestLocalXyzPointerMoveSwap() with rvalue references");
f1 = static_cast<LocalUNormalizer2Pointer &&>(f3);
if(f1.getAlias() != p1 || f3.isValid()) {
errln("LocalUNormalizer2Pointer move assignment operator did not move");
}
LocalUNormalizer2Pointer f4(static_cast<LocalUNormalizer2Pointer &&>(f2));
if(f4.getAlias() != p2 || f2.isValid()) {
errln("LocalUNormalizer2Pointer move constructor did not move");
}
#else
infoln("TestLocalXyzPointerMoveSwap() without rvalue references");
#endif
#endif /* !UCONFIG_NO_NORMALIZATION */
// Move self assignment leaves the object valid but in an undefined state.
// Do it to make sure there is no crash,
// but do not check for any particular resulting value.
f1.moveFrom(f1);
f3.moveFrom(f3);
}
// Try LocalXyzPointer types with NULL pointers.
void LocalPointerTest::TestLocalXyzPointerNull() {
{

View file

@ -30,41 +30,33 @@ UnicodeStringTest::~UnicodeStringTest() {}
void UnicodeStringTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char *par)
{
if (exec) logln("TestSuite UnicodeStringTest: ");
switch (index) {
case 0:
name = "StringCaseTest";
if (exec) {
logln("StringCaseTest---"); logln("");
StringCaseTest test;
callTest(test, par);
}
break;
case 1: name = "TestBasicManipulation"; if (exec) TestBasicManipulation(); break;
case 2: name = "TestCompare"; if (exec) TestCompare(); break;
case 3: name = "TestExtract"; if (exec) TestExtract(); break;
case 4: name = "TestRemoveReplace"; if (exec) TestRemoveReplace(); break;
case 5: name = "TestSearching"; if (exec) TestSearching(); break;
case 6: name = "TestSpacePadding"; if (exec) TestSpacePadding(); break;
case 7: name = "TestPrefixAndSuffix"; if (exec) TestPrefixAndSuffix(); break;
case 8: name = "TestFindAndReplace"; if (exec) TestFindAndReplace(); break;
case 9: name = "TestBogus"; if (exec) TestBogus(); break;
case 10: name = "TestReverse"; if (exec) TestReverse(); break;
case 11: name = "TestMiscellaneous"; if (exec) TestMiscellaneous(); break;
case 12: name = "TestStackAllocation"; if (exec) TestStackAllocation(); break;
case 13: name = "TestUnescape"; if (exec) TestUnescape(); break;
case 14: name = "TestCountChar32"; if (exec) TestCountChar32(); break;
case 15: name = "TestStringEnumeration"; if (exec) TestStringEnumeration(); break;
case 16: name = "TestNameSpace"; if (exec) TestNameSpace(); break;
case 17: name = "TestUTF32"; if (exec) TestUTF32(); break;
case 18: name = "TestUTF8"; if (exec) TestUTF8(); break;
case 19: name = "TestReadOnlyAlias"; if (exec) TestReadOnlyAlias(); break;
case 20: name = "TestAppendable"; if (exec) TestAppendable(); break;
case 21: name = "TestUnicodeStringImplementsAppendable"; if (exec) TestUnicodeStringImplementsAppendable(); break;
case 22: name = "TestSizeofUnicodeString"; if (exec) TestSizeofUnicodeString(); break;
case 23: name = "TestStartsWithAndEndsWithNulTerminated"; if (exec) TestStartsWithAndEndsWithNulTerminated(); break;
default: name = ""; break; //needed to end loop
}
TESTCASE_AUTO_BEGIN;
TESTCASE_AUTO_CLASS(StringCaseTest);
TESTCASE_AUTO(TestBasicManipulation);
TESTCASE_AUTO(TestCompare);
TESTCASE_AUTO(TestExtract);
TESTCASE_AUTO(TestRemoveReplace);
TESTCASE_AUTO(TestSearching);
TESTCASE_AUTO(TestSpacePadding);
TESTCASE_AUTO(TestPrefixAndSuffix);
TESTCASE_AUTO(TestFindAndReplace);
TESTCASE_AUTO(TestBogus);
TESTCASE_AUTO(TestReverse);
TESTCASE_AUTO(TestMiscellaneous);
TESTCASE_AUTO(TestStackAllocation);
TESTCASE_AUTO(TestUnescape);
TESTCASE_AUTO(TestCountChar32);
TESTCASE_AUTO(TestStringEnumeration);
TESTCASE_AUTO(TestNameSpace);
TESTCASE_AUTO(TestUTF32);
TESTCASE_AUTO(TestUTF8);
TESTCASE_AUTO(TestReadOnlyAlias);
TESTCASE_AUTO(TestAppendable);
TESTCASE_AUTO(TestUnicodeStringImplementsAppendable);
TESTCASE_AUTO(TestSizeofUnicodeString);
TESTCASE_AUTO(TestStartsWithAndEndsWithNulTerminated);
TESTCASE_AUTO(TestMoveSwap);
TESTCASE_AUTO_END;
}
void
@ -2130,3 +2122,66 @@ UnicodeStringTest::TestSizeofUnicodeString() {
expectedStackBufferLength);
}
}
void
UnicodeStringTest::TestMoveSwap() {
static const UChar abc[3] = { 0x61, 0x62, 0x63 }; // "abc"
UnicodeString s1(FALSE, abc, UPRV_LENGTHOF(abc)); // read-only alias
UnicodeString s2(100, 0x7a, 100); // 100 * 'z' should be on the heap
UnicodeString s3("defg", 4, US_INV); // in stack buffer
const UChar *p = s2.getBuffer();
s1.swap(s2);
if(s1.getBuffer() != p || s1.length() != 100 || s2.getBuffer() != abc || s2.length() != 3) {
errln("UnicodeString.swap() did not swap");
}
swap(s2, s3);
if(s2 != UNICODE_STRING_SIMPLE("defg") || s3.getBuffer() != abc || s3.length() != 3) {
errln("swap(UnicodeString) did not swap back");
}
UnicodeString s4;
s4.moveFrom(s1);
if(s4.getBuffer() != p || s4.length() != 100 || !s1.isBogus()) {
errln("UnicodeString.moveFrom(heap) did not move");
}
UnicodeString s5;
s5.moveFrom(s2);
if(s5 != UNICODE_STRING_SIMPLE("defg")) {
errln("UnicodeString.moveFrom(stack) did not move");
}
UnicodeString s6;
s6.moveFrom(s3);
if(s6.getBuffer() != abc || s6.length() != 3) {
errln("UnicodeString.moveFrom(alias) did not move");
}
#if U_HAVE_RVALUE_REFERENCES
infoln("TestMoveSwap() with rvalue references");
s1 = static_cast<UnicodeString &&>(s6);
if(s1.getBuffer() != abc || s1.length() != 3) {
errln("UnicodeString move assignment operator did not move");
}
UnicodeString s7(static_cast<UnicodeString &&>(s4));
if(s7.getBuffer() != p || s7.length() != 100 || !s4.isBogus()) {
errln("UnicodeString move constructor did not move");
}
#else
infoln("TestMoveSwap() without rvalue references");
UnicodeString s7;
#endif
// Move self assignment leaves the object valid but in an undefined state.
// Do it to make sure there is no crash,
// but do not check for any particular resulting value.
s1.moveFrom(s1);
s2.moveFrom(s2);
s3.moveFrom(s3);
s4.moveFrom(s4);
s5.moveFrom(s5);
s6.moveFrom(s6);
s7.moveFrom(s7);
// Simple copy assignment must work.
UnicodeString simple = UNICODE_STRING_SIMPLE("simple");
s1 = s6 = s4 = s7 = simple;
if(s1 != simple || s4 != simple || s6 != simple || s7 != simple) {
errln("UnicodeString copy after self-move did not work");
}
}

View file

@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2012, International Business Machines Corporation and
* Copyright (c) 1997-2015, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
@ -88,6 +88,7 @@ public:
void TestAppendable();
void TestUnicodeStringImplementsAppendable();
void TestSizeofUnicodeString();
void TestMoveSwap();
};
class StringCaseTest: public IntlTest {