© Peter Van Weert and Marc Gregoire 2016
Peter Van Weert and Marc GregoireC++ Standard Library Quick Reference10.1007/978-1-4842-1876-1_8

8. Diagnostics

Peter Van Weert and Marc Gregoire2
(1)
Kessel-Lo, Belgium
(2)
Meldert, Belgium
 

Assertions    <cassert>

Assertions are Boolean expressions that are expected to be true at a given point in the code. The assert macro of <cassert> is defined similar to this:
#ifdef NDEBUG
 #define assert(_)
#else
 #define assert(CONDITION) if (!CONDITION) { print_msg(...); std::abort(); }
#endif
If an assertion fails, a diagnostic message is written to the standard error output, and std::abort() is called, which terminates the application without performing any cleanup. While debugging an application, certain IDEs give you the option to continue the execution if an assertion fails. Common practice is to use assertions as a debugging aid and to define NDEBUG when building a release build of your application, turning asserts into no-operations.
Assertions are generally used to check invariants, such as loop invariants, or function pre- and postconditions. One example is parameter validation:
A417649_1_En_8_Figa_HTML.gif
A possible output of this program is
Assertion failed: msg != nullptr, file d:TestTest.cpp, line 13
Caution
Make sure the condition you provide to assert() does not have any side effects that are required for the proper execution of your program, because this expression is not evaluated if NDEBUG is defined (for example, for a release build).

Exceptions     <exception>, <stdexcept>

std::exception, defined in <exception>, is not intended to be thrown itself but instead serves as a base class for all exceptions defined by the Standard Library and can serve as a base class for your own. Figure 8-1 outlines all standard exceptions.
A417649_1_En_8_Fig1_HTML.jpg
Figure 8-1.
The C++ Standard Library exception hierarchy
An exception can be copied and offers a what() method that returns a string representation of the error. This function is virtual and should be overridden. The return type is const char*, but the character encoding is not specified (Unicode strings encoded as UTF-8 could be used, for instance; see Chapter 6).
The exceptions defined in <stdexcept> are the only standard exceptions that are intended to be thrown by application code. As a rule, logic_errors represent avoidable errors in the program’s logic, whereas runtime_errors are caused by less predictable events beyond the scope of the program. logic_error, runtime_error, and most of their subclasses (except system_errors and future_error, which require an error code, as discussed later) must be passed a std::string or const char* pointer upon construction, which is returned by what() afterward. There is thus no need to further override what().

Exception Pointers     <exception>

The <exception> header provides std::exception_ptr, an unspecified pointer-like type, used to store and transfer caught exceptions even without knowing the concrete exception type. An exception_ptr can point to a value of any type, not just an std::exception. It can point to a custom exception class, an integer, a string, and so on. Any pointed-to value stays valid while there is at least one exception_ptr still referring to it (that is, a reference-counted smart pointer may be used to implement exception_ptr).
A couple of functions are defined in <exception> to work with exception pointers:
exception_ptr std::current_exception() noexcept
  • Creates and returns an exception_ptr that refers to the exception (remember, this can have any type) currently in flight when called from inside a catch() block, either directly or indirectly (a catch() block may call, for example, a helper function to handle an exception). The returned exception_ptr refers to a null value if called when no exception is being handled.
template<typename T>
exception_ptr std::make_exception_ptr(T t) noexcept
  • Creates and returns an exception_ptr that points to t.
[[noreturn]] void std::rethrow_exception(exception_ptr)
  • Rethrows the exception to which the given exception_ptr points. This is the only way to obtain the object pointed to by an exception_ptr. An exception_ptr cannot be dereferenced, nor is there a getter function.
Once created, exception_ptrs can be copied, compared, and in particular assigned and compared with nullptr. This makes them useful to store and move exceptions around and to test later whether an exception has occurred. For this, an exception_ptr is also convertible to a Boolean: true if it points to an exception, false if it is a null pointer. Default constructed instances are equivalent to nullptr.
Exception pointers can be used, for example, to transfer exceptions from a worker thread to the main thread (note that this is essentially what the utilities of <future> discussed in the previous chapter implicitly do for you, as well):
A417649_1_En_8_Figb_HTML.gif

Nested Exceptions     <exception>

The <exception> header also offers facilities to work with nested exceptions. They allow you to wrap a caught exception in another one: for instance, to augment it with extra context information or to convert it to a more suitable exception for your application. std::nested_exception is a copyable mixin1 class whose default constructor captures current_exception() and stores it. This nested exception can be retrieved as an exception_ptr with nested_ptr(), or by using rethrow_nested(), which rethrows it. Take care, though: std::terminate() is called when rethrow_nested() is called without any stored exception. It is therefore generally recommended that you not use nested_exception directly, but use these helper methods instead:
[[noreturn]] template<typename T> void std::throw_with_nested(T&& t)
  • Throws an undefined type deriving from both std::nested_exception and T (with reference qualifiers stripped), which can be handled using a regular catch (const T&) expression, ignoring the nested exception. Being a std::nested_exception as well, it also contains the result of std::current_exception(), which may optionally be retrieved and handled.
template <typename T> void std::rethrow_if_nested(const T& t)
  • If t derives from nested_exception, calls rethrow_nested() on it; otherwise does nothing.
The following example demonstrates nested exceptions:
void execute_helper() {
   throw std::range_error("Out-of-range error in execute_helper()");
}
void execute() {
   try { execute_helper(); }
   catch (...) {
      std::throw_with_nested(std::runtime_error("Caught in execute()"));
   }
}
void print(const std::exception& exc) {
   std::cout << "Exception: " << exc.what() << std::endl;
   try { std::rethrow_if_nested(exc); }
   catch (const std::exception& e) {
      std::cout << "   Nested ";
      print(e);
   }
}
int main() {
   try { execute(); }
   catch (const std::exception& e) { print(e); }
}
The output of this piece of code is as follows:
Exception: Caught in execute()
   Nested Exception: Out-of-range error in execute_helper()  

System Errors    <system_error>

Errors from the operating system or other low-level APIs are called system errors. These are handled by classes and functions defined in the <system_error> header:
  • error_code: Generally wraps a platform-specific error code (an int), although for some categories the error codes are defined by the standard (see Table 8-1).
    Table 8-1.
    Available Error Category Functions and Corresponding Error Condition and Error Code Enum Classes
    Singleton Function
    Error Conditions
    Error Codes
    Header
    generic_category()
    std::errc
     
    <system_error>
    system_category()
      
    <system_error>
    iostream_category()
     
    std::io_errc
    <ios>
    future_category()
     
    std::future_errc
    <future>
  • error_condition: Wraps a portable, platform-independent error condition (an int). The enum class std::errc lists the built-in conditions. They correspond to the standard POSIX error codes, defined also as macros in <cerrno>. See Table 8-2 at the end of this chapter.
    Table 8-2.
    std::errc Error Condition Values and Corresponding <cerrno> Macros
    std::errc enum Value
    <cerrno> Macro
    address_family_not_supported
    EAFNOSUPPORT
    address_in_use
    EADDRINUSE
    address_not_available
    EADDRNOTAVAIL
    already_connected
    EISCONN
    argument_list_too_long
    E2BIG
    argument_out_of_domain
    EDOM
    bad_address
    EFAULT
    bad_file_descriptor
    EBADF
    bad_message
    EBADMSG
    broken_pipe
    EPIPE
    connection_aborted
    ECONNABORTED
    connection_already_in_progress
    EALREADY
    connection_refused
    ECONNREFUSED
    connection_reset
    ECONNRESET
    cross_device_link
    EXDEV
    destination_address_required
    EDESTADDRREQ
    device_or_resource_busy
    EBUSY
    directory_not_empty
    ENOTEMPTY
    executable_format_error
    ENOEXEC
    file_exists
    EEXIST
    file_too_large
    EFBIG
    filename_too_long
    ENAMETOOLONG
    function_not_supported
    ENOSYS
    host_unreachable
    EHOSTUNREACH
    identifier_removed
    EIDRM
    illegal_byte_sequence
    EILSEQ
    inappropriate_io_control_operation
    ENOTTY
    interrupted
    EINTR
    invalid_argument
    EINVAL
    invalid_seek
    ESPIPE
    io_error
    EIO
    is_a_directory
    EISDIR
    message_size
    EMSGSIZE
    network_down
    ENETDOWN
    network_reset
    ENETRESET
    network_unreachable
    ENETUNREACH
    no_buffer_space
    ENOBUFS
    no_child_process
    ECHILD
    no_link
    ENOLINK
    no_lock_available
    ENOLOCK
    no_message
    ENOMSG
    no_message_available
    ENODATA
    no_protocol_option
    ENOPROTOOPT
    no_space_on_device
    ENOSPC
    no_stream_resources
    ENOSR
    no_such_device
    ENODEV
    no_such_device_or_address
    ENXIO
    no_such_file_or_directory
    ENOENT
    no_such_process
    ESRCH
    not_a_directory
    ENOTDIR
    not_a_socket
    ENOTSOCK
    not_a_stream
    ENOSTR
    not_connected
    ENOTCONN
    not_enough_memory
    ENOMEM
    not_supported
    ENOTSUP
    operation_canceled
    ECANCELED
    operation_in_progress
    EINPROGRESS
    operation_not_permitted
    EPERM
    operation_not_supported
    EOPNOTSUPP
    operation_would_block
    EWOULDBLOCK
    owner_dead
    EOWNERDEAD
    permission_denied
    EACCES
    protocol_error
    EPROTO
    protocol_not_supported
    EPROTONOSUPPORT
    read_only_file_system
    EROFS
    resource_deadlock_would_occur
    EDEADLK
    resource_unavailable_try_again
    EAGAIN
    result_out_of_range
    ERANGE
    state_not_recoverable
    ENOTRECOVERABLE
    stream_timeout
    ETIME
    text_file_busy
    ETXTBSY
    timed_out
    ETIMEDOUT
    too_many_files_open
    EMFILE
    too_many_files_open_in_system
    ENFILE
    too_many_links
    EMLINK
    too_many_symbolic_link_levels
    ELOOP
    value_too_large
    EOVERFLOW
    wrong_protocol_type
    EPROTOTYPE
  • error_category: Error codes and conditions belong to a category. The category singleton objects are responsible for converting between both numberings.
  • system_error: An exception class (see Figure 8-1) with an extra code() member returning an error_code.
In addition to a numeric value, both error_code and error_condition objects hold a reference to their error_category. Within one category, a number is unique, but the same number may be used by different categories.
All this may seem fairly complicated, but the main uses of these errors remain straightforward. To compare a given error code, such as that from a caught system_error exception, with either an error condition or a code, the == and != operators can be used. For instance:
if (systemError.code() == std::errc::argument_out_of_domain)
   ...
Note
Working with std::ios_base::failure (Chapter 5) and future_error (Chapter 7) is analogous. They also have a code() member returning an error_code that can be compared with known code values (see Table 8-1) using == and !=.

std::error_category

The different std::error_category instances are implemented as singletons: that is, there is only one global, non-copyable instance per category. A number of predefined categories exist, obtainable from the global functions listed in Table 8-1.
An std::error_category has the following methods:
Member
Description
name()
Returns the category’s name (as a const char*).
message()
Returns an explanatory std::string for a given error condition value (an int).
default_error_condition()
Converts a given error code value (an int) to a portable error_condition.
equivalent()
Compares error codes with portable conditions. It is easier to use the == and != operators shown earlier, instead.

std::error_code

std::error_code encapsulates an error-code value and an error_category. There are three constructors:
  • A default one that sets the error code to 0 (this conventionally represents “no error”) and associates it with system_category.
  • One accepting an error code int and an error_category.
  • One constructing an error_code from an error-code enumeration value e by calling std::make_error_code(e). The parameter type must be an error-code enumeration type, an enumeration type for which the std::is_error_code_enum type trait has a value of true (see Chapter 2 for type traits). This automatically sets the correct category as well. The enum classes for the standard categories are shown in Table 8-1.
To raise your own std::system_error, you have to provide an error_code, which can be created with one of its constructors or with make_error_code(). For example:
A417649_1_En_8_Figc_HTML.gif
std::error_code provides the following methods:
Method
Description
assign(int,
error_category&)
Assigns the given error code and category to this error_code
operator=
Uses std::make_error_code() to assign a given error-code enumeration value to this error_code
clear()
Sets the error code to 0 and the category to system_category to represent no error
int value()
error_category& category()
Returns the error value / associated category
error_condition
default_error_condition()
Calls category().default_error_condition(value()), returning the corresponding portable error condition
string message()
Calls category().message(value())
operator bool
Returns true if the error code is not 0

std::error_condition

The std::error_condition class encapsulates a portable condition code and the associated error category. This class has a set of constructors and methods similar to error_code, except
  • It does not have a default_error_condition() method or equivalent function to go from error condition to error code.
  • Error condition enumerations are used instead of error code enumerations: those enum types for which the is_error_condition_enum type trait has a value of true.
  • Members that use std::make_error_code() use std::make_error_condition() instead.

C Error Numbers     <cerrno>

The <cerrno> header defines errno, a macro that expands to a value equivalent to int&. Functions can set the value of errno to a specific error value to signal an error. A separate errno is provided per thread of execution. Setting errno is very common for functions from the C headers. The C++ libraries mostly throw exceptions upon failure, although some set errno as well (std::string-to-numeric conversions, for example). Table 8-2 lists the macros with default POSIX error numbers defined by <cerrno>.
If you want to use errno to detect errors in functions that use errno to report errors, then you have to make sure to set errno to 0 before calling the function, as is done in this example (needs <cmath>)2:
A417649_1_En_8_Figd_HTML.gif
The output depends on your platform, but it can be something like the following:
Error: result out of range
For completeness, we show two alternative ways of reporting an error string for the current errno. They use, respectively, strerror() from <cstring> (take care: this function is not thread-safe!) and std::perror() from <cstdio>. The following two lines print a message similar to the earlier code:
A417649_1_En_8_Fige_HTML.gif

Failure Handling    <exception>

std::uncaught_exception()

If, anywhere in your code, you want to know whether an exception is currently in flight that has not been caught yet—in other words, detect that stack unwinding is in progress—use uncaught_exception() , which returns true if that is the case.
Note
There usually is no reason or safe way to use uncaught_exception(), so we advise against using it. It is just mentioned here for completeness.

std::terminate()

If exception handling fails for any reason—for example, an exception is thrown but never caught—then the runtime calls std::terminate(), which calls the terminate handler. The default handler calls std::abort(), which in turn aborts the application without performing any further cleanup. The active terminate handler is managed using the following functions from <exception>, where std::terminate_handler is a function pointer type and must point to a void function without arguments:
std::terminate_handler std::set_terminate(std::terminate_handler) noexcept
std::terminate_handler std::get_terminate() noexcept
One use case for a custom terminate handler is to automatically generate a process dump when std::terminate() is called. Having a dump file to analyze aids tremendously in tracking down the bug that triggered the process to terminate(). You should consider setting this up for any professional application.

std::unexpected()

The runtime calls std::unexpected() if a dynamic exception specification3 is disregarded: that is, if a function throws something it is not allowed to. Analogous to terminate(), this function calls an std::unexpected_handler function that can be managed using std::set_unexpected() / get_unexpected(). The default handler calls std::terminate().
Note
Both dynamic exception specifications and std::unexpected() have been deprecated and are only mentioned here for completeness.
Footnotes
1
A mixin is a class that provides some functionality to add to other classes (in this case, the capability of storing a pointer to a nested exception and some related functions). In C++, mixins are generally implemented through multiple inheritance.
 
2
std::exp() only sets errno for implementations where math_errhandling defined in <cmath> contains MATH_ERRNO: see Chapter 1. This appears to be mostly the case, though.
 
3
A dynamic exception specification is part of the function declaration and specifies, with a comma-separated list, which exceptions that function is allowed to throw. For example: ReturnType Func(...) throw(exception1, exception2, ...);.
 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset