The errors are assigned values according to the HTTP response codes. The importance of this will become obvious when it comes to using the error codes. Whatever values you choose, errors should have non-zero values. As you may recall, the <system_error> facility uses a convention where zero means success.

You can use regular (that is, C++03-compatible) enums by dropping the class keyword:

enum http_error{ ...};

Note: C++0x's enum class differs from enum in that the former encloses enumerator names in the class scope. To access an enumerator you must prefix the class name, as in http_error::ok. You can approximate this behaviour by wrapping the plain enum in a namespace:

namespace http_error{ enum http_error_t { ... };}

For the remainder of this example I will assume the use of enum class. Applying the namespace-wrapping approach is left as an exercise for the reader.

Step 2: define an error_category class

An error_code object consists of both an error value and a category. The error category determines whether a value of 100 means http_error::continue_request, std::errc::network_down (ENETDOWN on Linux), or something else entirely.

To create a new category, you must derive a class from error_category:

For the moment, this class will implement only error_category's pure virtual functions.

Step 3: give the category a human-readable name

The error_category::name() virtual function must return a string identifying the category:

const char* http_category_impl::name() const{ return "http";}

This name does not need to be universally unique, as it is really only used when writing an error code to a std::ostream. However, it would certainly be desirable to make it unique within a given program.

Step 4: convert error codes to strings

The error_category::message() function converts an error value into a string that describes the error:

When you call the error_code::message() member function, the error_code in turn calls the above virtual function to obtain the error message.

It's important to remember that these error messages must stand alone; they may be written (to a log file, say) at a point in the program when no additional context is available. If you are wrapping an existing API that uses error messages with "inserts", you'll have to create your own messages. For example, if an HTTP API uses the message string "HTTP version %d.%d not supported", the equivalent stand-alone message would be "HTTP version not supported".

The <system_error> facility provides no assistance when it comes to localisation of these messages. It is likely that the messages emitted by your standard library's error categories will be based on the current locale. If localisation is a requirement in your program, I recommend using the same approach. (Some history: The LWG was aware of the need for localisation, but there was no design before the group that satisfactorily reconciled localisation with user-extensibility. Rather than engage in some design-by-committee, the LWG opted to say nothing in the standard about localisation of the error messages.)

Step 5: uniquely identify the category

The identity of an error_category-derived object is determined by its address. This means that when you write:

In this case, the category object is initialised on first use. C++0x also guarantees that the initialisation is thread-safe. (C++03 makes no such guarantee.)

History: In the early design stages, we considered using an integer or string to identify an error_code's category. The main issue with that approach was ensuring uniqueness in conjunction with user extensibility. If a category was identified by integer or string, what was to stop collisions between two unrelated libraries? Using object identity leverages the linker in preventing different categories from having the same identity. Furthermore, storing a pointer to a base class allows us to make error_codes polymorphic while keeping them as copyable value types.

Step 6: construct an error_code from the enum

As I showed in part 3, the <system_error> implementation requires a function called make_error_code() to associate an error value with a category. For the HTTP errors, you would write this function as follows:

Step 8 (optional): assign default error conditions

Some of the errors you define may have a similar meaning to the standard's errc error conditions. For example, the HTTP response code 403 Forbidden means basically the same thing as std::errc::permission_denied.

The error_category::default_error_condition() virtual function lets you define an error_condition that is equivalent to a given error code. (See part 2 for the definition of equivalence.) For the HTTP errors, you can write:

2 comments:

Anonymous
said...

Just so you know, I find this articles very interesting cause I'm in the process of designing an error class for my personal library. I wasn't sure what to you use (an enum ?) and now I can see other options. Thanks.