Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Mandatory Error Codes Revisited


The Solution

If you want to use this kind of framework effectively, you first need to tweak it a bit.

First, change your assumption that an ErrorCode must be used. There is no real way to make sure that users actually used it. You should be more concerned with callers accepting the return code, rather than having them use it. After all, a caller to this kind of method can accept the error code, then assign it to some variable that is not used.

Second, make sure that implicitly thrown exceptions won't terminate the process. To do this, first add a new class, ThrowableErrorCode (Listing Two). This class is returned from methods that want to enforce error code checking, instead of the ErrorCode. ThrowableErrorCode doesn't grant access to the error code it carries.

template <class CODE>
class ThrowableErrorCode

{
    template <class CODE> friend class ErrorCode;
    CODE        code_;
    bool        throw_;
public:
    /// ctor - receive the code and arm the exception
    ThrowableErrorCode(CODE i_code):
    code_(i_code),
    throw_(true)
    {}
    // explicitly ignore the error code and avoid exception
    operator IgnoreError() {
        this-> throw_ = false;
        return IgnoreError();
    }
    ~ThrowableErrorCode() {
        // will throw unless ErrorCode<CODE> or
                // IgnoreError will prevent it
        if (this-> throw_)
        {
            throw MandatoryErrorCodeException(...);
        }
    }
};
Listing Two

As its name implies, ThrowableErrorCode might throw an exception. In fact, it throws one when it goes out of scope. The only way to stop it from doing so is by either explicitly ignoring the error code using ThrowableErrorCode's operator IgnoreError, or by assigning it into an ErrorCode.

The new ErrorCode (Listing Three) does not throw an exception. It disarms ThrowableErrorCode and grants access to the error code.

template <class CODE>
class ErrorCode {
    CODE    code_;
public:
    // Explicit ctor to make sure that the user of this
    // class knows what she/he is doing
    explicit ErrorCode(ThrowableErrorCode<CODE>& code):
    code_(code.code_) {
        // prevent the throw
        code.throw_ = false;
    }
    operator CODE() {
        return this->code_;
    }
    ~ErrorCode()
    {}
};
Listing Three

So the caller to this kind of method has these alternatives:

  • Accept the returned ThrowableErrorCode into an ErrorCode (which, presumably, the caller uses). Good practice.
  • Explicitly ignore the returned error code by using the IgnoreError operator. Good practice.
  • Ignore the returned ThrowableErrorCode, in which case a temporary instance of ThrowableErrorCode is created and deleted within the scope of a statement, which causes an exception but won't jeopardize the process. Bad practice.
  • Accept the return code as ThrowableErrorCode, which means that the exception is not disarmed. This means that callers are willingly holding an instance of a class that might throw, in which case this caller better know what they are doing. Bad practice.

Example 3 shows how you can use this new framework. Now when you revisit the problem encountered in Example 2, the code works fine because ErrorCodes ec1 and ec2 disarm the ThrowableErrorCodes that return from the fallable methods. That is, in case of an exception from these methods, the code reaches the catch statement just as you intended.

// function declaration
ThrowableErrorCode<int> FallableFunction(); 	


// ok
ErrorCode<int> result = FallableFunction();
if (0 == result) {...}	

// will throw - but will not terminate the process
if (FallableFunction ()) {...} 

// will throw - but will not terminate the process
FallableFunction(); 

// ok - Explicitly ignoring the error code
(IgnoreError) FallableFunction(); 

// might throw - the caller is taking a risk!
ThrowableErrorCode<int> mightThrow = FallableFunction(); 

Example 3: Example use of the new framework.

Acknowledgment

Thanks to Andrei Alexandrescu.

DDJ


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.