Overview
System error numbers are different in different platforms. e.g., file system errors. In C++11, a new exception class system_error along with helper classes - error_category, error_code, error_condition
were introduced to the diagnostic infrastructure to bring uniformity and develop cross platform applications.
Details
system_error
This exception was added in C++11 to report exceptions from Operating System and low level interfaces.
The class inherits from runtime_error. In addition to what() it adds code() that returns of type error_code to gather the error information.
Constructors
Name | Description |
---|---|
|
|
Example //1 system_error(error_code{io_errc::stream},string("hello world!")); //2 system_error(error_code{io_errc::stream},"hello world!"); //3 system_error(error_code{io_errc::stream}); //4 system_error(1,iostream_category(),string("hello world!")); //5 system_error(1,iostream_category(),"hello world!"); //6 system_error(1,iostream_category()); |
Methods
Name | Description |
---|---|
const error_code& code() | Returns the error_code object associated with the exception. Example auto se = system_error(error_code{io_errc::stream},string("hello world!")); //prints:1 cout << se.code().value(); |
const char* what() | Returns the message that describes the exception. This message includes the string used on construction as s, and -possibly- additional information, such as the message associated with the error_code Example auto se = system_error(error_code{io_errc::stream},string("hello world!")); //prints:hello world!: iostream error cout << se.what(); |
error_code
An error_code object holds an platform dependent error code value associated with a platform independent category.
The operating system and other low-level applications and libraries generate numerical error codes to represent possible results. These numerical values may carry essential information for a specific platform, but be non-portable from one platform to another.
For example the returned value for "path does not exist" error in POSIX platforms is 2 (ENOENT). In Windows it's 3 (ERROR_PATH_NOT_FOUND).
error_code objects associate such numerical codes to error categories, so that they can be interpreted when needed as more abstract (and portable) error conditions.
The following describes the class in more detail.
Constructors
Name | Description |
---|---|
|
|
Example void print_errorcode(error_code e) { cout << "Code: " << e.value() << endl; cout << "Category: " << e.category().name() << endl; cout << "Message: " << e.message() << endl; cout << endl; } //1 auto ec = error_code{}; /*prints Code: 0 Category: system Message: Success */ print_errorcode(ec); //2 ec = error_code{ENOENT,generic_category()}; /*prints Code: 2 Category: generic Message: No such file or directory */ print_errorcode(ec); //3 ec = make_error_code(io_errc::stream); /*prints Code: 1 Category: iostream Message: iostream error */ print_errorcode(ec); |
Methods
Name | Description |
---|---|
void assign (int ev, const error_category& cat) | Assigns the error_code object a value of ev associated with the error_category cat. Example error_code ec; auto fh = open( "noname.c", O_RDONLY ); if( fh == -1 ) { ec.assign(errno,generic_category()); } /*prints Code: 2 Category: generic Message: No such file or directory */ print_errorcode(ec); |
error_code& operator= (ErrorCodeEnum e) | Calls make_error_code() to construct an error code from e, whose value is assigned to the error_code object. Note that is_error_code_enum<decltype(e)>::value == true_type Example error_code ec; ifstream ifs("noname.c"); if( !ifs ) { ec = io_errc::stream; }/*prints Code: 1 Category: iostream Message: iostream error */print_errorcode(ec); |
void clear() | Clears the value in the error_code object so that it is set to a value of 0 of the system_category (indicating no error). Example error_code ec; ec = io_errc::stream; ec.clear(); /*prints Code: 0 Category: system Message: Success */ print_errorcode(ec); |
int value() | Returns the error value associated with the error_code object. |
const error_category& category() | Returns a reference to the error category associated with the error_code object. |
string message() | Error messages are defined by the category the error code belongs to. This is same as category().message(value()). |
error_condition default_error_condition() | Returns the default error_condition object associated with the error_code object. Same as category().default_error_condition(value()). |
operator bool() | Returns whether the error code has a numerical value other than 0. If it is zero (which is generally used to represent no error), the function returns false, otherwise it returns true. |
basic_ostream& operator<< (basic_ostream& os, const error_code& ec) | Writes a textual representation of the error code, producing the same output as the following operation: os << ec.category.name() << ':' << ec.value(); Example //prints:iostream:1 auto ec = make_error_code(io_errc::stream); cout << ec; |
External Methods
Name | Description |
---|---|
bool operator == (const error_code& lhs, const error_code& rhs) | compares two error codes for equality. Internally evaluated as (lhs.category()==rhs.category() && lhs.value()==rhs.value()). Example auto ec = make_error_code(io_errc::stream); auto ec2 = error_code(1,iostream_category()); //b:true auto b = (ec == ec2); |
bool operator != (const error_code& lhs, const error_code& rhs) | compares two error codes for inequality. Evaluated as !(lhs == rhs). Example auto ec = make_error_code(errc::file_exists); auto ec2 = error_code{ERROR_FILE_EXISTS,system_category()}; //b:true b = (ec != ec3); |
error_condition
error_condition is a platform-independent error code. Like error_code, it is uniquely identified by an integer value and a error_category, but unlike error_code, the value is not platform-dependent.
Therefore, it can be compared against a value. A typical implementation holds a value and a pointer to an error_category. An error_codition can be created with make_error_condition() by passing appropriate argument. For example, make_error_condition(errc::invalid_argument).
Because error_condition objects can be compared with error_code objects directly by using relational operators, error_condition objects are generally used to check whether a particular error_code obtained from the system matches a specific error condition no matter the system.
The categories associated with the error_condition and the error_code define the equivalences between them.
Constructors
Name | Description |
---|---|
|
|
Example void print_errorcondition(error_condition e) { cout << "Code: " << e.value() << endl; cout << "Category: " << e.category().name() << endl; cout << "Message: " << e.message() << endl; cout << endl; } //1 auto ec = error_condition{}; /*printsCode: 0 Category: generic Message: Success*/ print_errorcondition(ec); //2 ec = error_condition{ENOENT,generic_category()}; /*printsCode: 2 Category: generic Message: No such file or directory*/ print_errorcondition(ec); //3 ec = make_error_condition(io_errc::stream); /*prints Code: 1 Category: iostream Message: iostream error */ print_errorcondition(ec); |
Methods
Name | Description |
---|---|
void assign (int val, const error_category& cat) | Assigns the error_condition object a value of val associated with the error_category cat. Example error_condition ec; auto fh = open( "noname.c", O_RDONLY ); if( fh == -1 ) { ec.assign(errno,generic_category()); } /*prints Code: 2 Category: generic Message: No such file or directory */ print_errorcondition(ec); |
error_condition& operator= (ErrorCodeEnum e) | Calls make_error_condition() to construct an error error_condition from e, whose value is assigned to the error_condition object. Here is_error_condition_enum<decltype(e)>::value == true_type. Example error_condition ec; ifstream ifs("noname.c"); if( !ifs ) { ec = errc::no_such_file_or_directory; } /*prints Code: 2 Category: generic Message: No such file or directory */ print_errorcondition(ec); |
void clear() | Clears the value in the error_condition object so that it is set to a value of 0 of the generic_category (indicating no error). Example error_condition ec; ec = errc::no_such_file_or_directory; ec.clear(); /*prints Code: 0 Category: generic Message: Success */ print_errorcondition(ec); |
int value() | Returns the error value associated with the error_condition object. |
const error_category& category() | Returns a reference to the error category associated with the error_condition object. |
string message() | Error messages are defined by the category the error_condition belongs to. This is same as category().message(value()). |
operator bool() | Returns whether the error_condition has a numerical value other than 0. If it is zero (which is generally used to represent no error), the function returns false, otherwise it returns true. |
External Methods
Name | Description |
---|---|
|
Example //1 auto econd = make_error_condition(io_errc::stream); auto econd2 = error_condition(1,iostream_category()); //b:true bool b = (econd == econd); //2 auto ec = io_errc::stream; //b:true b = (ec == econd); |
|
Example //1 auto econd = errc::file_exists; auto econd2 = error_condition{EEXIST,system_category()}; //b:true bool b = (econd != econd2); //2 auto ec = make_error_code(errc::file_exists); //b:true b = (ec != econd2); |
Using error_code and error_condition
As discussed earlier, in some cases, error_code is platform dependent value and error_condition is platform independent value. Thus it's not appropriate to compare the result of an operation such as a system call that returns an error_code directly.
For example, the following code would be fine since error_code defines explicit bool() operator to ascertain if there were errors.
boost::system::error_code ec; create_directory("", ec); if (!ec) { // Success. } else { // Failure. }
However going deeper and examining the returned values as below, it's not advisable to use the returned values directly as they are platform dependent. For example the returned value of ec.value() call in POSIX platforms is ENOENT (2) and in Windows is ERROR_PATH_NOT_FOUND (3). Therefore use of error_code.value() should be avoided if there platform dependency.
boost::system::error_code ec;create_directory("", ec);//linux if (ec.value() == ENOENT) // No!//windows
if (ec.value() == ERROR_PATH_NOT_FOUND) // No!
instead error_code should be compared with error_condition as shown below.
boost::system::error_code ec;create_directory("", ec); if (ec == std::errc::no_such_file_or_directory)
In the above code, internally the if condition is evaluated as below:
ec.category().equivalent(ec.value(), std::errc::no_such_file_or_directory)
error_category
This type serves as a base class for specific category types.
Category types are used to identify the source of an error. They also define the relation between error_code and error_condition objects of its category, as well as the message set for error_code objects.
As noted earlier, an error_category is used to define sources of errors or categories of error codes and error_conditions.
It also provides method equivalent() that provides mapping between error_codes and
and error_conditions. In addition, it provides a method default_error_condition() to return error_condition for a error_code.
Standard library predefines following categories.
Name | Description |
---|---|
generic_category | identifies the generic error category. The generic category represents the error values of the portable subset of errno values defined by the POSIX standard, whereas the system category is OS dependent. |
system_category | identifies the operating system error category. Under POSIX, the system category represents the errno values returned by the OS APIs (a superset of those in the generic category), whereas under Windows, the system category represents the error values returned by GetLastError(). |
iostream_category | identifies the iostream error category. |
future_category | identifies the future error category. Even though future_error exception is not derived from system_error, it supports error_code. |
Methods
Name | Description |
---|---|
const char* name() | In derived classes, the function returns a C-string naming the category. For a generic_category object, it returns "generic". For a system_category object, it returns "system". For a iostream_category object, it returns "iostream". For a future_category object, it returns "future". |
error_condition default_error_condition (int val) | Returns the default error_condition object of this category that is associated with the error_code identified by a value of val. Returns the error condition for the given error value. Equivalent to error_condition(val, *this). Classes derived from error_category may override this function to map certain error values to a generic category. For example, system_category overrides this function to map the error values that match POSIX errno values to generic_category. |
| Checks whether error code is equivalent to an error condition for the error category represented by *this.
|
string message (int val) | Returns a string object with a message describing the error condition denoted by val. This function is called both by error_code::message and error_condition::message to obtain the corresponding message in the category. Therefore, numerical values used by custom error codes and error conditions should only match for a category if they describe the same error. |
Usage examples
As noted above an error_code in the exception should be always compared to an error_condition from the same category that can be created by make_error_condition().
future_category
The example 12 depicts the usage.
iostream_category
The example 13 depicts the usage.
Custom error_category
Application can define their own categories to throw custom error_codes in the exceptions.
This is discussed in detail below.
The following example 15 discusses an implementation of a custom error_category class to handle http protocol errors such as 404 (page not found) as below.
- type trait class is_error_condition_enum is implemented returning true, indicating that it's an error_condition enumeration.
- Custom enum class is defined enumerate error_conditions. e.g., client_error.
- make_error_condition() is overridden to return error_condition object for an enumeration for this category. e.g., client_error.
- It basically derives from error_category class.
- It defines a set of error numbers commonly indicate http errors such as error code 404.
- It overrides default_error_condition() to return a error_condition for a error number. e.g., returns error_condition, client_error for error code 404 .
- It overrides equivalent() to check if the error number in the error_code matches with the error condition. e.g., error code 404 with client_error.
- It overrides message() to return to text message for error number. for e.g., 404.
// custom error conditions enum type: enum class custom_errc { success=0, client_error, server_error, other }; namespace std { template<> struct is_error_condition_enum<custom_errc> : public true_type {}; } // custom category: struct custom_category_t : public std::error_category { virtual const char* name() const noexcept { return catname; } virtual std::error_condition default_error_condition (int ev) const noexcept { if ((ev>=200)&&(ev<300)) return std::error_condition(custom_errc::success); else if ((ev>=400)&&(ev<500)) return std::error_condition(custom_errc::client_error); else if ((ev>=500)&&(ev<600)) return std::error_condition(custom_errc::server_error); else return std::error_condition(custom_errc::other); } virtual bool equivalent (const std::error_code& code, int condition) const noexcept { return *this==code.category() && static_cast<int>(default_error_condition(code.value()).value())==condition; } virtual std::string message(int ev) const { switch (ev) { case 200: return "OK"; case 403: return "403 Forbidden"; case 404: return "404 Not Found"; case 500: return "500 Internal Server Error"; case 503: return "503 Service Unavailable"; default: return "Unknown error"; } } const char *catname = "custom_http"; } custom_category; // make_error_code overload to generate custom conditions: std::error_condition make_error_condition (custom_errc e) { return std::error_condition(static_cast<int>(e), custom_category); } void readapage(const char* url) { if (std::string(url) == "no url") throw (std::system_error(std::error_code(404, custom_category))); } int main() { //prints:client Error: 404 Not Found try { readapage("no url"); } catch(const std::system_error& ex) { auto &e = ex.code(); if (e == custom_errc::success) cout << "Success: " << e.message() << endl; else if (e == custom_errc::client_error) cerr << "Client Error: " << e.message() << endl; else if (e == custom_errc::server_error) cerr << "Server Error: " << e.message() << endl; else cerr << "Unknown" << endl;; }
No comments:
Post a Comment