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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
// 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 program demonstrates the use of leaf::on_error to capture the path an
// error takes as is bubbles up the call stack. The error path-capturing code
// only runs if:
// - An error occurs, and
// - A handler that takes e_error_trace argument is present. Otherwise none of
// the error trace machinery will be invoked by LEAF.
// This example is similar to error_log, except the path the error takes is
// recorded in a std::deque, rather than just printed in-place.
#include <boost/leaf.hpp>
#include <iostream>
#include <deque>
#include <cstdlib>
#define ENABLE_ERROR_TRACE 1
namespace leaf = boost::leaf;
// The error trace is activated only if an error handling scope provides a
// handler for e_error_trace.
struct e_error_trace
{
struct rec
{
char const * file;
int line;
friend std::ostream & operator<<( std::ostream & os, rec const & x )
{
return os << x.file << '(' << x.line << ')' << std::endl;
}
};
std::deque<rec> value;
friend std::ostream & operator<<( std::ostream & os, e_error_trace const & tr )
{
for( auto & i : tr.value )
os << i;
return os;
}
};
// The ERROR_TRACE macro is designed for use in functions that detect or forward
// errors up the call stack. If an error occurs, and if an error handling scope
// provides a handler for e_error_trace, the supplied lambda is executed as the
// error bubbles up.
#define ERROR_TRACE auto _trace = leaf::on_error( []( e_error_trace & tr ) { tr.value.emplace_front(e_error_trace::rec{__FILE__, __LINE__}); } )
// Each function in the sequence below calls the previous function, and each
// function has failure_percent chance of failing. If a failure occurs, the
// ERROR_TRACE macro will cause the path the error takes to be captured in an
// e_error_trace.
int const failure_percent = 25;
leaf::result<void> f1()
{
ERROR_TRACE;
if( (std::rand()%100) > failure_percent )
return { };
else
return leaf::new_error();
}
leaf::result<void> f2()
{
ERROR_TRACE;
if( (std::rand()%100) > failure_percent )
return f1();
else
return leaf::new_error();
}
leaf::result<void> f3()
{
ERROR_TRACE;
if( (std::rand()%100) > failure_percent )
return f2();
else
return leaf::new_error();
}
leaf::result<void> f4()
{
ERROR_TRACE;
if( (std::rand()%100) > failure_percent )
return f3();
else
return leaf::new_error();
}
leaf::result<void> f5()
{
ERROR_TRACE;
if( (std::rand()%100) > failure_percent )
return f4();
else
return leaf::new_error();
}
int main()
{
for( int i=0; i!=10; ++i )
leaf::try_handle_all(
[&]() -> leaf::result<void>
{
std::cout << "Run # " << i << ": ";
BOOST_LEAF_CHECK(f5());
std::cout << "Success!" << std::endl;
return { };
},
#if ENABLE_ERROR_TRACE // This single #if enables or disables the capturing of the error trace.
[]( e_error_trace const & tr )
{
std::cerr << "Error! Trace:" << std::endl << tr;
},
#endif
[]
{
std::cerr << "Error!" << std::endl;
} );
return 0;
}
////////////////////////////////////////
#ifdef BOOST_LEAF_NO_EXCEPTIONS
namespace boost
{
[[noreturn]] void throw_exception( std::exception const & e )
{
std::cerr << "Terminating due to a C++ exception under BOOST_LEAF_NO_EXCEPTIONS: " << e.what();
std::terminate();
}
struct source_location;
[[noreturn]] void throw_exception( std::exception const & e, boost::source_location const & )
{
throw_exception(e);
}
}
#endif
|