Chapter 56. Boost.Exception
Boost.Exception supports the mechanism from the C++11 standard that transports an exception from one thread to another. boost::exception_ptr
is similar to std::exception_ptr
. However, Boost.Exception isn’t a full replacement for the header file exception
from the standard library. For example, Boost.Exception is missing support for nested exceptions of type std::nested_exception
.
To compile the examples in this chapter with Visual C++ 2013, remove the keyword noexcept
. This version of the Microsoft compiler doesn’t support noexcept
yet.
Example 56.1. Using boost::exception
Example 56.1 calls the function write_lots_of_zeros()
, which in turn calls allocate_memory()
. allocate_memory()
allocates memory dynamically. The function passes std::nothrow
to new
and checks whether the return value is 0. If memory allocation fails, an exception of type allocation_failed
is thrown. allocation_failed
replaces the exception std::bad_alloc
thrown by default if new
fails to allocate memory.
write_lots_of_zeros()
calls allocate_memory()
to try and allocate a memory block with the greatest possible size. This is done with the help of max()
from std::numeric_limits
. The example intentionally tries to allocate that much memory to make the allocation fail.
allocation_failed
is derived from boost::exception
and std::exception
. Deriving the class from std::exception
is not necessary. allocation_failed
could have also been derived from a class from a different class hierarchy in order to embed it in an existing framework. While uses the class hierarchy defined by the standard, deriving solely from boost::exception
would have been sufficient.
If an exception of type allocation_failed
is caught, allocate_memory()
must be the origin of the exception, since it is the only function that throws exceptions of this type. In programs that have many functions calling allocate_memory()
, knowing the type of the exception is no longer sufficient to debug the program effectively. In those cases, it would help to know which function tried to allocate more memory than allocate_memory()
could provide.
With Boost.Exception, data can be added to an exception at any time. You just need to define a type based on boost::error_info
for each bit of data you need to add.
boost::error_info
is a template that expects two parameters. The first parameter is a tag that uniquely identifies the newly created type. This is typically a structure with a unique name. The second parameter refers to the type of the value stored inside the exception. Example 56.1 defines a new type, errmsg_info
– uniquely identifiable via the structure tag_errmsg
– that stores a string of type std::string
.
In the catch
handler of write_lots_of_zeros()
, errmsg_info
is used to create an object that is initialized with the string “writing lots of zeros failed”. This object is then added to the exception of type boost::exception
using operator<<
. Then the exception is re-thrown.
Now, the exception doesn’t just denote a failed memory allocation. It also says that the memory allocation failed when the program tried to write lots of zeros in the function write_lots_of_zeros()
. Knowing which function called allocate_memory()
makes debugging larger programs easier.
To retrieve all available data from an exception, the function boost::diagnostic_information()
can be called in the catch
handler of main()
. boost::diagnostic_information()
calls the member function what()
for each exception passed to it and accesses all of the additional data stored inside the exception. boost::diagnostic_information()
returns a string of type std::string
, which, for example, can be written to standard error.
When compiled with Visual C++ 2013, will display the following message:
The message contains the type of the exception, the error message retrieved from what()
, and the description, including the name of the structure.
The name of the function that threw the exception of type allocation_failed
is unknown.
Boost.Exception provides a macro to throw an exception that contains not only the name of the function, but also additional data such as the file name and the line number.
Example 56.2. More data with BOOST_THROW_EXCEPTION
Using the macro BOOSTTHROWEXCEPTION
instead of throw
, data such as function name, file name, and line number are automatically added to the exception. But this only works if the compiler supports macros for the additional data. While macros such as FILE
and LINE
have been standardized since C++98, the macro __func
, which gets the name of the current function, only became standard with C++11. Because many compilers provided such a macro before C++11, BOOST_THROW_EXCEPTION
tries to identify the underlying compiler and use the corresponding macro if it exists.
Compiled with Visual C++ 2013, Example 56.2 displays the following message:
In , allocation_failed
is no longer derived from boost::exception
. BOOST_THROW_EXCEPTION
accesses the function boost::enable_error_info()
, which identifies whether or not an exception is derived from boost::exception
. If not, it creates a new exception type derived from the specified type and boost::exception
. This is why the message shown above contains a different exception type than allocation_failed
.
Example 56.3. Selectively accessing data with boost::get_error_info()