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:
typedef ErrCodeWithGoodValue<int, 0> Err;
Err Test_Err(bool isGood)
{
int e = isGood?(Err::value):(Err::value+1);
return Err(e);
}
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)
{
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 #include
d 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