2
0
mirror of https://github.com/boostorg/leaf.git synced 2026-01-24 18:02:22 +00:00
Files
leaf/examples/exception_to_result.cpp
2019-02-11 22:10:04 -08:00

116 lines
3.3 KiB
C++

// Copyright (c) 2018-2019 Emil Dotchevski
// Copyright (c) 2018-2019 Second Spectrum, 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 context that is not exception-safe, to be handled in a high level
// function which may or may not be exception-safe.
// An real-world example for this use case is when a C API (which may not throw) is implemented
// using a C++ library that throws exceptions. As demonstrated below, these exception objects are
// intercepted and reported by leaf::result<>.
#include <boost/leaf/capture.hpp>
#include <boost/leaf/result.hpp>
#include <boost/leaf/handle_error.hpp>
#include <boost/leaf/handle_exception.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();
}
}
// Calls compute_answer_throws, switches 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
{
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 handled as regular LEAF error objects...
for( int i=0; i!=42; ++i )
{
leaf::try_handle_all(
[ ]() -> leaf::result<void>
{
LEAF_CHECK(print_answer());
return { };
},
[ ]( error_a const & e )
{
std::cerr << "Error A!" << std::endl;
},
[ ]( error_b const & e )
{
std::cerr << "Error B!" << std::endl;
},
//...except for error_c errors, which (for demonstration) are captured as exceptions
// into std::exception_ptr as "unknown" exception. Presumably this should not
// happen, therefore at this point we treat this situation a logic error: we print
// diagnostic information and bail out.
[ ]( std::exception_ptr const * ep )
{
std::cerr << "Got unknown error!" << std::endl;
// Why do we take std::exception_ptr as a pointer? Because handle_all requires
// that the last handler matches any error -- 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 compile.
if( ep )
leaf::try_catch(
[&]
{
std::rethrow_exception(*ep);
},
[ ]( leaf::error_info const & unmatched )
{
std::cerr << unmatched;
} );
} );
}
return 0;
}