From 2c2bf3e72a21f0d85f234f1aef4da11eb065bd08 Mon Sep 17 00:00:00 2001 From: Markus Scherer Date: Wed, 11 Mar 2009 05:27:11 +0000 Subject: [PATCH] ICU-6483 add ErrorCode class, C++ convenience wrapper for UErrorCode X-SVN-Rev: 25546 --- icu4c/source/common/Makefile.in | 2 +- icu4c/source/common/common.vcproj | 44 ++++++++ icu4c/source/common/errorcode.cpp | 43 ++++++++ icu4c/source/common/unicode/errorcode.h | 103 +++++++++++++++++++ icu4c/source/test/intltest/itutil.cpp | 127 +++++++++++++++++++++++- icu4c/source/test/intltest/itutil.h | 10 +- 6 files changed, 323 insertions(+), 6 deletions(-) create mode 100644 icu4c/source/common/errorcode.cpp create mode 100644 icu4c/source/common/unicode/errorcode.h diff --git a/icu4c/source/common/Makefile.in b/icu4c/source/common/Makefile.in index 83fbe398260..45bb9813d00 100644 --- a/icu4c/source/common/Makefile.in +++ b/icu4c/source/common/Makefile.in @@ -67,7 +67,7 @@ LDFLAGS += $(LDFLAGSICUUC) # $(LIBICUDT) is either stub data or the real DLL common data. LIBS = $(LIBICUDT) $(DEFAULT_LIBS) -OBJECTS = putil.o umath.o utypes.o uinvchar.o umutex.o ucln_cmn.o uinit.o uobject.o cmemory.o \ +OBJECTS = errorcode.o putil.o umath.o utypes.o uinvchar.o umutex.o ucln_cmn.o uinit.o uobject.o cmemory.o \ udata.o ucmndata.o udatamem.o umapfile.o udataswp.o ucol_swp.o utrace.o \ uhash.o uhash_us.o uenum.o ustrenum.o uvector.o ustack.o uvectr32.o \ ucnv.o ucnv_bld.o ucnv_cnv.o ucnv_io.o ucnv_cb.o ucnv_err.o ucnvlat1.o \ diff --git a/icu4c/source/common/common.vcproj b/icu4c/source/common/common.vcproj index 68b5e4b1408..3ef8763211b 100644 --- a/icu4c/source/common/common.vcproj +++ b/icu4c/source/common/common.vcproj @@ -1044,6 +1044,50 @@ /> + + + + + + + + + + + + + + + + diff --git a/icu4c/source/common/errorcode.cpp b/icu4c/source/common/errorcode.cpp new file mode 100644 index 00000000000..e0b70706090 --- /dev/null +++ b/icu4c/source/common/errorcode.cpp @@ -0,0 +1,43 @@ +/* +******************************************************************************* +* +* Copyright (C) 2009, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: errorcode.cpp +* encoding: US-ASCII +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2009mar10 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" +#include "unicode/errorcode.h" + +U_NAMESPACE_BEGIN + +ErrorCode::~ErrorCode() {} +/* Logically + if(isFailure()) { + handleFailure(kDestructor); + } + but in the destructor, even if it's virtual, this does not call + the subclass' handleFailure(), and our own handleFailure() + does not do anything. + The subclass must have this code. +*/ + +UErrorCode ErrorCode::reset() { + UErrorCode code = errorCode; + errorCode = U_ZERO_ERROR; + return code; +} + +void ErrorCode::check() const { + if(isFailure()) { handleFailure(kCheck); } +} + +U_NAMESPACE_END diff --git a/icu4c/source/common/unicode/errorcode.h b/icu4c/source/common/unicode/errorcode.h new file mode 100644 index 00000000000..49ca45f9397 --- /dev/null +++ b/icu4c/source/common/unicode/errorcode.h @@ -0,0 +1,103 @@ +/* +******************************************************************************* +* +* Copyright (C) 2009, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: errorcode.h +* encoding: US-ASCII +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2009mar10 +* created by: Markus W. Scherer +*/ + +#ifndef __ERRORCODE_H__ +#define __ERRORCODE_H__ + +/** + * \file + * \brief C++ API: ErrorCode class intended to make it easier to use + * ICU C and C++ APIs from C++ user code. + */ + +#include "unicode/utypes.h" +#include "unicode/uobject.h" + +U_NAMESPACE_BEGIN + +/** + * Wrapper class for UErrorCode, with conversion operators for direct use + * in ICU C and C++ APIs. + * Intended to be used as a base class, where a subclass overrides + * the handleFailure() function so that it throws an exception, + * does an assert(), logs an error, etc. + * This is not an abstract base class. This class can be used and instantiated + * by itself, although it will be more useful when subclassed. + * + * Features: + * - The constructor initializes the internal UErrorCode to U_ZERO_ERROR, + * removing one common source of errors. + * - Same use in C APIs taking a UErrorCode * (pointer) + * and C++ taking UErrorCode & (reference) via conversion operators. + * - Automatic checking for success when it goes out of scope. + * + * Code sample, using an appropriate IcuErrorCode subclass: + * \code + * IcuErrorCode error_code; + * UConverter *cnv = ucnv_open("Shift-JIS", error_code); + * length = ucnv_fromUChars(dest, capacity, src, length, error_code); + * ucnv_close(cnv); + * // IcuErrorCode destructor checks for success. + * \endcode + * + * @draft ICU 4.2 + */ +class U_COMMON_API ErrorCode: public UMemory { +public: + /** + * Default constructor. Initializes its UErrorCode to U_ZERO_ERROR. + * @draft ICU 4.2 + */ + ErrorCode() : errorCode(U_ZERO_ERROR) {} + /** + * Destructor, does nothing. + * A subclass destructor should do + * if(isFailure()) { handleFailure(kDestructor); } + * @draft ICU 4.2 + */ + virtual ~ErrorCode(); + /** Conversion operator, returns a reference. @draft ICU 4.2 */ + operator UErrorCode & () { return errorCode; } + /** Conversion operator, returns a pointer. @draft ICU 4.2 */ + operator UErrorCode * () { return &errorCode; } + /** Tests for U_SUCCESS(). @draft ICU 4.2 */ + UBool isSuccess() const { return U_SUCCESS(errorCode); } + /** Tests for U_FAILURE(). @draft ICU 4.2 */ + UBool isFailure() const { return U_FAILURE(errorCode); } + /** Returns the UErrorCode value. @draft ICU 4.2 */ + UErrorCode get() const { return errorCode; } + /** Sets the UErrorCode value. @draft ICU 4.2 */ + void set(UErrorCode value) { errorCode=value; } + /** Returns the UErrorCode value and resets it to U_ZERO_ERROR. @draft ICU 4.2 */ + UErrorCode reset(); + /** + * Checks for a failure code: + * if(isFailure()) { handleFailure(kCheck); } + * @draft ICU 4.2 + */ + void check() const; + +protected: + UErrorCode errorCode; + enum EOrigin { kCheck, kDestructor }; + // Note: A C++ class destructor must not throw an exception. + // Use the origin parameter to avoid this if necessary. + virtual void handleFailure(EOrigin origin) const {} +}; + +U_NAMESPACE_END + +#endif // __ERRORCODE_H__ diff --git a/icu4c/source/test/intltest/itutil.cpp b/icu4c/source/test/intltest/itutil.cpp index 032ad74cfd0..f9a4629bde1 100644 --- a/icu4c/source/test/intltest/itutil.cpp +++ b/icu4c/source/test/intltest/itutil.cpp @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2008, International Business Machines Corporation and + * Copyright (c) 1997-2009, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ @@ -10,6 +10,7 @@ */ #include "unicode/utypes.h" +#include "unicode/errorcode.h" #include "itutil.h" #include "strtest.h" #include "loctest.h" @@ -55,8 +56,130 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* & CASE(11, UVectorTest); CASE(12, UTextTest); CASE(13, LocaleAliasTest); - CASE(14, UnicodeSetTest); + CASE(14, UnicodeSetTest); + CASE(15, ErrorCodeTest); default: name = ""; break; //needed to end loop } } +void ErrorCodeTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* par) { + if (exec) logln("TestSuite Utilities: "); + switch (index) { + case 0: name = "TestErrorCode"; if (exec) TestErrorCode(); break; + case 1: name = "TestSubclass"; if (exec) TestSubclass(); break; + default: name = ""; break; //needed to end loop + } +} + +static void RefPlusOne(UErrorCode &code) { code=(UErrorCode)(code+1); } +static void PtrPlusTwo(UErrorCode *code) { *code=(UErrorCode)(*code+2); } + +void ErrorCodeTest::TestErrorCode() { + ErrorCode errorCode; + if(errorCode.get()!=U_ZERO_ERROR || !errorCode.isSuccess() || errorCode.isFailure()) { + errln("ErrorCode did not initialize properly"); + return; + } + errorCode.check(); + RefPlusOne(errorCode); + if(errorCode.get()!=U_ILLEGAL_ARGUMENT_ERROR || errorCode.isSuccess() || !errorCode.isFailure()) { + errln("ErrorCode did not yield a writable reference"); + } + PtrPlusTwo(errorCode); + if(errorCode.get()!=U_INVALID_FORMAT_ERROR || errorCode.isSuccess() || !errorCode.isFailure()) { + errln("ErrorCode did not yield a writable pointer"); + } + errorCode.set(U_PARSE_ERROR); + if(errorCode.get()!=U_PARSE_ERROR || errorCode.isSuccess() || !errorCode.isFailure()) { + errln("ErrorCode.set() failed"); + } + if( errorCode.reset()!=U_PARSE_ERROR || errorCode.get()!=U_ZERO_ERROR || + !errorCode.isSuccess() || errorCode.isFailure() + ) { + errln("ErrorCode did not reset properly"); + } +} + +class MyErrorCode: public ErrorCode { +public: + MyErrorCode(int32_t &countChecks, int32_t &countDests) + : checks(countChecks), dests(countDests) {} + ~MyErrorCode() { + if(isFailure()) { + handleFailure(kDestructor); + } + } +private: + virtual void handleFailure(EOrigin origin) const { + switch(origin) { + case kCheck: + ++checks; + break; + case kDestructor: + ++dests; + break; + default: + break; + } + } + int32_t &checks; + int32_t &dests; +}; + +void ErrorCodeTest::TestSubclass() { + int32_t countChecks=0; + int32_t countDests=0; + { + MyErrorCode errorCode(countChecks, countDests); + if( errorCode.get()!=U_ZERO_ERROR || !errorCode.isSuccess() || errorCode.isFailure() || + countChecks!=0 || countDests!=0 + ) { + errln("ErrorCode did not initialize properly"); + return; + } + errorCode.check(); + if(countChecks!=0) { + errln("ErrorCode.check() called handleFailure(kCheck) despite success"); + } + RefPlusOne(errorCode); + if(errorCode.get()!=U_ILLEGAL_ARGUMENT_ERROR || errorCode.isSuccess() || !errorCode.isFailure()) { + errln("ErrorCode did not yield a writable reference"); + } + errorCode.check(); + if(countChecks!=1) { + errln("ErrorCode.check() did not handleFailure(kCheck)"); + } + PtrPlusTwo(errorCode); + if(errorCode.get()!=U_INVALID_FORMAT_ERROR || errorCode.isSuccess() || !errorCode.isFailure()) { + errln("ErrorCode did not yield a writable pointer"); + } + errorCode.check(); + if(countChecks!=2) { + errln("ErrorCode.check() did not handleFailure(kCheck)"); + } + errorCode.set(U_PARSE_ERROR); + if(errorCode.get()!=U_PARSE_ERROR || errorCode.isSuccess() || !errorCode.isFailure()) { + errln("ErrorCode.set() failed"); + } + if( errorCode.reset()!=U_PARSE_ERROR || errorCode.get()!=U_ZERO_ERROR || + !errorCode.isSuccess() || errorCode.isFailure() + ) { + errln("ErrorCode did not reset properly"); + } + errorCode.check(); + if(countChecks!=2) { + errln("ErrorCode.check() called handleFailure(kCheck) despite success"); + } + } + if(countDests!=0) { + errln("ErrorCode.check() called handleFailure(kDestructor) despite success"); + } + countChecks=countDests=0; + { + MyErrorCode errorCode(countChecks, countDests); + errorCode.set(U_PARSE_ERROR); + } + if(countDests!=1) { + errln("ErrorCode destructor did not handleFailure(kDestructor)"); + } +} diff --git a/icu4c/source/test/intltest/itutil.h b/icu4c/source/test/intltest/itutil.h index e2dd8e10e3c..5fe691e4e3c 100644 --- a/icu4c/source/test/intltest/itutil.h +++ b/icu4c/source/test/intltest/itutil.h @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2001, International Business Machines Corporation and + * Copyright (c) 1997-2009, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ @@ -11,14 +11,18 @@ #ifndef _INTLTESTUTILITIES #define _INTLTESTUTILITIES - #include "intltest.h" - class IntlTestUtilities: public IntlTest { public: void runIndexedTest( int32_t index, UBool exec, const char* &name, char* par = NULL ); }; +class ErrorCodeTest: public IntlTest { +public: + void runIndexedTest(int32_t index, UBool exec, const char* &name, char* par = NULL); + void TestErrorCode(); + void TestSubclass(); +}; #endif