// Copyright (c) 2018-2019 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::accumulate to print the path an error takes // as is bubbles up the call stack. The printing code only runs if: // - An error occurs, and // - A handler that takes e_error_log argument is present. // Otherwise none of the error log machinery will be invoked by LEAF. // This example is similar to error_trace, except the path the error takes is not captured, // only printed. #include #include #include #include #include #define ENABLE_ERROR_LOG 1 namespace leaf = boost::leaf; // The error log is activated only if an error-handling scope provides a handler for e_error_log. // This activation logic applies to any type passed to leaf::accumulate. struct e_error_log { struct rec { char const * file; int line; friend std::ostream & operator<<( std::ostream & os, rec const & x ) { return os << x.file << '(' << x.line << ')'; } }; e_error_log() { std::cerr << "Error! Log:" << std::endl; } // Our e_error_log instance is stateless, used only as a target to operator<<. template friend std::ostream & operator<<( e_error_log const &, T const & x ) { return std::cerr << x << std::endl; } }; // Specializing leaf::is_e_type for e_error_log enables its use with LEAF. namespace boost { namespace leaf { template <> struct is_e_type: std::true_type { }; } } // The ERROR_LOG 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_log, the supplied lambda is executed as the error bubbles up. #define ERROR_LOG auto _log = leaf::accumulate( []( e_error_log & log ) { log << e_error_log::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_LOG macro will cause // the path the error takes to be printed. int const failure_percent = 25; leaf::result f1() { ERROR_LOG; if( (std::rand()%100) > failure_percent ) return { }; else return leaf::new_error(); } leaf::result f2() { ERROR_LOG; if( (std::rand()%100) > failure_percent ) return f1(); else return leaf::new_error(); } leaf::result f3() { ERROR_LOG; if( (std::rand()%100) > failure_percent ) return f2(); else return leaf::new_error(); } leaf::result f4() { ERROR_LOG; if( (std::rand()%100) > failure_percent ) return f3(); else return leaf::new_error(); } leaf::result f5() { ERROR_LOG; 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 { std::cout << "Run # " << i << ": "; LEAF_CHECK(f5()); std::cout << "Success!" << std::endl; return { }; }, #if ENABLE_ERROR_LOG // This single #if enables or disables the printing of the error log. []( e_error_log const & ) { }, #endif [] { std::cerr << "Error!" << std::endl; } ); return 0; }