Click here to Skip to main content
Click here to Skip to main content
Go to top

Active Error Codes

, 2 May 2009
Rate this:
Please Sign up or sign in to vote.
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:

// 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:

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

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

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.

bool (*IsGoodValueFn)(ErrCodeType) 

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

Methods

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.

~ErrCodeWithFn()

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

bool good() const

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

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.

template <class ErrCodeType, ErrCodeType GoodValue>
class ErrCodeWithGoodValue

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

Template Parameters

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.

ErrCodeType GoodValue 

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

Methods

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.

~ErrCodeWithGoodValue()

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

bool good() const

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

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.

template <class ErrCodeType, ErrCodeType BadValue> class ErrCodeWithBadValue

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

Template Parameters

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.

ErrCodeType BadValue 

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

Methods

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.

~ErrCodeWithBadValue()

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

bool good() const

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

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.

class UncheckedErrorBase

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

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

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)

Share

About the Author

Stuart Dootson
Architect
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralThis is a solution to a whole generation. Pinmember«_Superman_»13-May-09 19:25 
QuestionWhy does ErrCodeWithFn::CheckError test good()? Pinmemberdavidbakin3-May-09 17:36 
Hi! I like this idea, it is very simple yet will help enforce good coding habits. It is very easy to unintentionally omit a check of an error code.
 
In my version I have modified ErrCodeWithFn::CheckError() to throw whenever !hasBeenChecked, regardless of good(). The point of the class, it seems to me, is to notify the developer when an error code has not been checked, whether or not it happens to be a "good" value in a particular run. The developer forgot to check the error code, therefore he must be warned, end of story.
 
And I have a question: Why does ErrCodeWithBadValue override ErrCodeType() and call CheckError(), while ErrCodeWithGoodValue does not?
 
Thanks -- David
AnswerRe: Why does ErrCodeWithFn::CheckError test good()? PinmemberStuart Dootson3-May-09 23:37 
GeneralRe: Why does ErrCodeWithFn::CheckError test good()? Pinmemberdavidbakin4-May-09 5:51 
GeneralRe: Why does ErrCodeWithFn::CheckError test good()? PinmemberStuart Dootson4-May-09 10:17 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 2 May 2009
Article Copyright 2009 by Stuart Dootson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid