//Copyright (c) 2018 Emil Dotchevski //Copyright (c) 2018 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 #include #include 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(); } } //A wrapper function that provides a noexcept interface to compute_answer_throws (above), //by transporting different exception types using leaf::result. leaf::result compute_answer() noexcept { //We define special handling for error_a and error_b: exception objects of these types //will be returned directly as LEAF errors... try { return compute_answer_throws(); } catch( error_a const & e ) { return leaf::error(e); } catch( error_b const & e ) { return leaf::error(e); } catch(...) { //...but "unknown" exceptions are still captured and transported using std::exception_ptr. //This is less efficient, because it will likely allocate memory dynamically. return leaf::error(std::current_exception()); } } //Intermediate caller of compute_answer. leaf::result print_answer() noexcept { if( leaf::result r = compute_answer() ) { std::cout << "Answer: " << r.value() << std::endl; return { }; } else return r.error(); } int main() { //Exercise print_answer a few times and handle errors. Note that the exception objects that //compute_answer_throws throws will land in the exp object, rather than arrive as exceptions... for( int i=0; i!=10; ++i ) { leaf::expect exp; if( leaf::result r = print_answer() ) continue; else { leaf::handle_error( exp, r, [ ]( 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, together with any other unknow exception. Presumably this should //never happen, therefore at this point we treat this situation a a logic error: we print //diagnostic information and bail out. [&exp]( std::exception_ptr const & ep ) { assert(ep); try { std::rethrow_exception(ep); } catch(...) { leaf::diagnostic_output_current_exception(std::cerr,exp); } } ); } } return 0; }