diff --git a/icu4c/source/common/unicode/localpointer.h b/icu4c/source/common/unicode/localpointer.h index a4ceaefc424..0b67fb63da2 100644 --- a/icu4c/source/common/unicode/localpointer.h +++ b/icu4c/source/common/unicode/localpointer.h @@ -20,6 +20,20 @@ /** * \file * \brief C++ API: "Smart pointers" for use with and in ICU4C C++ code. + * + * These classes are inspired by + * - std::auto_ptr + * - boost::scoped_ptr & boost::scoped_array + * - Taligent Safe Pointers (TOnlyPointerTo) + * + * but none of those provide for all of the goals for ICU smart pointers: + * - Smart pointer owns the object and releases it when it goes out of scope. + * - No transfer of ownership via copy/assignment to reduce misuse. Simpler & more robust. + * - ICU-compatible: No exceptions. + * - Need to be able to orphan/release the pointer and its ownership. + * - Need variants for normal C++ object pointers, C++ arrays, and ICU C service objects. + * + * For details see http://site.icu-project.org/design/cpp/scoped_ptr */ #include "unicode/utypes.h" @@ -150,6 +164,17 @@ private: * "Smart pointer" class, deletes objects via the standard C++ delete operator. * For most methods see the LocalPointerBase base class. * + * Usage example: + * \code + * LocalPointer s(new UnicodeString((UChar32)0x50005)); + * int32_t length=s->length(); // 2 + * UChar lead=s->charAt(0); // 0xd900 + * if(some condition) { return; } // no need to explicitly delete the pointer + * s.adoptInstead(new UnicodeString((UChar)0xfffc)); + * length=s->length(); // 1 + * // no need to explicitly delete the pointer + * \endcode + * * @see LocalPointerBase * @draft ICU 4.4 */ @@ -186,6 +211,16 @@ public: * For most methods see the LocalPointerBase base class. * Adds operator[] for array item access. * + * Usage example: + * \code + * LocalArray a(new UnicodeString[2]); + * a[0].append((UChar)0x61); + * if(some condition) { return; } // no need to explicitly delete the array + * a.adoptInstead(new UnicodeString[4]); + * a[3].append((UChar)0x62).append((UChar)0x63).reverse(); + * // no need to explicitly delete the array + * \endcode + * * @see LocalPointerBase * @draft ICU 4.4 */ @@ -235,6 +270,15 @@ public: * Requirement: The closeFunction must tolerate a NULL pointer. * (We could add a NULL check here but it is normally redundant.) * + * Usage example: + * \code + * LocalUCaseMapPointer csm(ucasemap_open(localeID, options, &errorCode)); + * utf8OutLength=ucasemap_utf8ToLower(csm.getAlias(), + * utf8Out, (int32_t)sizeof(utf8Out), + * utf8In, utf8InLength, &errorCode); + * if(U_FAILURE(errorCode)) { return; } // no need to explicitly delete the UCaseMap + * \endcode + * * @see LocalPointerBase * @see LocalPointer * @draft ICU 4.4 diff --git a/icu4c/source/test/intltest/itmajor.cpp b/icu4c/source/test/intltest/itmajor.cpp index 341364b9d6f..b6b1ea8760f 100644 --- a/icu4c/source/test/intltest/itmajor.cpp +++ b/icu4c/source/test/intltest/itmajor.cpp @@ -15,6 +15,7 @@ ***********************************************************************/ #include "unicode/utypes.h" +#include "unicode/localpointer.h" #include "itmajor.h" #include "itutil.h" @@ -198,9 +199,8 @@ void MajorTestLevel::runIndexedTest( int32_t index, UBool exec, const char* &nam case 15: name = "bidi"; if (exec) { logln("TestSuite bidi---"); logln(); - IntlTest *test = createBiDiConformanceTest(); + LocalPointer test(createBiDiConformanceTest()); callTest(*test, par); - delete test; } break; diff --git a/icu4c/source/test/intltest/itutil.cpp b/icu4c/source/test/intltest/itutil.cpp index 6f0c1a96b56..09f06aed440 100644 --- a/icu4c/source/test/intltest/itutil.cpp +++ b/icu4c/source/test/intltest/itutil.cpp @@ -11,6 +11,7 @@ #include "unicode/utypes.h" #include "unicode/errorcode.h" +#include "unicode/localpointer.h" #include "itutil.h" #include "strtest.h" #include "loctest.h" @@ -28,6 +29,7 @@ #include "aliastst.h" #include "usettest.h" +static IntlTest *createLocalPointerTest(); #define CASE(id, test) case id: \ name = #test; \ @@ -58,6 +60,14 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* & CASE(13, LocaleAliasTest); CASE(14, UnicodeSetTest); CASE(15, ErrorCodeTest); + case 16: + name = "LocalPointerTest"; + if (exec) { + logln("TestSuite LocalPointerTest---"); logln(); + LocalPointer test(createLocalPointerTest()); + callTest(*test, par); + } + break; default: name = ""; break; //needed to end loop } } @@ -177,3 +187,206 @@ void ErrorCodeTest::TestSubclass() { errln("MyErrorCode destructor failed to detect failure"); } } + +class LocalPointerTest : public IntlTest { +public: + LocalPointerTest() {} + + void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=NULL); + + void TestLocalPointer(); + void TestLocalArray(); + void TestLocalXyzPointer(); + void TestLocalXyzPointerNull(); +}; + +static IntlTest *createLocalPointerTest() { + return new LocalPointerTest(); +} + +void LocalPointerTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *par) { + if(exec) { + logln("TestSuite LocalPointerTest: "); + } + switch (index) { + TESTCASE(0, TestLocalPointer); + TESTCASE(1, TestLocalArray); + TESTCASE(2, TestLocalXyzPointer); + TESTCASE(3, TestLocalXyzPointerNull); + default: + name=""; + break; // needed to end the loop + } +} + +// Exercise every LocalPointer and LocalPointerBase method. +void LocalPointerTest::TestLocalPointer() { + // constructor + LocalPointer s(new UnicodeString((UChar32)0x50005)); + // isNULL(), isValid(), operator==(), operator!=() + if(s.isNull() || !s.isValid() || s==NULL || !(s!=NULL)) { + errln("LocalPointer constructor or NULL test failure"); + return; + } + // getAlias(), operator->, operator* + if(s.getAlias()->length()!=2 || s->length()!=2 || (*s).length()!=2) { + errln("LocalPointer access failure"); + } + // adoptInstead(), orphan() + s.adoptInstead(new UnicodeString((UChar)0xfffc)); + if(s->length()!=1) { + errln("LocalPointer adoptInstead(U+FFFC) failure"); + } + UnicodeString *orphan=s.orphan(); + if(orphan==NULL || orphan->length()!=1 || s.isValid() || s!=NULL) { + errln("LocalPointer orphan() failure"); + } + // destructor + s.adoptInstead(new UnicodeString()); + if(s->length()!=0) { + errln("LocalPointer adoptInstead(empty) failure"); + } +} + +// Exercise every LocalArray method (but not LocalPointerBase). +void LocalPointerTest::TestLocalArray() { + // constructor + LocalArray a(new UnicodeString[2]); + // operator[]() + a[0].append((UChar)0x61); + a[1].append((UChar32)0x60006); + if(a[0].length()!=1 || a[1].length()!=2) { + errln("LocalArray access failure"); + } + // adoptInstead() + a.adoptInstead(new UnicodeString[4]); + a[3].append((UChar)0x62).append((UChar)0x63).reverse(); + if(a[3].length()!=2 || a[3][1]!=0x62) { + errln("LocalArray adoptInstead() failure"); + } + // destructor +} + +#include "unicode/ucnvsel.h" +#include "unicode/ucal.h" +#include "unicode/udatpg.h" +#include "unicode/umsg.h" +#include "unicode/uregex.h" +#include "unicode/utrans.h" + +// Use LocalXyzPointer types that are not covered elsewhere in the intltest suite. +void LocalPointerTest::TestLocalXyzPointer() { + IcuTestErrorCode errorCode(*this, "TestLocalXyzPointer"); + + static const char *const encoding="ISO-8859-1"; + LocalUConverterSelectorPointer sel( + ucnvsel_open(&encoding, 1, NULL, UCNV_ROUNDTRIP_SET, errorCode)); + if(errorCode.logIfFailureAndReset("ucnvsel_open()")) { + return; + } + if(sel.isNull()) { + errln("LocalUConverterSelectorPointer failure"); + return; + } + + LocalUCalendarPointer cal(ucal_open(NULL, 0, "root", UCAL_GREGORIAN, errorCode)); + if(errorCode.logIfFailureAndReset("ucal_open()")) { + return; + } + if(sel.isNull()) { + errln("LocalUCalendarPointer failure"); + return; + } + + LocalUDateTimePatternGeneratorPointer patgen(udatpg_open("root", errorCode)); + if(errorCode.logIfFailureAndReset("udatpg_open()")) { + return; + } + if(sel.isNull()) { + errln("LocalUDateTimePatternGeneratorPointer failure"); + return; + } + + UnicodeString hello=UNICODE_STRING_SIMPLE("Hello {0}!"); + LocalUMessageFormatPointer msg( + umsg_open(hello.getBuffer(), hello.length(), "root", NULL, errorCode)); + if(errorCode.logIfFailureAndReset("umsg_open()")) { + return; + } + if(sel.isNull()) { + errln("LocalUMessageFormatPointer failure"); + return; + } + + UnicodeString pattern=UNICODE_STRING_SIMPLE("abc|xy+z"); + LocalURegularExpressionPointer regex( + uregex_open(pattern.getBuffer(), pattern.length(), 0, NULL, errorCode)); + if(errorCode.logIfFailureAndReset("uregex_open()")) { + return; + } + if(sel.isNull()) { + errln("LocalURegularExpressionPointer failure"); + return; + } + + UnicodeString id=UNICODE_STRING_SIMPLE("Grek-Latn"); + LocalUTransliteratorPointer trans( + utrans_openU(id.getBuffer(), id.length(), UTRANS_FORWARD, NULL, 0, NULL, errorCode)); + if(errorCode.logIfFailureAndReset("utrans_open()")) { + return; + } + if(sel.isNull()) { + errln("LocalUTransliteratorPointer failure"); + return; + } + + // destructors +} + +// Try LocalXyzPointer types with NULL pointers. +void LocalPointerTest::TestLocalXyzPointerNull() { + { + IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalUConverterSelectorPointer"); + static const char *const encoding="ISO-8859-1"; + LocalUConverterSelectorPointer null; + LocalUConverterSelectorPointer sel( + ucnvsel_open(&encoding, 1, NULL, UCNV_ROUNDTRIP_SET, errorCode)); + sel.adoptInstead(NULL); + } + { + IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalUCalendarPointer"); + LocalUCalendarPointer null; + LocalUCalendarPointer cal(ucal_open(NULL, 0, "root", UCAL_GREGORIAN, errorCode)); + cal.adoptInstead(NULL); + } + { + IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalUDateTimePatternGeneratorPointer"); + LocalUDateTimePatternGeneratorPointer null; + LocalUDateTimePatternGeneratorPointer patgen(udatpg_open("root", errorCode)); + patgen.adoptInstead(NULL); + } + { + IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalUMessageFormatPointer"); + UnicodeString hello=UNICODE_STRING_SIMPLE("Hello {0}!"); + LocalUMessageFormatPointer null; + LocalUMessageFormatPointer msg( + umsg_open(hello.getBuffer(), hello.length(), "root", NULL, errorCode)); + msg.adoptInstead(NULL); + } + { + IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalURegularExpressionPointer"); + UnicodeString pattern=UNICODE_STRING_SIMPLE("abc|xy+z"); + LocalURegularExpressionPointer null; + LocalURegularExpressionPointer regex( + uregex_open(pattern.getBuffer(), pattern.length(), 0, NULL, errorCode)); + regex.adoptInstead(NULL); + } + { + IcuTestErrorCode errorCode(*this, "TestLocalXyzPointerNull/LocalUTransliteratorPointer"); + UnicodeString id=UNICODE_STRING_SIMPLE("Grek-Latn"); + LocalUTransliteratorPointer null; + LocalUTransliteratorPointer trans( + utrans_openU(id.getBuffer(), id.length(), UTRANS_FORWARD, NULL, 0, NULL, errorCode)); + trans.adoptInstead(NULL); + } +} diff --git a/icu4c/source/tools/ctestfw/tstdtmod.cpp b/icu4c/source/tools/ctestfw/tstdtmod.cpp index 8f1c4528c05..917f01cea2e 100644 --- a/icu4c/source/tools/ctestfw/tstdtmod.cpp +++ b/icu4c/source/tools/ctestfw/tstdtmod.cpp @@ -40,7 +40,7 @@ UBool IcuTestErrorCode::logIfFailureAndReset(const char *fmt, ...) { void IcuTestErrorCode::handleFailure() const { // testClass.errln("%s failure - %s", testName, errorName()); UnicodeString msg(testName, -1, US_INV); - msg.append(UNICODE_STRING_SIMPLE(" failure - ")).append(UnicodeString(errorName(), -1, US_INV)); + msg.append(UNICODE_STRING_SIMPLE(" failure: ")).append(UnicodeString(errorName(), -1, US_INV)); testClass.errln(msg); }