On 2/14/22 00:43, Peter Dimov via Boost wrote:
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
Thank you, Peter.