1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// This example demonstrates how to transport exceptions thrown by a low level
// function through an intermediate scopes that are not exception-safe, to be
// handled in a high level function which may or may not be exception-safe.
#include <boost/leaf.hpp>
#include <iostream>
namespace leaf = boost::leaf;
class error_base: public virtual std::exception { };
class error_a: public virtual error_base { };
class error_b: public virtual error_base { };
class error_c: public virtual error_base { };
// Lower-level library function which throws exceptions.
int compute_answer_throws()
{
switch( rand()%4 )
{
default: return 42;
case 1: throw error_a();
case 2: throw error_b();
case 3: throw error_c();
}
}
// Call compute_answer_throws, switch to result<int> for error handling.
leaf::result<int> compute_answer() noexcept
{
// Convert exceptions of types error_a and error_b to be communicated by
// leaf::result. Any other exception will be communicated as a
// std::exception_ptr.
return leaf::exception_to_result<error_a, error_b>(
[]
{
return compute_answer_throws();
} );
}
// Print the answer if the call to compute_answer is successful.
leaf::result<void> print_answer() noexcept
{
BOOST_LEAF_AUTO( answer, compute_answer());
std::cout << "Answer: " << answer << std::endl;
return { };
}
int main()
{
// Exercise print_answer a few times and handle errors. Note that the
// exception objects that compute_answer_throws throws are not handled as
// exceptions, but as regular LEAF error error objects...
for( int i=0; i!=42; ++i )
{
leaf::try_handle_all(
[]() -> leaf::result<void>
{
BOOST_LEAF_CHECK(print_answer());
return { };
},
[]( error_a const & )
{
std::cerr << "Error A!" << std::endl;
},
[]( error_b const & )
{
std::cerr << "Error B!" << std::endl;
},
// ...except for error_c errors, which (for demonstration) are
// captured as exceptions into std::exception_ptr as "unknown"
// exceptions. Presumably this should not happen, therefore at this
// point we treat this situation as a logic error: we print
// diagnostic information and bail out.
[]( std::exception_ptr const * ep )
{
std::cerr << "Got unknown error!" << std::endl;
// Above, why do we take ep as a pointer? Because handle_all
// requires that the last handler matches any error and, taken
// as a pointer, if there isn't a std::exception_ptr associated
// with the error, the handler will still be matched (with 0
// passed for ep). Had we taken it by value or by const &, the
// program would not have compiled.
if( ep )
leaf::try_catch(
[&]
{
std::rethrow_exception(*ep);
},
[]( leaf::error_info const & unmatched )
{
std::cerr << unmatched;
} );
} );
}
return 0;
}
|