This is a quick summary of some 1.78 and 1.79 changes in Assert,
ThrowException, and System related to error reporting.
In Boost 1.73, Assert acquired a new component,
boost::source_location. This is a class encapsulating a source
location (a collection of source file name, line number, column,
and function name) that is modeled after C++20's
std::source_location, except of course it doesn't require C++20.
In 1.79, the macro that creates the current source location,
BOOST_CURRENT_LOCATION, is now usable in a function default
argument, similarly to std::source_location::current(). This
enables functions to be declared like this:
T& my_vector::at( size_t i, boost::source_location const&
loc = BOOST_CURRENT_LOCATION )
{
if( i >= size() )
{
boost::throw_exception( std::out_of_range( "at" ),
loc );
}
return data_[ i ];
}
Since functions such as `at` are called from many places in the
program, it's more useful for the source location in the thrown
exception to point at the call site, rather than inside of `at`
(which doesn't provide much information.)
See for example https://godbolt.org/z/6o9e187a1. Note that the
location returned by diagnostic_information(x) points to line 35,
which is the call to `v.at(5);` inside `main`.
Recommendation one: add source_location default arguments to your
throwing functions so that the information logged at the catch site
points to the call that threw.
In case you find boost::throw_exception too heavyweight, as it
adds a boost::exception base class and support for boost::exception_ptr,
and this would prevent you from applying the technique above, there
is now an alternative function, boost::throw_with_location, in
. It doesn't integrate with Boost.Exception
or boost::exception_ptr, but does add a source location to the exception
that can later be retrieved with boost::get_throw_location(x).
The above example using boost::throw_with_location can be seen at
https://godbolt.org/z/Pcj1xad3v.
In 1.78, boost::system::error_code had also acquired support for source
locations, although it's not as automatic as the above technique where the
default argument automatically adds a location without the user needing to
do anything.
Since there's no space in error_code for the entire boost::source_location
struct, it can only store a pointer to it. This requires the following
incantation
BOOST_STATIC_CONSTEXPR boost::source_location loc = BOOST_CURRENT_LOCATION;
ec.assign( ENOENT, boost::system::generic_category(), &loc );
which can be wrapped in a macro. See the documentation of Boost.System:
https://www.boost.org/doc/libs/1_78_0/libs/system/doc/html/system.html#usage...
Unlike the case with exceptions, when working with error codes, the
situation is reversed and we want to store the source location immediately
after discovering the error, rather than the one of the user call that
failed. That's both because this location gives the most information as
to the source of the error (ENOENT on its own doesn't say much), and
because error codes are generally checked close to the user call, so the
call location isn't as useful.
Recommendation two: add source locations to your error_codes if using
Boost.System.
Finally, when providing dual APIs, that is, APIs that can signal an error
using both error_code or by throwing an exception, you can combine the
above two approaches and store both the "deep" source location in the
error_code and the "shallow" (user call) location in the system_error
exception.
I hope you find those useful.
Further reading:
https://www.boost.org/doc/libs/develop/libs/assert/doc/html/assert.html#sour...
https://www.boost.org/doc/libs/develop/libs/throw_exception/doc/html/throw_e...
https://www.boost.org/doc/libs/develop/libs/system/doc/html/system.html