Click here to Skip to main content
15,880,796 members
Articles / Programming Languages / C++
Article

Active Error Codes

Rate me:
Please Sign up or sign in to vote.
4.79/5 (12 votes)
2 May 2009CPOL5 min read 28.3K   101   16   5
A mechanism to ensure that error codes are checked and not just ignored

Motivation

The eternal question - should you use error codes or exceptions? Both have advantages at times, disadvantages at other times. Exceptions don't need to be checked, but make it more difficult to check an error locally and have an impact on performance.

Enter active error codes - error codes return values that know if you've checked them...and if you don't, then they throw an exception (if they indicate an error).

Tutorial

So, what is an error code? For the purposes of active error codes, an error code is a value of some arbitrary type that can be determined to be either 'good' (not indicating an error) or 'bad' (indicating an error). For example, the Win32 function CreateFile returns the pre-defined value INVALID_HANDLE_VALUE, of type HANDLE, if a file couldn't be opened. Another example, CoCreateInstance, returns an HRESULT. This indicates some form of failure if the most significant bit is set, so there isn't one good or one bad value. In this case, you'd probably need some form of function to determine the status of the error code.

So, active error codes come in two categories - errors that have a single value that indicate that an erroneous state has (or has not) been encountered and errors that use a function to determine their status:

C++
// Define and use an error code (of type int) that has a single 'good' value (0)
typedef ErrCodeWithGoodValue<int, 0> Err;
Err Test_Err(bool isGood)
{
   // Return the error's good value if isGood is true, some other value if false.
   int e = isGood?(Err::value):(Err::value+1);
   return Err(e);
}

// Define and use an error code (of type HRESULT) defined by a function (HRIsGood)
bool HRIsGood(HRESULT hr)
{
   return SUCCEEDED(hr);
}
typedef ErrCodeWithFn<HRESULT, &HRIsGood> eHRESULT;
eHRESULT Test_eHRESULT(bool isGood)
{
   return isGood?S_OK:E_UNEXPECTED;
}

These error code values can be manually checked, or can be left to throw an exception:

C++
void TestThrowOnError(bool b)
{
   // Will throw an exception when Test's return value
   // goes out of scope, i.e. at the end of the line
   Test_eHRESULT(b);
}

void TestHandleError(bool b)
{
   if (Test_Err(b).good())
   {
      std::cout << "Good\n";
   }
   else
   {
      std::cout << "Bad\n";
   }
}

If the exception throwing capability of error codes is used, then the error value return should not be assigned to a variable - this will have the effect of delaying the check of the error value until the variable goes out of scope, which could be after execution of functionality that is dependent on the possibly erroneous function.

Usage

This library consists of a single header file, ActiveErrors.h. This should be #included into the files that need to define error code types. In general, this is likely to be in header files that specify function and (within class definitions) method signatures.

Reference

C++
template <class ErrCodeType, bool (*IsGoodValueFn)(ErrCodeType)> class ErrCodeWithFn

Template class that uses a function to determine whether an error value indicates an error condition.

Template Parameters

C++
class ErrCodeType 

ErrCodeType should be a copy-constructible type. In addition, as values of this type are passed by value, it should be a cheaply copied type.

C++
bool (*IsGoodValueFn)(ErrCodeType) 

Pointer to the function that determines error code goodness. Note that this cannot be a pointer to method.

Methods

C++
ErrCodeWithFn(ErrCodeType errorCode)
ErrCodeWithFn(ErrCodeWithFn const& other)
ErrCodeWithFn& operator=(ErrCodeWithFn const& other)

The constructors store the error code value and mark it as unchecked. The copy constructor and assignment operator will mark the source error object as checked.

C++
~ErrCodeWithFn()

The destructor will throw an exception if its value indicates an error and it hasn't been checked.

C++
bool good() const

good() indicates if an error has occurred. Executing this method will mark the error code as checked.

C++
operator ErrCodeType() const

This conversion operator allows the specific value of the error code to be examined. Executing this method will mark the error code as checked.

C++
template <class ErrCodeType, ErrCodeType GoodValue>
class ErrCodeWithGoodValue

Template class that for an error code type has a single good value.

Template Parameters

C++
class ErrCodeType 

ErrCodeType should be an integral type. This is a more restrictive constraint than for ErrCodeWithFn because an item of this type is used as a template parameter.

C++
ErrCodeType GoodValue 

Value of type ErrCodeType that is the single 'good' value of type ErrCodeType.

Methods

C++
ErrCodeWithGoodValue(ErrCodeType errorCode)
ErrCodeWithGoodValue(ErrCodeWithGoodValue const& other)
ErrCodeWithGoodValue& operator=(ErrCodeWithGoodValue const& other)

The constructors store the error code value and mark it as unchecked. The copy constructor and assignment operator will mark the source error object as checked.

C++
~ErrCodeWithGoodValue()

The destructor will throw an exception if its value indicates an error and it hasn't been checked.

C++
bool good() const

good() indicates if an error has occurred. Executing this method will mark the error code as checked.

C++
operator ErrCodeType() const

This conversion operator allows the specific value of the error code to be examined. Executing this method will mark the error code as checked.

C++
template <class ErrCodeType, ErrCodeType BadValue> class ErrCodeWithBadValue

Template class that for an error code type has a single bad value.

Template Parameters

C++
class ErrCodeType 

ErrCodeType should be an integral type. This is a more restrictive constraint than for ErrCodeWithFn because an item of this type is used as a template parameter.

C++
ErrCodeType BadValue 

Value of type ErrCodeType that is the single 'bad' value of type ErrCodeType.

Methods

C++
ErrCodeWithBadValue(ErrCodeType errorCode)
ErrCodeWithBadValue(ErrCodeWithBadValue const& other)
ErrCodeWithBadValue& operator=(ErrCodeWithBadValue const& other)

The constructors store the error code value and mark it as unchecked. The copy constructor and assignment operator will mark the source error object as checked.

C++
~ErrCodeWithBadValue()

The destructor will throw an exception if its value indicates an error and it hasn't been checked.

C++
bool good() const

good() indicates if an error has occurred. Executing this method will mark the error code as checked.

C++
operator ErrCodeType() const

This conversion operator allows the specific value of the error code to be examined.

Unlike the other error code classes, this operator will throw an exception if the value is bad and its validity hasn't been checked before this operator is called. This is because this type of return value is likely to return some resource that you will use, so reading the value and then using it without checking it is more likely to happen than with the other error code types.

An example of this sort of resource is a pointer returned from a memory allocation routine, which is good unless it is null.

C++
class UncheckedErrorBase

Base class for all exceptions thrown when an unchecked 'bad' error class is destructed.

C++
template <class ErrCodeType> class UncheckedError;

Template class; the exception thrown when an unchecked 'bad' error class is destructed. This class is derived from UncheckedErrorBase so that any unchecked error can be caught with a single catch handler.

Template Parameters

C++
class ErrCodeType 

The type of the underlying error value.

History

  • 1st May, 2009: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralThis is a solution to a whole generation. Pin
«_Superman_»13-May-09 19:25
professional«_Superman_»13-May-09 19:25 
QuestionWhy does ErrCodeWithFn::CheckError test good()? Pin
davidbakin3-May-09 17:36
davidbakin3-May-09 17:36 
AnswerRe: Why does ErrCodeWithFn::CheckError test good()? Pin
Stuart Dootson3-May-09 23:37
professionalStuart Dootson3-May-09 23:37 
GeneralRe: Why does ErrCodeWithFn::CheckError test good()? Pin
davidbakin4-May-09 5:51
davidbakin4-May-09 5:51 
GeneralRe: Why does ErrCodeWithFn::CheckError test good()? Pin
Stuart Dootson4-May-09 10:17
professionalStuart Dootson4-May-09 10:17 
davidbakin wrote:
Perhaps an additional template parameter (an optional "policy" parameter) could control whether the exception was always thrown or only thrown on a bad value. I do understand how you intended the classes to be used.


Exactly what I was thinking - great minds and all that Smile | :)

Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.