diff --git a/doc/changes.adoc b/doc/changes.adoc index d3772d8..7d7d9c4 100644 --- a/doc/changes.adoc +++ b/doc/changes.adoc @@ -1,5 +1,5 @@ //// -Copyright 2019 Peter Dimov +Copyright 2019, 2022 Peter Dimov Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt //// @@ -10,6 +10,11 @@ http://www.boost.org/LICENSE_1_0.txt :toc-title: :idprefix: +## Changes in 1.79.0 + +* Added `boost::throw_with_location`, a more lightweight alternative of + `BOOST_THROW_EXCEPTION` for programs that do not use Boost.Exception. + ## Changes in 1.73.0 * Added an overload of `throw_exception` that takes a `boost::source_location` diff --git a/doc/description.adoc b/doc/description.adoc index 262e369..0d1e868 100644 --- a/doc/description.adoc +++ b/doc/description.adoc @@ -1,5 +1,5 @@ //// -Copyright 2019 Peter Dimov +Copyright 2019, 2022 Peter Dimov Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt //// @@ -35,83 +35,12 @@ The macro `BOOST_THROW_EXCEPTION(x)` expands to `::boost::throw_exception(x, BOOST_CURRENT_LOCATION)`, passing the current source location. -[#examples] -# Examples -:toc: -:toc-title: -:idprefix: +When integration with Boost.Exception and `boost::exception_ptr` is not needed, +the function `boost::throw_with_location` can be used instead. It also throws +a user-provided exception, associating it with a supplied or inferred source +location, but does not supply the `boost::exception` base class and does not +enable `boost::exception_ptr` support. -## Using BOOST_THROW_EXCEPTION - -``` -#include -#include -#include -#include - -void f() -{ - BOOST_THROW_EXCEPTION( std::runtime_error( "Unspecified runtime error" ) ); -} - -int main() -{ - try - { - f(); - } - catch( std::exception const & x ) - { - std::cerr << boost::diagnostic_information( x ) << std::endl; - } -} -``` - -## Using boost::throw_exception with a source location - -``` -#include -#include -#include -#include -#include -#include - -void throw_index_error( std::size_t i, std::size_t n, - boost::source_location const & loc ) -{ - std::string msg = "Index out of range: " - + boost::lexical_cast( i ) + " >= " - + boost::lexical_cast( n ); - - boost::throw_exception( std::out_of_range( msg ), loc ); -} - -void f1( std::size_t i, std::size_t n ) -{ - if( i >= n ) - { - throw_index_error( i, n, BOOST_CURRENT_LOCATION ); - } -} - -void f2( std::size_t i, std::size_t n ) -{ - if( i >= n ) - { - throw_index_error( i, n, BOOST_CURRENT_LOCATION ); - } -} - -int main() -{ - try - { - f1( 4, 3 ); - } - catch( std::exception const & x ) - { - std::cerr << boost::diagnostic_information( x ) << std::endl; - } -} -``` +The source location of the exception thrown by `boost::throw_with_location` +can be retrieved, after `catch(std::exception const & x)`, by using +`boost::get_throw_location(x)`. diff --git a/doc/examples.adoc b/doc/examples.adoc new file mode 100644 index 0000000..3902ba8 --- /dev/null +++ b/doc/examples.adoc @@ -0,0 +1,287 @@ +//// +Copyright 2019, 2022 Peter Dimov +Distributed under the Boost Software License, Version 1.0. +http://www.boost.org/LICENSE_1_0.txt +//// + +[#examples] +# Examples +:toc: +:toc-title: +:idprefix: + +## Using BOOST_THROW_EXCEPTION + +Demonstrates the use of `BOOST_THROW_EXCEPTION`. + +``` +#include +#include +#include +#include + +void f() +{ + BOOST_THROW_EXCEPTION( std::runtime_error( "Unspecified runtime error" ) ); +} + +int main() +{ + try + { + f(); + } + catch( std::exception const & x ) + { + std::cerr << boost::diagnostic_information( x ) << std::endl; + } +} +``` + +Sample output: + +```none +example.cpp(8): Throw in function void f() +Dynamic exception type: boost::wrapexcept +std::exception::what: Unspecified runtime error +``` + +## Using boost::throw_exception with a source location + +Demonstrates moving the call to `boost::throw_exception` to a common +helper function that can be marked `BOOST_NOINLINE` to avoid +unnecessary code duplication. The source location is passed +explicitly to the helper function so that it can still record the +logical throw point, instead of always pointing into the helper. + +``` +#include +#include +#include +#include +#include +#include + +BOOST_NORETURN BOOST_NOINLINE +void throw_index_error( std::size_t i, std::size_t n, + boost::source_location const & loc ) +{ + std::string msg = "Index out of range: " + + boost::lexical_cast( i ) + " >= " + + boost::lexical_cast( n ); + + boost::throw_exception( std::out_of_range( msg ), loc ); +} + +void f1( std::size_t i, std::size_t n ) +{ + if( i >= n ) + { + throw_index_error( i, n, BOOST_CURRENT_LOCATION ); + } +} + +void f2( std::size_t i, std::size_t n ) +{ + if( i >= n ) + { + throw_index_error( i, n, BOOST_CURRENT_LOCATION ); + } +} + +int main() +{ + try + { + f1( 0, 3 ); + f2( 4, 3 ); + } + catch( std::exception const & x ) + { + std::cerr << boost::diagnostic_information( x ) << std::endl; + } +} +``` + +Sample output: + +```none +example.cpp(31): Throw in function void f2(std::size_t, std::size_t) +Dynamic exception type: boost::wrapexcept +std::exception::what: Index out of range: 4 >= 3 +``` + +## Using boost::throw_with_location + +This example demonstrates a trivial use of `boost::throw_with_location`. Since +a source location is not supplied, the location of the call to +`boost::throw_with_location` is implicitly captured. + +``` +#include +#include +#include +#include + +void my_terminate_handler(); + +int f1( int x ) +{ + if( x < 0 ) + { + boost::throw_with_location( + std::invalid_argument( "f1: x cannot be negative" ) ); + } + + return x; +} + +int main() +{ + std::set_terminate( my_terminate_handler ); + + return f1( -4 ); +} + +void my_terminate_handler() +{ + std::set_terminate( 0 ); + + try + { + throw; + } + catch( std::exception const& x ) + { + boost::source_location loc = boost::get_throw_location( x ); + std::string type = boost::core::demangle( typeid( x ).name() ); + + fprintf( stderr, + "std::terminate called after throwing an exception:\n" + " type: %s\n" + " what(): %s\n" + " location: %s:%u:%u in function '%s'\n", + + type.c_str(), + x.what(), + loc.file_name(), (unsigned)loc.line(), + (unsigned)loc.column(), loc.function_name() + ); + } + catch( ... ) + { + fputs( "std::terminate called after throwing an unknown exception", stderr ); + } + + std::abort(); +} +``` + +Sample output: + +```none +std::terminate called after throwing an exception: + type: boost::detail::with_throw_location + what(): f1: x cannot be negative + location: :12:9 in function 'f1' +``` + +## Using boost::throw_with_location with an explicit source location + +In this example, the call to `boost::throw_with_location` is moved into +a common helper function. Note how the "API" functions `f1` and `f2` +take a source location argument that defaults to `BOOST_CURRENT_LOCATION`. +This allows the source location attached to the exception to point at +the location of the call to `f2`, rather than inside of `f2`. + +Since `f2` is typically called many times, this is usually what we want, +because it enables us to identify the throwing call, rather than merely +to know that it was `f2` that threw. + +``` +#include +#include +#include +#include + +void my_terminate_handler(); + +BOOST_NORETURN BOOST_NOINLINE +void throw_invalid_argument( char const * msg, + boost::source_location const & loc ) +{ + boost::throw_with_location( std::invalid_argument( msg ), loc ); +} + +int f1( int x, + boost::source_location const & loc = BOOST_CURRENT_LOCATION ) +{ + if( x < 0 ) + { + throw_invalid_argument( "f1: x cannot be negative", loc ); + } + + return x; +} + +int f2( int x, + boost::source_location const & loc = BOOST_CURRENT_LOCATION ) +{ + if( x < 0 ) + { + throw_invalid_argument( "f2: x cannot be negative", loc ); + } + + return x; +} + +int main() +{ + std::set_terminate( my_terminate_handler ); + + return f1( 3 ) + f2( -11 ); +} + +void my_terminate_handler() +{ + std::set_terminate( 0 ); + + try + { + throw; + } + catch( std::exception const& x ) + { + boost::source_location loc = boost::get_throw_location( x ); + std::string type = boost::core::demangle( typeid( x ).name() ); + + fprintf( stderr, + "std::terminate called after throwing an exception:\n" + " type: %s\n" + " what(): %s\n" + " location: %s:%u:%u in function '%s'\n", + + type.c_str(), + x.what(), + loc.file_name(), (unsigned)loc.line(), + (unsigned)loc.column(), loc.function_name() + ); + } + catch( ... ) + { + fputs( "std::terminate called after throwing an unknown exception", + stderr ); + } + + std::abort(); +} +``` + +Sample output: + +```none +std::terminate called after throwing an exception: + type: boost::detail::with_throw_location + what(): f2: x cannot be negative + location: :41:22 in function 'main' +``` diff --git a/doc/index.adoc b/doc/index.adoc index 09144ff..325e379 100644 --- a/doc/index.adoc +++ b/doc/index.adoc @@ -1,5 +1,5 @@ //// -Copyright 2017, 2019 Peter Dimov +Copyright 2017, 2019, 2022 Peter Dimov Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt //// @@ -15,6 +15,7 @@ Peter Dimov, Emil Dotchevski :leveloffset: +1 include::description.adoc[] +include::examples.adoc[] include::changes.adoc[] include::reference.adoc[] @@ -25,5 +26,5 @@ include::reference.adoc[] This documentation is -* Copyright 2019 Peter Dimov +* Copyright 2019, 2022 Peter Dimov * Distributed under the http://www.boost.org/LICENSE_1_0.txt[Boost Software License, Version 1.0]. diff --git a/doc/reference.adoc b/doc/reference.adoc index 327c555..848fe5c 100644 --- a/doc/reference.adoc +++ b/doc/reference.adoc @@ -41,6 +41,16 @@ template BOOST_NORETURN void throw_exception( E const & e, #define BOOST_THROW_EXCEPTION(x) \ ::boost::throw_exception(x, BOOST_CURRENT_LOCATION) + +namespace boost +{ + +template BOOST_NORETURN void throw_with_location( E && e, + boost::source_location const & loc = BOOST_CURRENT_LOCATION ); + +template boost::source_location get_throw_location( E const & e ); + +} // namespace boost ``` ## throw_exception @@ -96,3 +106,31 @@ Effects: :: it, and containing the necessary support for `boost::exception_ptr`. The `boost::exception` base class is initialized to contain the source location `loc`. + +## throw_with_location + +``` +template BOOST_NORETURN void throw_with_location( E && e, + boost::source_location const & loc = BOOST_CURRENT_LOCATION ); +``` + +Requires: :: `std::decay::type` must have `std::exception` as a public + and unambiguous base class. + +Effects: :: + * When exceptions aren't available, `boost::throw_exception( e, loc );` + * Otherwise, the function throws an object of a type derived from `E`, + such that, if this object `x` is caught as `std::exception` or `E`, + `boost::get_throw_location( x )` would return `loc`. + +## get_throw_location + +``` +template boost::source_location get_throw_location( E const & e ); +``` + +Requires: :: `E` must be polymorphic. +Effects: :: If `e` is a subobject of the object thrown by + `boost::throw_with_location( x, loc )`, returns `loc`. Otherwise, returns + a default constructed source location. +