From 484ca0c55e34f0b7a059ac4813e63af4f34782cf Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Thu, 3 Jan 2019 13:46:38 -0800 Subject: [PATCH 01/34] decoupled diagnostic_output from expect<> --- example/print_file_eh.cpp | 2 +- example/print_file_result.cpp | 2 +- example/print_half.cpp | 5 +- example/return_exception.cpp | 4 +- include/boost/leaf/common.hpp | 2 +- include/boost/leaf/detail/optional.hpp | 17 ++-- include/boost/leaf/diagnostic_output.hpp | 53 +++++++++++ .../diagnostic_output_current_exception.hpp | 7 +- include/boost/leaf/error.hpp | 91 +++++++++++++++++-- include/boost/leaf/error_capture.hpp | 1 - include/boost/leaf/expect.hpp | 69 -------------- include/boost/leaf/result.hpp | 47 +++------- test/diagnostic_output_test.cpp | 4 +- 13 files changed, 173 insertions(+), 131 deletions(-) create mode 100644 include/boost/leaf/diagnostic_output.hpp diff --git a/example/print_file_eh.cpp b/example/print_file_eh.cpp index 75aeb7e..2313c1b 100644 --- a/example/print_file_eh.cpp +++ b/example/print_file_eh.cpp @@ -179,7 +179,7 @@ int main( int argc, char const * argv[ ] ) { //This catch-all case helps diagnose logic errors (presumably, missing catch). std::cerr << "Unknown error, cryptic information follows." << std::endl; - leaf::diagnostic_output_current_exception(std::cerr,exp); + leaf::diagnostic_output_current_exception(std::cerr); return 5; } } diff --git a/example/print_file_result.cpp b/example/print_file_result.cpp index 76832d1..1681305 100644 --- a/example/print_file_result.cpp +++ b/example/print_file_result.cpp @@ -199,7 +199,7 @@ int main( int argc, char const * argv[ ] ) default: { std::cerr << "Unknown error code " << ec << ", cryptic information follows." << std::endl; //<7> - leaf::diagnostic_output(std::cerr,exp,r); + leaf::diagnostic_output(std::cerr,r); return 5; } } diff --git a/example/print_half.cpp b/example/print_half.cpp index 2bc2496..f0b1b22 100644 --- a/example/print_half.cpp +++ b/example/print_half.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -97,13 +98,13 @@ int main( int argc, char const * argv[ ] ) } return 1; }, - [&exp,&r] + [&r] { //This will never execute in this program, but it would detect logic errors where an unknown error reaches main. //In this case, we print diagnostic information. Consider using leaf::unexpected_diagnostic_output in the //definition of exp. std::cerr << "Unknown error, cryptic diagnostic information follows." << std::endl; - leaf::diagnostic_output(std::cerr,exp,r); + leaf::diagnostic_output(std::cerr,r); return 2; } ); } diff --git a/example/return_exception.cpp b/example/return_exception.cpp index ae5d60f..657fa06 100644 --- a/example/return_exception.cpp +++ b/example/return_exception.cpp @@ -100,7 +100,7 @@ int main() //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 ) + [ ]( std::exception_ptr const & ep ) { assert(ep); try @@ -109,7 +109,7 @@ int main() } catch(...) { - leaf::diagnostic_output_current_exception(std::cerr,exp); + leaf::diagnostic_output_current_exception(std::cerr); } } diff --git a/include/boost/leaf/common.hpp b/include/boost/leaf/common.hpp index 50b4afe..c7c504b 100644 --- a/include/boost/leaf/common.hpp +++ b/include/boost/leaf/common.hpp @@ -9,11 +9,11 @@ #include #include -#include #include #include #ifdef _WIN32 #include +#include #ifdef min #undef min #endif diff --git a/include/boost/leaf/detail/optional.hpp b/include/boost/leaf/detail/optional.hpp index 4508c54..ef7ffe7 100644 --- a/include/boost/leaf/detail/optional.hpp +++ b/include/boost/leaf/detail/optional.hpp @@ -105,32 +105,37 @@ namespace boost { namespace leaf { return value_; } - bool has_value() const noexcept + T const * has_value() const noexcept { - return has_value_; + return has_value_ ? &value_ : 0; + } + + T * has_value() noexcept + { + return has_value_ ? &value_ : 0; } T const & value() const & noexcept { - assert(has_value()); + assert(has_value()!=0); return value_; } T & value() & noexcept { - assert(has_value()); + assert(has_value()!=0); return value_; } T const && value() const && noexcept { - assert(has_value()); + assert(has_value()!=0); return value_; } T value() && noexcept { - assert(has_value()); + assert(has_value()!=0); T tmp(std::move(value_)); reset(); return tmp; diff --git a/include/boost/leaf/diagnostic_output.hpp b/include/boost/leaf/diagnostic_output.hpp new file mode 100644 index 0000000..eb3b75b --- /dev/null +++ b/include/boost/leaf/diagnostic_output.hpp @@ -0,0 +1,53 @@ +#ifndef BOOST_LEAF_7062AB340F9411E9A7CFDBC88C7F4AFC +#define BOOST_LEAF_7062AB340F9411E9A7CFDBC88C7F4AFC + +//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) + +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + inline void slot_base::diagnostic_output( std::ostream & os, error const * e ) + { + for( slot_base const * p = first(); p; p=p->next_ ) + if( p->slot_diagnostic_output(os,e) ) + os << std::endl; + } + } + + inline void diagnostic_output( std::ostream & os, error const & e ) + { + os << "leaf::error serial number: " << e << std::endl; + leaf_detail::slot_base::diagnostic_output(os,&e); + } + + inline void diagnostic_output( std::ostream & os ) + { + leaf_detail::slot_base::diagnostic_output(os,0); + } + + template + class result; + + template + void diagnostic_output( std::ostream & os, result const & r ) + { + assert(!r); + if( r.which_==leaf_detail::result_variant::err ) + return diagnostic_output(os,r.err_); + else + { + assert(r.which_==leaf_detail::result_variant::cap); + return diagnostic_output(os,r.cap_); + } + } + +} } + +#endif diff --git a/include/boost/leaf/diagnostic_output_current_exception.hpp b/include/boost/leaf/diagnostic_output_current_exception.hpp index a863c7e..d2097cd 100644 --- a/include/boost/leaf/diagnostic_output_current_exception.hpp +++ b/include/boost/leaf/diagnostic_output_current_exception.hpp @@ -8,12 +8,13 @@ //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include #include namespace boost { namespace leaf { template - void diagnostic_output_current_exception( std::ostream & os, expect const & exp ) + void diagnostic_output_current_exception( std::ostream & os ) { os << "Current Exception Diagnostic Information:" << std::endl; @@ -42,11 +43,11 @@ namespace boost { namespace leaf { } catch( error const & e ) { - diagnostic_output(os,exp,e); + diagnostic_output(os,e); } catch( ... ) { - diagnostic_output(os,exp); + diagnostic_output(os); } } diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 406e102..7a5cb66 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -55,11 +55,25 @@ namespace boost { namespace leaf { { std::string value; std::set already; - friend std::ostream & operator<<( std::ostream & os, e_unexpected_diagnostic_output const & x ) noexcept { return os; } }; namespace leaf_detail { + template <> + struct diagnostic + { + static bool print( std::ostream & os, e_unexpected_diagnostic_output const & x ) noexcept + { + if( x.value.empty() ) + return false; + else + { + os << x.value; + return true; + } + } + }; + template struct has_data_member_value { @@ -216,8 +230,44 @@ namespace boost { namespace leaf { return c; } + class slot_base + { + slot_base( slot_base const & ) = delete; + slot_base & operator=( slot_base const & ) = delete; + + virtual bool slot_diagnostic_output( std::ostream &, error const * e ) const = 0; + + public: + + static void diagnostic_output( std::ostream & os, error const * e ); + + protected: + + static slot_base const * & first() noexcept + { + static thread_local slot_base const * p = 0; + return p; + } + + slot_base const * const next_; + + slot_base() noexcept: + next_(first()) + { + first() = this; + } + + ~slot_base() noexcept + { + slot_base const * & p = first(); + assert(p==this); + p = next_; + } + }; + template class slot: + slot_base, optional> { slot( slot const & ) = delete; @@ -225,10 +275,16 @@ namespace boost { namespace leaf { typedef optional> base; slot * prev_; static_assert(is_error_type::value,"Not an error type"); + + bool slot_diagnostic_output( std::ostream & os, error const * e ) const; + protected: + slot() noexcept; ~slot() noexcept; + public: + using base::put; using base::has_value; using base::value; @@ -336,6 +392,27 @@ namespace boost { namespace leaf { tl_slot_ptr() = prev_; } + template + bool slot::slot_diagnostic_output( std::ostream & os, error const * e ) const + { + if( tl_slot_ptr()==this ) + if( error_info const * ev = has_value() ) + if( e ) + { + if( ev->e==*e ) + return diagnosticv)>::print(os,ev->v); + } + else + { + if( diagnosticv)>::print(os,ev->v) ) + { + os << " {" << ev->e << '}'; + return true; + } + } + return false; + } + template error make_error( char const * file, int line, char const * function, E && ... e ) { @@ -346,14 +423,12 @@ namespace boost { namespace leaf { return error( std::move(sl), std::forward(e)... ); } - inline void diagnostic_output_prefix( std::ostream & os, leaf::error const * e ) + enum class result_variant { - if( e ) - os << "leaf::error serial number: " << *e << std::endl; - if( leaf_detail::slot const * unx = leaf_detail::tl_slot_ptr() ) - if( unx->has_value() ) - os << unx->value().v.value << std::endl; - } + value, + err, + cap + }; } //leaf_detail template diff --git a/include/boost/leaf/error_capture.hpp b/include/boost/leaf/error_capture.hpp index 2483f5b..d652a85 100644 --- a/include/boost/leaf/error_capture.hpp +++ b/include/boost/leaf/error_capture.hpp @@ -163,7 +163,6 @@ namespace boost { namespace leaf { void diagnostic_output_( std::ostream & os ) const { - leaf_detail::diagnostic_output_prefix(os,0); leaf_detail::tuple_for_each_capture::print(os,s_); } diff --git a/include/boost/leaf/expect.hpp b/include/boost/leaf/expect.hpp index 9b2b7fa..f5f049f 100644 --- a/include/boost/leaf/expect.hpp +++ b/include/boost/leaf/expect.hpp @@ -98,50 +98,6 @@ namespace boost { namespace leaf { //////////////////////////////////////// - template - struct tuple_for_each_expect - { - static void print( std::ostream & os, Tuple const & tup ) - { - tuple_for_each_expect::print(os,tup); - auto & opt = std::get(tup); - if( opt.has_value() ) - { - auto & x = opt.value(); - if( diagnostic::print(os,x.v) ) - os << " {" << x.e << '}' << std::endl; - } - } - - static void print( std::ostream & os, Tuple const & tup, error const & e ) - { - tuple_for_each_expect::print(os,tup,e); - auto & opt = std::get(tup); - if( opt.has_value() ) - { - auto & x = opt.value(); - if( x.e==e && diagnostic::print(os,x.v) ) - os << std::endl; - } - } - - static void clear( Tuple & tup ) noexcept - { - tuple_for_each_expect::clear(tup); - std::get(tup).reset(); - } - }; - - template - struct tuple_for_each_expect<0, Tuple> - { - static void print( std::ostream &, Tuple const & ) noexcept { } - static void print( std::ostream &, Tuple const &, error const & ) noexcept { } - static void clear( Tuple & ) noexcept { } - }; - - //////////////////////////////////////// - template optional convert_optional( expect_slot && x, error const & e ) noexcept { @@ -167,11 +123,6 @@ namespace boost { namespace leaf { template P const * peek( expect const &, error const & ) noexcept; - template - void diagnostic_output( std::ostream &, expect const & ); - - template - void diagnostic_output( std::ostream &, expect const &, error const & ); template typename leaf_detail::dependent_type>::error_capture capture( expect &, error const & ); @@ -187,12 +138,6 @@ namespace boost { namespace leaf { template friend P const * leaf::peek( expect const &, error const & ) noexcept; - template - friend void leaf::diagnostic_output( std::ostream &, expect const & ); - - template - friend void leaf::diagnostic_output( std::ostream &, expect const &, error const & ); - template friend typename leaf_detail::dependent_type>::error_capture leaf::capture( expect &, error const & ); @@ -255,20 +200,6 @@ namespace boost { namespace leaf { return 0; } - template - void diagnostic_output( std::ostream & os, expect const & exp ) - { - leaf_detail::diagnostic_output_prefix(os,0); - leaf_detail::tuple_for_each_expect::print(os,exp.s_); - } - - template - void diagnostic_output( std::ostream & os, expect const & exp, error const & e ) - { - leaf_detail::diagnostic_output_prefix(os,&e); - leaf_detail::tuple_for_each_expect::print(os,exp.s_,e); - } - template typename leaf_detail::dependent_type>::error_capture capture( expect & exp, error const & e ) { diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index 3120714..a84af36 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -38,24 +38,14 @@ namespace boost { namespace leaf { template P const * peek( expect const &, result const & ) noexcept; - template - void diagnostic_output( std::ostream &, expect const &, result const & ); + template + void diagnostic_output( std::ostream &, result const & ); template result capture( expect &, result const & ); //////////////////////////////////////// - namespace leaf_detail - { - enum class result_variant - { - value, - err, - cap - }; - } - template class result { @@ -65,8 +55,8 @@ namespace boost { namespace leaf { template friend P const * leaf::peek( expect const &, result const & ) noexcept; - template - friend void leaf::diagnostic_output( std::ostream &, expect const &, result const & ); + template + friend void leaf::diagnostic_output( std::ostream &, result const & ); template friend result leaf::capture( expect &, result const & ); @@ -254,12 +244,12 @@ namespace boost { namespace leaf { case leaf_detail::result_variant::value: return leaf::error(std::forward(e)...); case leaf_detail::result_variant::cap: - { - error_capture cap = cap_; - destroy(); - (void) new(&err_) leaf::error(cap.unload()); - which_ = leaf_detail::result_variant::err; - } + { + error_capture cap = cap_; + destroy(); + (void) new(&err_) leaf::error(cap.unload()); + which_ = leaf_detail::result_variant::err; + } default: assert(which_==leaf_detail::result_variant::err); return err_.propagate(std::forward(e)...); @@ -279,8 +269,8 @@ namespace boost { namespace leaf { template friend P const * leaf::peek( expect const &, result const & ) noexcept; - template - friend void leaf::diagnostic_output( std::ostream &, expect const &, result const & ); + template + friend void leaf::diagnostic_output( std::ostream &, result const & ); template friend result leaf::capture( expect &, result const & ); @@ -350,19 +340,6 @@ namespace boost { namespace leaf { } } - template - void diagnostic_output( std::ostream & os, expect const & exp, result const & r ) - { - assert(!r); - if( r.which_==leaf_detail::result_variant::err ) - return diagnostic_output(os,exp,r.err_); - else - { - assert(r.which_==leaf_detail::result_variant::cap); - return diagnostic_output(os,r.cap_); - } - } - template result capture( expect & exp, result const & r ) { diff --git a/test/diagnostic_output_test.cpp b/test/diagnostic_output_test.cpp index 866b347..e697d74 100644 --- a/test/diagnostic_output_test.cpp +++ b/test/diagnostic_output_test.cpp @@ -99,7 +99,7 @@ int main() catch( my_error & e ) { std::ostringstream st; - diagnostic_output_current_exception(st,exp); + leaf::diagnostic_output_current_exception(st); std::string s = st.str(); BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); BOOST_TEST(s.find(": N/A")!=s.npos); @@ -108,7 +108,7 @@ int main() BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); BOOST_TEST(s.find(") in function")!=s.npos); BOOST_TEST(s.find("Detected 2 attempts to communicate unexpected error objects, the first one of type ")!=s.npos); - BOOST_TEST(s.find("unexpected_test")!=s.npos); + BOOST_TEST(s.find("unexpected_test<2>")!=s.npos); std::cout << s; handle_exception( exp, e, [ ]{ } ); } From e032a0b4f7ffd31306618c711a0d64f61c3c93b9 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Thu, 3 Jan 2019 17:25:09 -0800 Subject: [PATCH 02/34] member handle_error --- .travis.yml | 1 + example/capture_eh.cpp | 2 +- example/capture_result.cpp | 2 +- example/lua_callback_result.cpp | 2 +- example/print_file_eh.cpp | 6 +- example/print_file_result.cpp | 10 +- example/print_half.cpp | 7 +- example/return_exception.cpp | 2 +- include/boost/leaf/diagnostic_output.hpp | 53 ----------- .../diagnostic_output_current_exception.hpp | 9 +- include/boost/leaf/error.hpp | 29 ++++-- include/boost/leaf/error_capture.hpp | 74 ++++++--------- include/boost/leaf/exception.hpp | 19 ++-- include/boost/leaf/exception_capture.hpp | 4 +- include/boost/leaf/expect.hpp | 95 ++++++++++++------- include/boost/leaf/result.hpp | 74 ++++----------- test/basic_test.cpp | 36 +++---- test/defer_test.1.cpp | 2 +- test/defer_test.2.cpp | 4 +- test/defer_test.3.cpp | 2 +- test/defer_test.4.cpp | 2 +- test/defer_test.5.cpp | 2 +- test/defer_test.6.cpp | 2 +- test/defer_test.7.cpp | 2 +- test/defer_test.8.cpp | 2 +- test/diagnostic_output_test.cpp | 2 +- test/error_capture_test.1.cpp | 16 ++-- test/error_capture_test.2.cpp | 22 ++--- test/error_test.cpp | 12 +-- test/exception_capture_test.cpp | 2 +- test/exception_test.1.cpp | 8 +- test/exception_test.2.cpp | 8 +- test/expect_fail_test.1.cpp | 2 +- test/expect_fail_test.2.cpp | 2 +- test/expect_test.1.cpp | 2 +- test/expect_test.2.cpp | 2 +- test/expect_test.3.cpp | 2 +- test/expect_test.4.cpp | 18 ++-- test/expect_test.5.cpp | 12 +-- test/multiple_errors_test.cpp | 4 +- test/preload_test.1.cpp | 4 +- test/preload_test.2.cpp | 2 +- test/preload_test.3.cpp | 2 +- test/preload_test.4.cpp | 2 +- test/preload_test.5.cpp | 2 +- test/preload_test.6.cpp | 2 +- test/preload_test.7.cpp | 2 +- test/result_capture_test.cpp | 2 +- test/result_test.1.cpp | 8 +- test/result_test.2.cpp | 32 +++---- test/result_test.3.cpp | 2 +- test/result_test.4.cpp | 2 +- test/result_test.5.cpp | 22 ++--- test/result_test.6.cpp | 18 ++-- test/result_void_capture_test.cpp | 2 +- 55 files changed, 303 insertions(+), 358 deletions(-) delete mode 100644 include/boost/leaf/diagnostic_output.hpp diff --git a/.travis.yml b/.travis.yml index f1acb82..edcbee9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ branches: only: - master - develop + - feature/* env: matrix: diff --git a/example/capture_eh.cpp b/example/capture_eh.cpp index a778602..725b4ce 100644 --- a/example/capture_eh.cpp +++ b/example/capture_eh.cpp @@ -91,7 +91,7 @@ int main() catch( failure const & e ) { //Failure! Handle the error, print failure info. - handle_exception( exp, e, + exp.handle_exception( e, [ ] ( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid ) { std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; diff --git a/example/capture_result.cpp b/example/capture_result.cpp index 0fabf15..f8a555e 100644 --- a/example/capture_result.cpp +++ b/example/capture_result.cpp @@ -81,7 +81,7 @@ int main() else { //Failure! Handle error, print failure info. - bool matched = handle_error( exp, r, + bool matched = exp.handle_error( r, [ ] ( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid ) { std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index 3c63fdb..b0042b1 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -119,7 +119,7 @@ int main() noexcept std::cout << "do_work succeeded, answer=" << *r << '\n'; else { - bool matched = handle_error( exp, r, + bool matched = exp.handle_error( r, //Handle e_do_work failures: [ ]( do_work_error_code e ) diff --git a/example/print_file_eh.cpp b/example/print_file_eh.cpp index 2313c1b..1a5e25e 100644 --- a/example/print_file_eh.cpp +++ b/example/print_file_eh.cpp @@ -123,7 +123,7 @@ int main( int argc, char const * argv[ ] ) //are associated with the error value stored in e. If no function can be matched, //handle_exception returns false. Otherwise the matched function is invoked with //the corresponding available error objects. - leaf::handle_exception( exp, e, + exp.handle_exception( e, [ ] ( e_file_name const & fn, e_errno const & errn ) { @@ -142,7 +142,7 @@ int main( int argc, char const * argv[ ] ) //e_file_name and e_errno, associated with e, are avialable in exp; if not, it will //next check if just e_errno is available; and if not, the last function (which //takes no arguments) will always match to print a generic error message. - leaf::handle_exception( exp, e, + exp.handle_exception( e, [ ] ( e_file_name const & fn, e_errno const & errn ) { @@ -165,7 +165,7 @@ int main( int argc, char const * argv[ ] ) catch( std::ostream::failure const & e ) { //Report failure to write to std::cout, print the relevant errno. - leaf::handle_exception( exp, e, + exp.handle_exception( e, [ ] ( e_errno const & errn ) { diff --git a/example/print_file_result.cpp b/example/print_file_result.cpp index 1681305..1948072 100644 --- a/example/print_file_result.cpp +++ b/example/print_file_result.cpp @@ -125,7 +125,7 @@ int main( int argc, char const * argv[ ] ) else { //Probe exp for the error_code object associated with the error stored in r. - switch( auto ec = *leaf::peek(exp,r) ) + switch( auto ec = *exp.peek(r) ) { case input_file_open_error: { @@ -134,7 +134,7 @@ int main( int argc, char const * argv[ ] ) //are associated with the error value stored in r. If no function can be matched, //handle_error returns false. Otherwise the matched function is invoked with //the matching corresponding error objects. - bool matched = leaf::handle_error( exp, r, + bool matched = exp.handle_error( r, [ ] ( e_file_name const & fn, e_errno const & errn ) { @@ -157,7 +157,7 @@ int main( int argc, char const * argv[ ] ) //e_file_name and e_errno, associated with r, are avialable in exp; if not, it will //next check if just e_errno is available; and if not, the last function (which //takes no arguments) will always match to print a generic error message. - bool matched = leaf::handle_error( exp, r, + bool matched = exp.handle_error( r, [ ] ( e_file_name const & fn, e_errno const & errn ) { @@ -182,7 +182,7 @@ int main( int argc, char const * argv[ ] ) case cout_error: { //Report failure to write to std::cout, print the relevant errno. - bool matched = leaf::handle_error( exp, r, + bool matched = exp.handle_error( r, [ ] ( e_errno const & errn ) { @@ -199,7 +199,7 @@ int main( int argc, char const * argv[ ] ) default: { std::cerr << "Unknown error code " << ec << ", cryptic information follows." << std::endl; //<7> - leaf::diagnostic_output(std::cerr,r); + r.diagnostic_output(std::cerr); return 5; } } diff --git a/example/print_half.cpp b/example/print_half.cpp index f0b1b22..201d293 100644 --- a/example/print_half.cpp +++ b/example/print_half.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -62,7 +61,7 @@ leaf::result print_half(const std::string& text) } else { - return leaf::handle_error( exp, r, + return exp.handle_error( r, [&]( ConversionErrc ec ) -> leaf::result { if( ec != ConversionErrc::TooLong ) @@ -84,7 +83,7 @@ int main( int argc, char const * argv[ ] ) } else { - return handle_error( exp, r, + return exp.handle_error( r, [ ]( ConversionErrc ec ) { switch(ec) @@ -104,7 +103,7 @@ int main( int argc, char const * argv[ ] ) //In this case, we print diagnostic information. Consider using leaf::unexpected_diagnostic_output in the //definition of exp. std::cerr << "Unknown error, cryptic diagnostic information follows." << std::endl; - leaf::diagnostic_output(std::cerr,r); + r.diagnostic_output(std::cerr); return 2; } ); } diff --git a/example/return_exception.cpp b/example/return_exception.cpp index 657fa06..88e3184 100644 --- a/example/return_exception.cpp +++ b/example/return_exception.cpp @@ -84,7 +84,7 @@ int main() continue; else { - leaf::handle_error( exp, r, + exp.handle_error( r, [ ]( error_a const & e ) { diff --git a/include/boost/leaf/diagnostic_output.hpp b/include/boost/leaf/diagnostic_output.hpp deleted file mode 100644 index eb3b75b..0000000 --- a/include/boost/leaf/diagnostic_output.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef BOOST_LEAF_7062AB340F9411E9A7CFDBC88C7F4AFC -#define BOOST_LEAF_7062AB340F9411E9A7CFDBC88C7F4AFC - -//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) - -#include - -namespace boost { namespace leaf { - - namespace leaf_detail - { - inline void slot_base::diagnostic_output( std::ostream & os, error const * e ) - { - for( slot_base const * p = first(); p; p=p->next_ ) - if( p->slot_diagnostic_output(os,e) ) - os << std::endl; - } - } - - inline void diagnostic_output( std::ostream & os, error const & e ) - { - os << "leaf::error serial number: " << e << std::endl; - leaf_detail::slot_base::diagnostic_output(os,&e); - } - - inline void diagnostic_output( std::ostream & os ) - { - leaf_detail::slot_base::diagnostic_output(os,0); - } - - template - class result; - - template - void diagnostic_output( std::ostream & os, result const & r ) - { - assert(!r); - if( r.which_==leaf_detail::result_variant::err ) - return diagnostic_output(os,r.err_); - else - { - assert(r.which_==leaf_detail::result_variant::cap); - return diagnostic_output(os,r.cap_); - } - } - -} } - -#endif diff --git a/include/boost/leaf/diagnostic_output_current_exception.hpp b/include/boost/leaf/diagnostic_output_current_exception.hpp index d2097cd..3ea08ca 100644 --- a/include/boost/leaf/diagnostic_output_current_exception.hpp +++ b/include/boost/leaf/diagnostic_output_current_exception.hpp @@ -8,7 +8,6 @@ //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include -#include #include namespace boost { namespace leaf { @@ -37,17 +36,17 @@ namespace boost { namespace leaf { { throw; } - catch( leaf_detail::captured_exception const & e ) + catch( leaf_detail::captured_exception const & ce ) { - diagnostic_output_(os,e); + ce.diagnostic_output(os); } catch( error const & e ) { - diagnostic_output(os,e); + e.diagnostic_output(os); } catch( ... ) { - diagnostic_output(os); + global_diagnostic_output(os); } } diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 7a5cb66..a9b398f 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -188,19 +188,21 @@ namespace boost { namespace leaf { return e1.id_!=e2.id_; } + error propagate() const noexcept + { + return *this; + } + friend std::ostream & operator<<( std::ostream & os, error const & e ) { os << e.id_; return os; } - error propagate() const noexcept - { - return *this; - } - template error propagate( E && ... ) const noexcept; + + void diagnostic_output( std::ostream & os ) const; }; inline error next_error_value() noexcept @@ -239,7 +241,12 @@ namespace boost { namespace leaf { public: - static void diagnostic_output( std::ostream & os, error const * e ); + static void diagnostic_output( std::ostream & os, error const * e ) + { + for( slot_base const * p = first(); p; p=p->next_ ) + if( p->slot_diagnostic_output(os,e) ) + os << std::endl; + } protected: @@ -439,6 +446,16 @@ namespace boost { namespace leaf { return *this; } + inline void error::diagnostic_output( std::ostream & os ) const + { + os << "leaf::error serial number: " << *this << std::endl; + leaf_detail::slot_base::diagnostic_output(os,this); + } + + inline void global_diagnostic_output( std::ostream & os ) + { + leaf_detail::slot_base::diagnostic_output(os,0); + } } } #endif diff --git a/include/boost/leaf/error_capture.hpp b/include/boost/leaf/error_capture.hpp index d652a85..e61d084 100644 --- a/include/boost/leaf/error_capture.hpp +++ b/include/boost/leaf/error_capture.hpp @@ -13,14 +13,6 @@ namespace boost { namespace leaf { class error_capture; - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( error_capture const &, F && ... ) noexcept; - - template - P const * peek( error_capture const & ) noexcept; - - void diagnostic_output( std::ostream &, error_capture const & ); - namespace leaf_detail { template @@ -29,10 +21,7 @@ namespace boost { namespace leaf { template struct all_available { - static bool check( error_capture const & cap ) noexcept - { - return peek(cap) && all_available::check(cap); - } + static bool check( error_capture const & cap ) noexcept; }; template <> @@ -87,16 +76,6 @@ namespace boost { namespace leaf { class error_capture { - template - friend typename leaf_detail::handler_pack_return_type::return_type handle_error( error_capture const &, F && ... ) noexcept; - - template - friend P const * leaf::peek( error_capture const & ) noexcept; - - friend void leaf::diagnostic_output( std::ostream &, error_capture const & ); - - //////////////////////////////////////// - class dynamic_store { mutable std::atomic refcount_; @@ -189,7 +168,7 @@ namespace boost { namespace leaf { using namespace leaf_detail; typedef typename handler_wrapper::return_type return_type; if( leaf_detail::all_available::type>::type...>::check(*this) ) - return std::make_pair(true, handler_wrapper(std::forward(f))( *leaf::peek::type>::type>(*this)... )); + return std::make_pair(true, handler_wrapper(std::forward(f))( *this->template peek::type>::type>()... )); else return std::make_pair(false, return_type(uhnandled_error::value(e_))); } @@ -283,30 +262,37 @@ namespace boost { namespace leaf { } return e_; } + + template + typename leaf_detail::handler_pack_return_type::return_type handle_error( F && ... f ) const noexcept + { + return this->find_handler_( std::forward(f)... ).second; + } + + template + P const * peek() const noexcept + { + if( *this ) + if( auto * opt = ds_->bind

() ) + if( opt->has_value() ) + return &opt->value(); + return 0; + } + + void diagnostic_output( std::ostream & os ) const + { + if( *this ) + ds_->diagnostic_output_(os); + } }; - //////////////////////////////////////// - - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( error_capture const & ec, F && ... f ) noexcept + namespace leaf_detail { - return ec.find_handler_( std::forward(f)... ).second; - } - - template - P const * peek( error_capture const & e ) noexcept - { - if( e ) - if( auto * opt = e.ds_->bind

() ) - if( opt->has_value() ) - return &opt->value(); - return 0; - } - - inline void diagnostic_output( std::ostream & os, error_capture const & e ) - { - if( e ) - e.ds_->diagnostic_output_(os); + template + bool all_available::check( error_capture const & cap ) noexcept + { + return cap.template peek() && all_available::check(cap); + } } } } diff --git a/include/boost/leaf/exception.hpp b/include/boost/leaf/exception.hpp index e6c9f29..9ec7d55 100644 --- a/include/boost/leaf/exception.hpp +++ b/include/boost/leaf/exception.hpp @@ -21,28 +21,29 @@ namespace boost { namespace leaf { return next_error_value(); } - template - P const * peek( expect const & exp, std::exception const & ex ) noexcept + template + template + P const * expect::peek( std::exception const & ex ) const noexcept { - return peek

(exp,get_error(ex)); + return this->template peek

(get_error(ex)); } - template - void handle_exception( expect & exp, std::exception const & ex, F && ... f ) + template + template + void expect::handle_exception( std::exception const & ex, F && ... f ) const { - if( handle_error(exp,get_error(ex),f...) ) + if( this->handle_error(get_error(ex),f...) ) (void) error(); else throw; } - template - void diagnostic_output( std::ostream & os, expect const & exp, std::exception const & ex ) + inline void diagnostic_output( std::ostream & os, std::exception const & ex ) { os << "Exception dynamic type: " << leaf_detail::demangle(typeid(ex).name()) << std::endl << "std::exception::what(): " << ex.what() << std::endl; - diagnostic_output(os,exp,get_error(ex)); + get_error(ex).diagnostic_output(os); } } } diff --git a/include/boost/leaf/exception_capture.hpp b/include/boost/leaf/exception_capture.hpp index 2098d35..75c6c11 100644 --- a/include/boost/leaf/exception_capture.hpp +++ b/include/boost/leaf/exception_capture.hpp @@ -44,9 +44,9 @@ namespace boost { namespace leaf { std::rethrow_exception(ex_); } - friend void diagnostic_output_( std::ostream & os, captured_exception const & ce ) + void diagnostic_output( std::ostream & os ) const { - diagnostic_output(os,static_cast(ce)); + static_cast(this)->diagnostic_output(os); } }; diff --git a/include/boost/leaf/expect.hpp b/include/boost/leaf/expect.hpp index f5f049f..4b0b4b5 100644 --- a/include/boost/leaf/expect.hpp +++ b/include/boost/leaf/expect.hpp @@ -8,11 +8,16 @@ //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include +#include namespace boost { namespace leaf { class error_capture; + template + class result; + namespace leaf_detail { template @@ -117,13 +122,6 @@ namespace boost { namespace leaf { template class expect; - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( expect const &, error const &, F && ... ) noexcept; - - template - P const * peek( expect const &, error const & ) noexcept; - - template typename leaf_detail::dependent_type>::error_capture capture( expect &, error const & ); @@ -132,12 +130,6 @@ namespace boost { namespace leaf { { friend class error; - template - friend typename leaf_detail::handler_pack_return_type::return_type leaf::handle_error( expect const &, error const &, F && ... ) noexcept; - - template - friend P const * leaf::peek( expect const &, error const & ) noexcept; - template friend typename leaf_detail::dependent_type>::error_capture leaf::capture( expect &, error const & ); @@ -152,7 +144,7 @@ namespace boost { namespace leaf { using namespace leaf_detail; typedef typename handler_wrapper::return_type return_type; if( slots_subset::type>::type>...>::have_values(s_,e) ) - return std::make_pair(true, handler_wrapper(std::forward(f))( *leaf::peek::type>::type>(*this,e)... )); + return std::make_pair(true, handler_wrapper(std::forward(f))( *peek::type>::type>(e)... )); else return std::make_pair(false, uhnandled_error::value(e)); } @@ -177,29 +169,66 @@ namespace boost { namespace leaf { { } + ////////////////////////////////////////////// + + template + typename leaf_detail::handler_pack_return_type::return_type handle_error( error const & e, F && ... f ) const noexcept + { + return this->find_handler_( e, std::forward(f)... ).second; + } + + template + P const * peek( error const & e ) const noexcept + { + auto & opt = std::get::value>(s_); + if( opt.has_value() ) + { + auto & x = opt.value(); + if( x.e==e ) + return &x.v; + } + return 0; + } + + ////////////////////////////////////////////// + + template + typename leaf_detail::handler_pack_return_type::return_type handle_error( result const & r, F && ... f ) const noexcept + { + assert(!r); + if( r.which_ == leaf_detail::result_variant::err ) + return this->handle_error(r.err_,f...); + else + { + assert(r.which_==leaf_detail::result_variant::cap); + return r.cap_.handle_error(f...); + } + } + + template + P const * peek( result const & r ) const noexcept + { + assert(!r); + if( r.which_==leaf_detail::result_variant::err ) + return this->template peek

(r.err_); + else + { + assert(r.which_==leaf_detail::result_variant::cap); + return r.cap_.template peek

(); + } + } + + ////////////////////////////////////////////// + + template + P const * peek( std::exception const & ) const noexcept; + + template + void handle_exception( std::exception const &, F && ... ) const; }; //////////////////////////////////////// - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( expect const & exp, error const & e, F && ... f ) noexcept - { - return exp.find_handler_( e, std::forward(f)... ).second; - } - - template - P const * peek( expect const & exp, error const & e ) noexcept - { - auto & opt = std::get::value>(exp.s_); - if( opt.has_value() ) - { - auto & x = opt.value(); - if( x.e==e ) - return &x.v; - } - return 0; - } - template typename leaf_detail::dependent_type>::error_capture capture( expect & exp, error const & e ) { diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index a84af36..27c75b8 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -32,15 +32,6 @@ namespace boost { namespace leaf { }; } - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( expect const &, result const &, F && ... ) noexcept; - - template - P const * peek( expect const &, result const & ) noexcept; - - template - void diagnostic_output( std::ostream &, result const & ); - template result capture( expect &, result const & ); @@ -49,21 +40,15 @@ namespace boost { namespace leaf { template class result { - template - friend typename leaf_detail::handler_pack_return_type::return_type leaf::handle_error( expect const &, result const &, F && ... ) noexcept; - - template - friend P const * leaf::peek( expect const &, result const & ) noexcept; - - template - friend void leaf::diagnostic_output( std::ostream &, result const & ); - - template - friend result leaf::capture( expect &, result const & ); + template + friend class leaf::expect; template friend class result; + template + friend result leaf::capture( expect &, result const & ); + union { T value_; @@ -255,6 +240,18 @@ namespace boost { namespace leaf { return err_.propagate(std::forward(e)...); } } + + void diagnostic_output( std::ostream & os ) const + { + assert(!(*this)); + if( which_==leaf_detail::result_variant::err ) + return err_.diagnostic_output(os); + else + { + assert(which_==leaf_detail::result_variant::cap); + return cap_.diagnostic_output(os); + } + } }; //////////////////////////////////////// @@ -263,14 +260,8 @@ namespace boost { namespace leaf { class result: result { - template - friend typename leaf_detail::handler_pack_return_type::return_type leaf::handle_error( expect const &, result const &, F && ... ) noexcept; - - template - friend P const * leaf::peek( expect const &, result const & ) noexcept; - - template - friend void leaf::diagnostic_output( std::ostream &, result const & ); + template + friend class leaf::expect; template friend result leaf::capture( expect &, result const & ); @@ -310,36 +301,11 @@ namespace boost { namespace leaf { using base::operator bool; using base::error; + using base::diagnostic_output; }; //////////////////////////////////////// - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( expect const & exp, result const & r, F && ... f ) noexcept - { - assert(!r); - if( r.which_ == leaf_detail::result_variant::err ) - return handle_error(exp,r.err_,f...); - else - { - assert(r.which_==leaf_detail::result_variant::cap); - return handle_error(r.cap_,f...); - } - } - - template - P const * peek( expect const & exp, result const & r ) noexcept - { - assert(!r); - if( r.which_==leaf_detail::result_variant::err ) - return peek

(exp,r.err_); - else - { - assert(r.which_==leaf_detail::result_variant::cap); - return peek

(r.cap_); - } - } - template result capture( expect & exp, result const & r ) { diff --git a/test/basic_test.cpp b/test/basic_test.cpp index 0d5dfca..013c6e5 100644 --- a/test/basic_test.cpp +++ b/test/basic_test.cpp @@ -40,63 +40,63 @@ int main() leaf::expect,info<2>,info<4>> exp0; leaf::error e0 = f4(); { - info<1> const * p = leaf::peek>(exp0,e0); + info<1> const * p = exp0.peek>(e0); BOOST_TEST(p && p->value==1); } { - info<2> const * p = leaf::peek>(exp0,e0); + info<2> const * p = exp0.peek>(e0); BOOST_TEST(p && p->value==2); } - BOOST_TEST(!leaf::peek>(exp0,e0)); + BOOST_TEST(!exp0.peek>(e0)); leaf::expect,info<2>,info<4>> exp; leaf::error e1 = f4(); { - info<1> const * p = leaf::peek>(exp0,e0); + info<1> const * p = exp0.peek>(e0); BOOST_TEST(p && p->value==1); } { - info<2> const * p = leaf::peek>(exp0,e0); + info<2> const * p = exp0.peek>(e0); BOOST_TEST(p && p->value==2); } - BOOST_TEST(!leaf::peek>(exp0,e0)); - BOOST_TEST(!leaf::peek>(exp,e0)); - BOOST_TEST(!leaf::peek>(exp,e0)); - BOOST_TEST(!leaf::peek>(exp,e0)); + BOOST_TEST(!exp0.peek>(e0)); + BOOST_TEST(!exp.peek>(e0)); + BOOST_TEST(!exp.peek>(e0)); + BOOST_TEST(!exp.peek>(e0)); { - info<1> const * p = leaf::peek>(exp,e1); + info<1> const * p = exp.peek>(e1); BOOST_TEST(p && p->value==1); } { - info<2> const * p = leaf::peek>(exp,e1); + info<2> const * p = exp.peek>(e1); BOOST_TEST(p && p->value==2); } - BOOST_TEST(!leaf::peek>(exp,e1)); - BOOST_TEST( !handle_error( exp, e1, [ ](info<1>,info<2>,info<4>)->void { } ) ); + BOOST_TEST(!exp.peek>(e1)); + BOOST_TEST( !exp.handle_error( e1, [ ](info<1>,info<2>,info<4>)->void { } ) ); leaf::error e2 = f4(); { - info<1> const * p = leaf::peek>(exp,e2); + info<1> const * p = exp.peek>(e2); BOOST_TEST(p && p->value==1); } { - info<2> const * p = leaf::peek>(exp,e2); + info<2> const * p = exp.peek>(e2); BOOST_TEST(p && p->value==2); } - BOOST_TEST(!leaf::peek>(exp,e2)); + BOOST_TEST(!exp.peek>(e2)); { int c1=0, c2=0, c3=0; - bool handled = handle_error( exp, e2, + bool handled = exp.handle_error( e2, [&c1]( info<1>, info<2>, info<4> ) { ++c1; @@ -119,7 +119,7 @@ int main() { int c=0; - bool handled = handle_error( exp0, e0, + bool handled = exp0.handle_error( e0, [&c]( info<2> const & i2, info<1> const & i1 ) { BOOST_TEST(i1.value==1); diff --git a/test/defer_test.1.cpp b/test/defer_test.1.cpp index af57613..ea43e91 100644 --- a/test/defer_test.1.cpp +++ b/test/defer_test.1.cpp @@ -39,7 +39,7 @@ int main() { leaf::expect exp; int c=0; - bool handled = handle_error( exp, f(), + bool handled = exp.handle_error( f(), [&c]( info const & i42 ) { BOOST_TEST(i42.value==42); diff --git a/test/defer_test.2.cpp b/test/defer_test.2.cpp index deac3a2..838ed1b 100644 --- a/test/defer_test.2.cpp +++ b/test/defer_test.2.cpp @@ -37,9 +37,9 @@ int main() { leaf::expect,info<1>,info<2>,info<3>,info<4>> exp; leaf::error e = f2(); - BOOST_TEST(!leaf::peek>(exp,e)); + BOOST_TEST(!exp.peek>(e)); int c=0; - bool handled = handle_error( exp, e, + bool handled = exp.handle_error( e, [&c]( info<1> const & i1, info<2> const & i2, info<4> const & i4 ) { BOOST_TEST(i1.value==1); diff --git a/test/defer_test.3.cpp b/test/defer_test.3.cpp index d61bece..9cc2b0e 100644 --- a/test/defer_test.3.cpp +++ b/test/defer_test.3.cpp @@ -36,7 +36,7 @@ int main() leaf::expect exp; leaf::result r = f(); int c=0; - BOOST_TEST( handle_error( exp, r, + BOOST_TEST( exp.handle_error( r, [&c]( info const & x ) { BOOST_TEST(x.value==2); diff --git a/test/defer_test.4.cpp b/test/defer_test.4.cpp index 6296563..b3d3a36 100644 --- a/test/defer_test.4.cpp +++ b/test/defer_test.4.cpp @@ -42,7 +42,7 @@ int main() catch( my_error & e ) { int c=0; - handle_exception( exp, e, + exp.handle_exception( e, [&c]( info const & x ) { BOOST_TEST(x.value==2); diff --git a/test/defer_test.5.cpp b/test/defer_test.5.cpp index 6ccd6b2..9450893 100644 --- a/test/defer_test.5.cpp +++ b/test/defer_test.5.cpp @@ -39,7 +39,7 @@ int main() leaf::expect,info<1>,info<2>,info<3>> exp; leaf::error e = f2(); int c=0; - bool handled = handle_error( exp, e, + bool handled = exp.handle_error( e, [&c]( info<0> const & i0, info<1> const & i1, info<2> const & i2, info<3> const & i3 ) { BOOST_TEST(i0.value==0); diff --git a/test/defer_test.6.cpp b/test/defer_test.6.cpp index 943324a..331ce64 100644 --- a/test/defer_test.6.cpp +++ b/test/defer_test.6.cpp @@ -32,7 +32,7 @@ int main() leaf::expect exp; leaf::error e = f1(); int c=0; - bool handled = handle_error( exp, e, + bool handled = exp.handle_error( e, [&c]( info const & i0 ) { BOOST_TEST(i0.value==0); diff --git a/test/defer_test.7.cpp b/test/defer_test.7.cpp index 0093710..b399ef0 100644 --- a/test/defer_test.7.cpp +++ b/test/defer_test.7.cpp @@ -42,7 +42,7 @@ int main() catch( my_error & e ) { int c=0; - handle_exception( exp, e, + exp.handle_exception( e, [&c]( info const & x ) { BOOST_TEST(x.value==2); diff --git a/test/defer_test.8.cpp b/test/defer_test.8.cpp index a7e1fa5..4aa7a67 100644 --- a/test/defer_test.8.cpp +++ b/test/defer_test.8.cpp @@ -31,7 +31,7 @@ int main() leaf::expect exp; leaf::error e = f1(); int c=0; - bool handled = handle_error( exp, e, + bool handled = exp.handle_error( e, [&c]( leaf::e_unexpected const & unx, leaf::e_unexpected_diagnostic_output const & unxdo ) { BOOST_TEST(unx.count==1); diff --git a/test/diagnostic_output_test.cpp b/test/diagnostic_output_test.cpp index e697d74..3a2786e 100644 --- a/test/diagnostic_output_test.cpp +++ b/test/diagnostic_output_test.cpp @@ -110,7 +110,7 @@ int main() BOOST_TEST(s.find("Detected 2 attempts to communicate unexpected error objects, the first one of type ")!=s.npos); BOOST_TEST(s.find("unexpected_test<2>")!=s.npos); std::cout << s; - handle_exception( exp, e, [ ]{ } ); + exp.handle_exception( e, [ ]{ } ); } } BOOST_TEST(leaf::leaf_detail::tl_unexpected_enabled_counter()==0); diff --git a/test/error_capture_test.1.cpp b/test/error_capture_test.1.cpp index b3b561b..8aa1578 100644 --- a/test/error_capture_test.1.cpp +++ b/test/error_capture_test.1.cpp @@ -29,7 +29,7 @@ int main() { int c=0; - bool r = handle_error( ec, + bool r = ec.handle_error( [&c]( info<1> const & x ) { BOOST_TEST(c==0); @@ -42,7 +42,7 @@ int main() { int c=0; - bool r = handle_error( ec, + bool r = ec.handle_error( [&c]( info<2> const & x ) { BOOST_TEST(c==0); @@ -55,7 +55,7 @@ int main() { int c=0; - int r = handle_error( ec, + int r = ec.handle_error( [&c]( info<1> const & x ) { BOOST_TEST(c==0); @@ -67,7 +67,7 @@ int main() { int c=0; - int r = handle_error( ec, + int r = ec.handle_error( [&c]( info<2> const & x ) { BOOST_TEST(c==0); @@ -79,7 +79,7 @@ int main() { int c=0; - bool r = handle_error( ec, + bool r = ec.handle_error( [&c]( info<1> const & x, info<2> const & ) { BOOST_TEST(c==0); @@ -104,7 +104,7 @@ int main() { int c=0; - bool r = handle_error( ec, + bool r = ec.handle_error( [&c]( info<1> const & x, info<2> const & ) { BOOST_TEST(c==0); @@ -134,7 +134,7 @@ int main() leaf::error e2 = ec.unload(); { int c = 0; - bool r = handle_error( exp, e1, + bool r = exp.handle_error( e1, [&c]( info<1>, info<2>, info<3> ) { BOOST_TEST(c==0); @@ -156,7 +156,7 @@ int main() } { int c = 0; - bool r = handle_error( exp, e2, + bool r = exp.handle_error( e2, [&c]( info<1>, info<2>, info<3> ) { BOOST_TEST(c==0); diff --git a/test/error_capture_test.2.cpp b/test/error_capture_test.2.cpp index 697b18d..705650a 100644 --- a/test/error_capture_test.2.cpp +++ b/test/error_capture_test.2.cpp @@ -50,26 +50,26 @@ int main() { { leaf::error_capture ec1 = make_capture(); - BOOST_TEST( handle_error( ec1, [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec1.handle_error( [ ]( info<1>, info<3> ) { } ) ); BOOST_TEST(count==2); leaf::error_capture ec2(ec1); BOOST_TEST(count==2); - BOOST_TEST( handle_error( ec1, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec2, [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec1.handle_error( [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec2.handle_error( [ ]( info<1>, info<3> ) { } ) ); leaf::error_capture ec3(std::move(ec2)); BOOST_TEST(count==2); - BOOST_TEST( handle_error( ec1, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec3, [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec1.handle_error( [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec3.handle_error( [ ]( info<1>, info<3> ) { } ) ); leaf::error_capture ec4; ec4 = ec3; BOOST_TEST(count==2); - BOOST_TEST( handle_error( ec1, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec3, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec4, [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec1.handle_error( [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec3.handle_error( [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec4.handle_error( [ ]( info<1>, info<3> ) { } ) ); leaf::error_capture ec5; ec5 = std::move(ec4); BOOST_TEST(count==2); - BOOST_TEST( handle_error( ec1, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec3, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec5, [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec1.handle_error( [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec3.handle_error( [ ]( info<1>, info<3> ) { } ) ); + BOOST_TEST( ec5.handle_error( [ ]( info<1>, info<3> ) { } ) ); } BOOST_TEST(count==0); return boost::report_errors(); diff --git a/test/error_test.cpp b/test/error_test.cpp index c5b9dcf..f3ea124 100644 --- a/test/error_test.cpp +++ b/test/error_test.cpp @@ -37,7 +37,7 @@ leaf::error f3() { leaf::expect,info<3>,unexp<1>> exp; leaf::error e = f2().propagate( info<4>{4} ); - BOOST_TEST(leaf::peek>(exp,e)->value==1); + BOOST_TEST(exp.peek>(e)->value==1); return e; } @@ -46,14 +46,14 @@ leaf::error f4() leaf::expect,info<2>,info<3>,info<4>> exp; { leaf::error e = f3(); - bool handled = handle_error( exp, e, + bool handled = exp.handle_error( e, [ ]( info<1>, info<2>, info<3>, info<4> ){ }, [ ]( info<1>, info<2>, info<4> ) { } ); BOOST_TEST(handled); } leaf::error e = f3(); int c1=0, c2=0; - bool handled = handle_error( exp, e, + bool handled = exp.handle_error( e, [&c1]( info<1>,info<2>,info<3>,info<4> ) { ++c1; @@ -80,9 +80,9 @@ int main() { leaf::expect,info<3>,info<4>> exp; leaf::error e=f4(); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); + BOOST_TEST(!exp.peek>(e)); + BOOST_TEST(!exp.peek>(e)); + BOOST_TEST(!exp.peek>(e)); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); diff --git a/test/exception_capture_test.cpp b/test/exception_capture_test.cpp index 53c1ab2..d232b1d 100644 --- a/test/exception_capture_test.cpp +++ b/test/exception_capture_test.cpp @@ -64,7 +64,7 @@ void test( int task_count, F && f ) noexcept catch( my_error const & e ) { int c=0; - handle_exception( exp, e, [&f,&c]( info<1> const & x1, info<2> const & x2 ) + exp.handle_exception( e, [&f,&c]( info<1> const & x1, info<2> const & x2 ) { BOOST_TEST(x1.value==f.a); BOOST_TEST(x2.value==f.b); diff --git a/test/exception_test.1.cpp b/test/exception_test.1.cpp index 8938544..7ce7d70 100644 --- a/test/exception_test.1.cpp +++ b/test/exception_test.1.cpp @@ -60,7 +60,7 @@ void f4() catch( my_error const & e ) { int c1=0, c2=0; - handle_exception( exp, e, + exp.handle_exception( e, [&c1]( info<1>, info<2>, info<3>, info<4> ) { ++c1; @@ -90,9 +90,9 @@ void test( std::function const & f ) } catch( my_error const & e ) { - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); + BOOST_TEST(!exp.peek>(e)); + BOOST_TEST(!exp.peek>(e)); + BOOST_TEST(!exp.peek>(e)); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); diff --git a/test/exception_test.2.cpp b/test/exception_test.2.cpp index 7db1403..cfc61a1 100644 --- a/test/exception_test.2.cpp +++ b/test/exception_test.2.cpp @@ -60,7 +60,7 @@ void f4() catch( my_error const & e ) { int c1=0, c2=0; - handle_exception( exp, e, + exp.handle_exception( e, [&c1]( info<1>, info<2>, info<3>, info<4> ) { ++c1; @@ -90,9 +90,9 @@ void test( std::function const & f ) } catch( my_error const & e ) { - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); + BOOST_TEST(!exp.peek>(e)); + BOOST_TEST(!exp.peek>(e)); + BOOST_TEST(!exp.peek>(e)); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); diff --git a/test/expect_fail_test.1.cpp b/test/expect_fail_test.1.cpp index 9c8a913..d90c1e2 100644 --- a/test/expect_fail_test.1.cpp +++ b/test/expect_fail_test.1.cpp @@ -19,7 +19,7 @@ int main() { leaf::expect,info<2>,info<3>> exp; - (void) handle_error( exp, f(), + (void) exp.handle_error( f(), [ ]( info<1>, info<2> ) { return 42; diff --git a/test/expect_fail_test.2.cpp b/test/expect_fail_test.2.cpp index 40b42cb..70555d3 100644 --- a/test/expect_fail_test.2.cpp +++ b/test/expect_fail_test.2.cpp @@ -21,7 +21,7 @@ int main() { leaf::expect,info<2>,info<3>> exp; - (void) handle_error( exp, f(), + (void) exp.handle_error( f(), [ ]( info<1>, info<2> ) { return test { }; diff --git a/test/expect_test.1.cpp b/test/expect_test.1.cpp index 2905a68..a0a56ec 100644 --- a/test/expect_test.1.cpp +++ b/test/expect_test.1.cpp @@ -26,7 +26,7 @@ int main() { leaf::expect exp; int c=0; - BOOST_TEST( !handle_error( exp, f(), + BOOST_TEST( !exp.handle_error( f(), [&c]( info ) { ++c; diff --git a/test/expect_test.2.cpp b/test/expect_test.2.cpp index 61f8515..e80a0fc 100644 --- a/test/expect_test.2.cpp +++ b/test/expect_test.2.cpp @@ -38,7 +38,7 @@ int main() int c=0; try { - handle_exception( exp, e, + exp.handle_exception( e, [&c]( info ) { ++c; diff --git a/test/expect_test.3.cpp b/test/expect_test.3.cpp index 92d1374..a256572 100644 --- a/test/expect_test.3.cpp +++ b/test/expect_test.3.cpp @@ -38,7 +38,7 @@ int main() int c=0; try { - handle_exception( exp, e, + exp.handle_exception( e, [&c]( info ) { ++c; diff --git a/test/expect_test.4.cpp b/test/expect_test.4.cpp index 3184979..da51c41 100644 --- a/test/expect_test.4.cpp +++ b/test/expect_test.4.cpp @@ -23,7 +23,7 @@ int main() MStatus copy = ms; { int c=0; - bool handled = leaf::handle_error( exp, leaf::error(copy), + bool handled = exp.handle_error( leaf::error(copy), [&c]( MStatus ms ) { ++c; @@ -33,7 +33,7 @@ int main() } { int c=0; - bool handled = leaf::handle_error( exp, leaf::error(copy), + bool handled = exp.handle_error( leaf::error(copy), [&c]( MStatus const ms ) { ++c; @@ -43,7 +43,7 @@ int main() } { int c=0; - bool handled = leaf::handle_error( exp, leaf::error(copy), + bool handled = exp.handle_error( leaf::error(copy), [&c]( MStatus const & ms ) { ++c; @@ -56,7 +56,7 @@ int main() MStatus & ref = ms; { int c=0; - bool handled = leaf::handle_error( exp, leaf::error(ref), + bool handled = exp.handle_error( leaf::error(ref), [&c]( MStatus ms ) { ++c; @@ -66,7 +66,7 @@ int main() } { int c=0; - bool handled = leaf::handle_error( exp, leaf::error(ref), + bool handled = exp.handle_error( leaf::error(ref), [&c]( MStatus const ms ) { ++c; @@ -76,7 +76,7 @@ int main() } { int c=0; - bool handled = leaf::handle_error( exp, leaf::error(ref), + bool handled = exp.handle_error( leaf::error(ref), [&c]( MStatus const & ms ) { ++c; @@ -89,7 +89,7 @@ int main() { MStatus const & cref = ms; int c=0; - bool handled = leaf::handle_error( exp, leaf::error(cref), + bool handled = exp.handle_error( leaf::error(cref), [&c]( MStatus ms ) { ++c; @@ -100,7 +100,7 @@ int main() { MStatus const & cref = ms; int c=0; - bool handled = leaf::handle_error( exp, leaf::error(cref), + bool handled = exp.handle_error( leaf::error(cref), [&c]( MStatus const ms ) { ++c; @@ -111,7 +111,7 @@ int main() { MStatus const & cref = ms; int c=0; - bool handled = leaf::handle_error( exp, leaf::error(cref), + bool handled = exp.handle_error( leaf::error(cref), [&c]( MStatus const & ms ) { ++c; diff --git a/test/expect_test.5.cpp b/test/expect_test.5.cpp index 47681a1..263f43a 100644 --- a/test/expect_test.5.cpp +++ b/test/expect_test.5.cpp @@ -22,7 +22,7 @@ int main() { int c=0; - bool r = handle_error( exp, f(), + bool r = exp.handle_error( f(), [&c]( info<1> const & x ) { BOOST_TEST(c==0); @@ -35,7 +35,7 @@ int main() { int c=0; - bool r = handle_error( exp, f(), + bool r = exp.handle_error( f(), [&c]( info<2> const & x ) { BOOST_TEST(c==0); @@ -48,7 +48,7 @@ int main() { int c=0; - int r = handle_error( exp, f(), + int r = exp.handle_error( f(), [&c]( info<1> const & x ) { BOOST_TEST(c==0); @@ -60,7 +60,7 @@ int main() { int c=0; - int r = handle_error( exp, f(), + int r = exp.handle_error( f(), [&c]( info<2> const & x ) { BOOST_TEST(c==0); @@ -72,7 +72,7 @@ int main() { int c=0; - bool r = handle_error( exp, f(), + bool r = exp.handle_error( f(), [&c]( info<1> const & x, info<2> const & ) { BOOST_TEST(c==0); @@ -97,7 +97,7 @@ int main() { int c=0; - bool r = handle_error( exp, f(), + bool r = exp.handle_error( f(), [&c]( info<1> const & x, info<2> const & ) { BOOST_TEST(c==0); diff --git a/test/multiple_errors_test.cpp b/test/multiple_errors_test.cpp index 7e057b6..f7174ec 100644 --- a/test/multiple_errors_test.cpp +++ b/test/multiple_errors_test.cpp @@ -32,7 +32,7 @@ int main() leaf::error e2=f34(); { int e1c1=0, e1c2=0; - bool handled = handle_error( exp, e1, + bool handled = exp.handle_error( e1, [&e1c1]( info<3>, info<4> ) { ++e1c1; @@ -49,7 +49,7 @@ int main() } { int e2c1=0, e2c2=0; - bool handled = handle_error( exp, e2, + bool handled = exp.handle_error( e2, [&e2c1]( info<1>, info<2> ) { ++e2c1; diff --git a/test/preload_test.1.cpp b/test/preload_test.1.cpp index 4c90b00..84a7793 100644 --- a/test/preload_test.1.cpp +++ b/test/preload_test.1.cpp @@ -37,9 +37,9 @@ int main() { leaf::expect,info<1>,info<2>,info<3>,info<4>> exp; leaf::error e = f2(); - BOOST_TEST(!leaf::peek>(exp,e)); + BOOST_TEST(!exp.peek>(e)); int c=0; - bool handled = handle_error( exp, e, + bool handled = exp.handle_error( e, [&c]( info<0> const & i0, info<1> const & i1, info<2> const & i2, info<4> const & i4 ) { BOOST_TEST(i0.value==0); diff --git a/test/preload_test.2.cpp b/test/preload_test.2.cpp index 9e43ea7..f6b8bf5 100644 --- a/test/preload_test.2.cpp +++ b/test/preload_test.2.cpp @@ -37,7 +37,7 @@ main() leaf::expect exp; leaf::result r = f(); int c=0; - bool handled = handle_error( exp, r, + bool handled = exp.handle_error( r, [&c]( info const & x ) { BOOST_TEST(x.value==2); diff --git a/test/preload_test.3.cpp b/test/preload_test.3.cpp index 04a9c16..0f0c978 100644 --- a/test/preload_test.3.cpp +++ b/test/preload_test.3.cpp @@ -42,7 +42,7 @@ int main() catch( my_error & e ) { int c=0; - handle_exception( exp, e, + exp.handle_exception( e, [&c]( info const & x ) { BOOST_TEST(x.value==2); diff --git a/test/preload_test.4.cpp b/test/preload_test.4.cpp index d2939f3..9f07a7c 100644 --- a/test/preload_test.4.cpp +++ b/test/preload_test.4.cpp @@ -42,7 +42,7 @@ int main() catch( my_error & e ) { int c=0; - handle_exception( exp, e, + exp.handle_exception( e, [&c]( info const & x ) { BOOST_TEST(x.value==2); diff --git a/test/preload_test.5.cpp b/test/preload_test.5.cpp index a9fa9d1..9e6219c 100644 --- a/test/preload_test.5.cpp +++ b/test/preload_test.5.cpp @@ -39,7 +39,7 @@ int main() leaf::expect,info<1>,info<2>,info<3>> exp; leaf::error e = f2(); int c=0; - bool handled = handle_error( exp, e, + bool handled = exp.handle_error( e, [&c]( info<0> const & i0, info<1> const & i1, info<2> const & i2, info<3> const & i3 ) { BOOST_TEST(i0.value==0); diff --git a/test/preload_test.6.cpp b/test/preload_test.6.cpp index e935b60..ffceb60 100644 --- a/test/preload_test.6.cpp +++ b/test/preload_test.6.cpp @@ -32,7 +32,7 @@ int main() leaf::expect exp; leaf::error e = f1(); int c=0; - bool handled = handle_error( exp, e, + bool handled = exp.handle_error( e, [&c]( info const & i0 ) { BOOST_TEST(i0.value==0); diff --git a/test/preload_test.7.cpp b/test/preload_test.7.cpp index 13cc6f5..71585f5 100644 --- a/test/preload_test.7.cpp +++ b/test/preload_test.7.cpp @@ -31,7 +31,7 @@ int main() leaf::expect exp; leaf::error e = f1(); int c=0; - bool handled = handle_error( exp, e, + bool handled = exp.handle_error( e, [&c]( leaf::e_unexpected const & unx, leaf::e_unexpected_diagnostic_output const & unxdo ) { BOOST_TEST(unx.count==1); diff --git a/test/result_capture_test.cpp b/test/result_capture_test.cpp index 6803361..4e7c893 100644 --- a/test/result_capture_test.cpp +++ b/test/result_capture_test.cpp @@ -65,7 +65,7 @@ int main() else { int c=0; - bool handled = handle_error( exp, r, [&f,&c]( info<1> const & x1, info<2> const & x2 ) + bool handled = exp.handle_error( r, [&f,&c]( info<1> const & x1, info<2> const & x2 ) { BOOST_TEST(x1.value==f.a); BOOST_TEST(x2.value==f.b); diff --git a/test/result_test.1.cpp b/test/result_test.1.cpp index 0273b7d..76066e2 100644 --- a/test/result_test.1.cpp +++ b/test/result_test.1.cpp @@ -53,7 +53,7 @@ leaf::result f4( bool success ) else { int c1=0, c2=0; - bool handled = handle_error( exp, r, + bool handled = exp.handle_error( r, [&c1]( info<1>, info<2>, info<3>, info<4> ) { ++c1; @@ -79,9 +79,9 @@ int main() leaf::result r=f4(false); BOOST_TEST(!r); leaf::error e = r.error(); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); + BOOST_TEST(!exp.peek>(e)); + BOOST_TEST(!exp.peek>(e)); + BOOST_TEST(!exp.peek>(e)); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); diff --git a/test/result_test.2.cpp b/test/result_test.2.cpp index cb5e64f..2ef6566 100644 --- a/test/result_test.2.cpp +++ b/test/result_test.2.cpp @@ -402,7 +402,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2 = r1; BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -417,7 +417,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2 = r1; BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -432,7 +432,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2 = std::move(r1); BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -447,7 +447,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2 = std::move(r1); BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -462,7 +462,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=r1; BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -477,7 +477,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=r1; BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -492,7 +492,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=std::move(r1); BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -507,7 +507,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=std::move(r1); BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -522,7 +522,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2 = r1; BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -537,7 +537,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2 = r1; BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -552,7 +552,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2 = std::move(r1); BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -567,7 +567,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2 = std::move(r1); BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -582,7 +582,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=r1; BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -597,7 +597,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=r1; BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -612,7 +612,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=std::move(r1); BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -627,7 +627,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=std::move(r1); BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); + BOOST_TEST(exp.handle_error(r2,[ ]{ })); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } diff --git a/test/result_test.3.cpp b/test/result_test.3.cpp index 718f44a..25d2545 100644 --- a/test/result_test.3.cpp +++ b/test/result_test.3.cpp @@ -54,6 +54,6 @@ int main() leaf::expect exp; leaf::result r2 = r0.error( e_err2{ } ); BOOST_TEST(err::count==2); - BOOST_TEST(handle_error(exp,r2,[ ](e_err1,e_err2){ })); + BOOST_TEST(exp.handle_error(r2,[ ](e_err1,e_err2){ })); return boost::report_errors(); } diff --git a/test/result_test.4.cpp b/test/result_test.4.cpp index 051e200..76af89f 100644 --- a/test/result_test.4.cpp +++ b/test/result_test.4.cpp @@ -14,7 +14,7 @@ namespace leaf = boost::leaf; template void check( Expect & exp, leaf::bad_result const & e ) { - handle_exception( exp, e, + exp.handle_exception( e, [ ]( leaf::e_source_location const & x ) { BOOST_TEST(strstr(x.file,"result.hpp")!=0); diff --git a/test/result_test.5.cpp b/test/result_test.5.cpp index 577d539..1eefb84 100644 --- a/test/result_test.5.cpp +++ b/test/result_test.5.cpp @@ -23,7 +23,7 @@ int main() { int c=0; - bool r = handle_error( exp, f(), + bool r = exp.handle_error( f(), [&c]( info<1> const & x ) { BOOST_TEST(c==0); @@ -36,7 +36,7 @@ int main() { int c=0; - bool r = handle_error( exp, f(), + bool r = exp.handle_error( f(), [&c]( info<2> const & x ) { BOOST_TEST(c==0); @@ -49,7 +49,7 @@ int main() { int c=0; - int r = handle_error( exp, f(), + int r = exp.handle_error( f(), [&c]( info<1> const & x ) { BOOST_TEST(c==0); @@ -61,7 +61,7 @@ int main() { int c=0; - int r = handle_error( exp, f(), + int r = exp.handle_error( f(), [&c]( info<2> const & x ) { BOOST_TEST(c==0); @@ -73,7 +73,7 @@ int main() { int c=0; - bool r = handle_error( exp, f(), + bool r = exp.handle_error( f(), [&c]( info<1> const & x, info<2> const & ) { BOOST_TEST(c==0); @@ -98,7 +98,7 @@ int main() { int c=0; - bool r = handle_error( exp, f(), + bool r = exp.handle_error( f(), [&c]( info<1> const & x, info<2> const & ) { BOOST_TEST(c==0); @@ -124,7 +124,7 @@ int main() { int c=0; - leaf::result r = handle_error( exp, f(), + leaf::result r = exp.handle_error( f(), [&c]( info<1> const & x, info<3> const & ) -> leaf::result { BOOST_TEST(c==0); @@ -139,7 +139,7 @@ int main() int c=0; leaf::result r1 = f(); leaf::expect> exp1; - leaf::result r2 = handle_error( exp, r1, + leaf::result r2 = exp.handle_error( r1, [&c]( info<1> const & x, info<3> const & ) -> leaf::result { BOOST_TEST(c==0); @@ -148,7 +148,7 @@ int main() } ); BOOST_TEST(c==1); c = 0; - bool b = handle_error( exp1, r2, + bool b = exp1.handle_error( r2, [&c]( info<42> const & x ) { BOOST_TEST(c==0); @@ -162,7 +162,7 @@ int main() { int c=0; leaf::result r1 = f(); - leaf::result r2 = handle_error( exp, r1, + leaf::result r2 = exp.handle_error( r1, [&c]( info<2> const & ) -> leaf::result { BOOST_TEST(c==0); @@ -170,7 +170,7 @@ int main() return 42; } ); BOOST_TEST(c==0); - bool b = handle_error( exp, r2, + bool b = exp.handle_error( r2, [&c]( info<1> const & x, info<3> const & y ) { BOOST_TEST(c==0); diff --git a/test/result_test.6.cpp b/test/result_test.6.cpp index 22bcd40..89490b8 100644 --- a/test/result_test.6.cpp +++ b/test/result_test.6.cpp @@ -43,7 +43,7 @@ leaf::result handle_some_errors( int what_to_do ) if( leaf::result r = compute_answer(what_to_do) ) return r; else - return handle_error( exp, r, + return exp.handle_error( r, [&r]( error_code ec ) -> leaf::result { if( ec==error_code::error1 ) @@ -59,7 +59,7 @@ leaf::result handle_some_errors_float( int what_to_do ) if( leaf::result r = compute_answer(what_to_do) ) return r; else - return handle_error( exp, r, + return exp.handle_error( r, [&r]( error_code ec ) -> leaf::result { if( ec==error_code::error2 ) @@ -75,7 +75,7 @@ leaf::result handle_some_errors_void( int what_to_do ) if( leaf::result r = compute_answer(what_to_do) ) return { }; else - return handle_error( exp, r, + return exp.handle_error( r, [&r]( error_code ec ) -> leaf::result { if( ec==error_code::error3 ) @@ -97,7 +97,7 @@ int main() leaf::result r = handle_some_errors(2); BOOST_TEST(!r); int c=0; - BOOST_TEST( handle_error( exp, r, + BOOST_TEST( exp.handle_error( r, [&c]( error_code ec ) { BOOST_TEST(ec==error_code::error2); @@ -109,7 +109,7 @@ int main() leaf::result r = handle_some_errors(3); BOOST_TEST(!r); int c=0; - BOOST_TEST( handle_error( exp, r, + BOOST_TEST( exp.handle_error( r, [&c]( error_code ec ) { BOOST_TEST(ec==error_code::error3); @@ -125,7 +125,7 @@ int main() leaf::result r = handle_some_errors_float(1); BOOST_TEST(!r); int c=0; - BOOST_TEST( handle_error( exp, r, + BOOST_TEST( exp.handle_error( r, [&c]( error_code ec ) { BOOST_TEST(ec==error_code::error1); @@ -138,7 +138,7 @@ int main() leaf::result r = handle_some_errors_float(3); BOOST_TEST(!r); int c=0; - BOOST_TEST( handle_error( exp, r, + BOOST_TEST( exp.handle_error( r, [&c]( error_code ec ) { BOOST_TEST(ec==error_code::error3); @@ -154,7 +154,7 @@ int main() leaf::result r = handle_some_errors_void(1); BOOST_TEST(!r); int c=0; - BOOST_TEST( handle_error( exp, r, + BOOST_TEST( exp.handle_error( r, [&c]( error_code ec ) { BOOST_TEST(ec==error_code::error1); @@ -166,7 +166,7 @@ int main() leaf::result r = handle_some_errors_void(2); BOOST_TEST(!r); int c=0; - BOOST_TEST( handle_error( exp, r, + BOOST_TEST( exp.handle_error( r, [&c]( error_code ec ) { BOOST_TEST(ec==error_code::error2); diff --git a/test/result_void_capture_test.cpp b/test/result_void_capture_test.cpp index 9aa6bcd..43b2807 100644 --- a/test/result_void_capture_test.cpp +++ b/test/result_void_capture_test.cpp @@ -64,7 +64,7 @@ int main() else { int c=0; - bool handled = handle_error( exp, r, [&f,&c]( info<1> const & x1, info<2> const & x2 ) + bool handled = exp.handle_error( r, [&f,&c]( info<1> const & x1, info<2> const & x2 ) { BOOST_TEST(x1.value==f.a); BOOST_TEST(x2.value==f.b); From 6a85827e695f7ff272971cab254a990cfd1878aa Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Thu, 3 Jan 2019 17:46:33 -0800 Subject: [PATCH 03/34] building feature branches on travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index edcbee9..d67f527 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ branches: only: - master - develop - - feature/* + - /^feature.*/ env: matrix: From 108f0a9c08602084fc0147f6b114b7e266a8279e Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Thu, 3 Jan 2019 17:59:54 -0800 Subject: [PATCH 04/34] using master boost branch on travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d67f527..67841b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -231,7 +231,7 @@ install: - gem install coderay # - pip3 install meson - cd .. - - git clone -b $TRAVIS_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + - git clone -b master --depth 1 https://github.com/boostorg/boost.git boost-root - cd boost-root - git submodule update --init tools/build - git submodule update --init tools/inspect From 1b0578ca3218dceb736f8a45a8962abb530a6155 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Thu, 3 Jan 2019 18:41:38 -0800 Subject: [PATCH 05/34] removed sfinae workarounds from handle_error.hpp --- include/boost/leaf/detail/handle_error.hpp | 61 ++++------------------ 1 file changed, 9 insertions(+), 52 deletions(-) diff --git a/include/boost/leaf/detail/handle_error.hpp b/include/boost/leaf/detail/handle_error.hpp index a6049b1..0339449 100644 --- a/include/boost/leaf/detail/handle_error.hpp +++ b/include/boost/leaf/detail/handle_error.hpp @@ -49,14 +49,14 @@ namespace boost { namespace leaf { namespace leaf_detail { - template ::return_type> - struct handler_wrapper_base + template ::return_type> + struct handler_wrapper { - typedef typename function_traits::return_type return_type; + typedef ReturnType return_type; F f_; - explicit handler_wrapper_base( F && f ) noexcept: + explicit handler_wrapper( F && f ) noexcept: f_(std::forward(f)) { } @@ -69,13 +69,13 @@ namespace boost { namespace leaf { }; template - struct handler_wrapper_base + struct handler_wrapper { typedef bool return_type; F f_; - explicit handler_wrapper_base( F && f ) noexcept: + explicit handler_wrapper( F && f ) noexcept: f_(std::forward(f)) { } @@ -88,56 +88,13 @@ namespace boost { namespace leaf { } }; - template - struct handler_wrapper - { - typedef void return_type; - }; - - template - struct handler_wrapper::return_type>>: handler_wrapper_base - { - explicit handler_wrapper( F && f ) noexcept: - handler_wrapper_base(std::forward(f)) - { - } - }; - - template - struct handler_pack_return_type_helper - { - struct type; - }; - - template - struct handler_pack_return_type_helper - { - typedef R type; - }; - template struct handler_pack_return_type; - template - struct handler_pack_return_type + template + struct handler_pack_return_type { - typedef typename handler_wrapper::return_type return_type; - }; - - template - struct handler_pack_return_type - { - typedef typename handler_pack_return_type_helper< - typename handler_wrapper::return_type, - typename handler_wrapper::return_type>::type return_type; - }; - - template - struct handler_pack_return_type - { - typedef typename handler_pack_return_type_helper< - typename handler_wrapper::return_type, - typename handler_pack_return_type::return_type>::type return_type; + typedef typename handler_wrapper::return_type return_type; }; } //leaf_detail From a88d950045873370c38700ef9f6e692f6a3f0839 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Wed, 9 Jan 2019 17:30:31 -0800 Subject: [PATCH 06/34] Complete rewrite to try_, handle_some, handle_all --- .vscode/launch.json | 4 +- .vscode/tasks.json | 333 +----- example/capture_eh.cpp | 106 +- example/capture_result.cpp | 95 +- example/exception_to_result.cpp | 104 ++ example/lua_callback_eh.cpp | 2 +- example/lua_callback_result.cpp | 2 +- example/print_file_eh.cpp | 135 +-- example/print_file_result.cpp | 169 +-- example/print_half.cpp | 96 +- example/return_exception.cpp | 120 -- include/boost/leaf/all.hpp | 11 +- include/boost/leaf/capture_exception.hpp | 116 ++ include/boost/leaf/capture_result.hpp | 62 + .../boost/leaf/detail/captured_exception.hpp | 39 + include/boost/leaf/detail/dynamic_store.hpp | 41 + .../boost/leaf/detail/dynamic_store_impl.hpp | 91 ++ include/boost/leaf/detail/function_traits.hpp | 15 +- include/boost/leaf/detail/handle_error.hpp | 103 -- include/boost/leaf/detail/mp11.hpp | 211 ++++ include/boost/leaf/detail/print.hpp | 49 +- include/boost/leaf/detail/static_store.hpp | 465 ++++++++ .../diagnostic_output_current_exception.hpp | 55 - include/boost/leaf/error_capture.hpp | 300 ----- include/boost/leaf/exception.hpp | 51 - include/boost/leaf/exception_capture.hpp | 108 -- include/boost/leaf/exception_to_result.hpp | 54 + include/boost/leaf/expect.hpp | 246 ---- include/boost/leaf/handle.hpp | 84 ++ include/boost/leaf/result.hpp | 110 +- include/boost/leaf/{detail => }/throw.hpp | 3 +- include/boost/leaf/try.hpp | 146 +++ meson.build | 86 +- test/Jamfile.v2 | 105 +- ...st.cpp => _hpp_capture_exception_test.cpp} | 4 +- test/_hpp_capture_result_test.cpp | 9 + test/_hpp_exception_to_result_test.cpp | 9 + ...p_expect_test.cpp => _hpp_handle_test.cpp} | 4 +- ...exception_test.cpp => _hpp_throw_test.cpp} | 4 +- ...ror_capture_test.cpp => _hpp_try_test.cpp} | 4 +- test/capture_exception_async_test.cpp | 85 ++ test/capture_exception_state_test.cpp | 59 + test/capture_exception_unload_test.cpp | 171 +++ test/capture_result_async_test.cpp | 85 ++ test/capture_result_state_test.cpp | 59 + test/capture_result_unload_test.cpp | 163 +++ ...{defer_test.1.cpp => defer_basic_test.cpp} | 21 +- test/defer_nested_error_exception_test.cpp | 68 ++ ...cpp => defer_nested_error_result_test.cpp} | 31 +- .../defer_nested_new_error_exception_test.cpp | 75 ++ ...=> defer_nested_new_error_result_test.cpp} | 23 +- ...> defer_nested_success_exception_test.cpp} | 40 +- ...p => defer_nested_success_result_test.cpp} | 26 +- test/defer_test.6.cpp | 44 - test/defer_test.7.cpp | 54 - test/defer_test.8.cpp | 45 - test/diagnostic_output_test.cpp | 36 +- test/error_capture_test.1.cpp | 183 --- test/error_capture_test.2.cpp | 76 -- test/error_test.cpp | 91 -- test/exception_capture_test.cpp | 100 -- test/exception_test.1.cpp | 122 -- test/exception_test.2.cpp | 122 -- test/expect_fail_test.1.cpp | 33 - test/expect_fail_test.2.cpp | 35 - test/expect_test.1.cpp | 36 - test/expect_test.2.cpp | 55 - test/expect_test.3.cpp | 55 - test/expect_test.4.cpp | 124 -- test/expect_test.5.cpp | 125 -- test/function_traits_test.cpp | 5 +- test/handle_all_test.cpp | 350 ++++++ test/handle_some_basic_test.cpp | 148 +++ test/handle_some_test.cpp | 1013 +++++++++++++++++ ...l_test.cpp => is_error_type_fail_test.cpp} | 0 test/multiple_errors_test.cpp | 4 +- ...load_test.6.cpp => preload_basic_test.cpp} | 33 +- test/preload_nested_error_exception_test.cpp | 68 ++ ...p => preload_nested_error_result_test.cpp} | 30 +- ...reload_nested_new_error_exception_test.cpp | 75 ++ ... preload_nested_new_error_result_test.cpp} | 23 +- ...preload_nested_success_exception_test.cpp} | 40 +- ...=> preload_nested_success_result_test.cpp} | 30 +- test/preload_test.4.cpp | 54 - test/preload_test.7.cpp | 45 - test/result_bad_result_test.cpp | 68 ++ test/result_capture_test.cpp | 79 -- test/result_state_test.cpp | 844 ++++++++++++++ test/result_test.1.cpp | 90 -- test/result_test.2.cpp | 638 ----------- test/result_test.3.cpp | 59 - test/result_test.4.cpp | 81 -- test/result_test.5.cpp | 186 --- test/result_test.6.cpp | 182 --- test/result_void_capture_test.cpp | 78 -- test/static_store_deduction_test.cpp | 95 ++ .../{basic_test.cpp => static_store_test.cpp} | 59 +- test/try_exception_test.cpp | 62 + test/try_test.cpp | 522 +++++++++ 99 files changed, 6114 insertions(+), 4840 deletions(-) create mode 100644 example/exception_to_result.cpp delete mode 100644 example/return_exception.cpp create mode 100644 include/boost/leaf/capture_exception.hpp create mode 100644 include/boost/leaf/capture_result.hpp create mode 100644 include/boost/leaf/detail/captured_exception.hpp create mode 100644 include/boost/leaf/detail/dynamic_store.hpp create mode 100644 include/boost/leaf/detail/dynamic_store_impl.hpp delete mode 100644 include/boost/leaf/detail/handle_error.hpp create mode 100644 include/boost/leaf/detail/mp11.hpp create mode 100644 include/boost/leaf/detail/static_store.hpp delete mode 100644 include/boost/leaf/diagnostic_output_current_exception.hpp delete mode 100644 include/boost/leaf/error_capture.hpp delete mode 100644 include/boost/leaf/exception.hpp delete mode 100644 include/boost/leaf/exception_capture.hpp create mode 100644 include/boost/leaf/exception_to_result.hpp delete mode 100644 include/boost/leaf/expect.hpp create mode 100644 include/boost/leaf/handle.hpp rename include/boost/leaf/{detail => }/throw.hpp (97%) create mode 100644 include/boost/leaf/try.hpp rename test/{_hpp_exception_capture_test.cpp => _hpp_capture_exception_test.cpp} (74%) create mode 100644 test/_hpp_capture_result_test.cpp create mode 100644 test/_hpp_exception_to_result_test.cpp rename test/{_hpp_expect_test.cpp => _hpp_handle_test.cpp} (79%) rename test/{_hpp_exception_test.cpp => _hpp_throw_test.cpp} (78%) rename test/{_hpp_error_capture_test.cpp => _hpp_try_test.cpp} (76%) create mode 100644 test/capture_exception_async_test.cpp create mode 100644 test/capture_exception_state_test.cpp create mode 100644 test/capture_exception_unload_test.cpp create mode 100644 test/capture_result_async_test.cpp create mode 100644 test/capture_result_state_test.cpp create mode 100644 test/capture_result_unload_test.cpp rename test/{defer_test.1.cpp => defer_basic_test.cpp} (77%) create mode 100644 test/defer_nested_error_exception_test.cpp rename test/{defer_test.2.cpp => defer_nested_error_result_test.cpp} (66%) create mode 100644 test/defer_nested_new_error_exception_test.cpp rename test/{defer_test.5.cpp => defer_nested_new_error_result_test.cpp} (75%) rename test/{defer_test.4.cpp => defer_nested_success_exception_test.cpp} (64%) rename test/{defer_test.3.cpp => defer_nested_success_result_test.cpp} (76%) delete mode 100644 test/defer_test.6.cpp delete mode 100644 test/defer_test.7.cpp delete mode 100644 test/defer_test.8.cpp delete mode 100644 test/error_capture_test.1.cpp delete mode 100644 test/error_capture_test.2.cpp delete mode 100644 test/error_test.cpp delete mode 100644 test/exception_capture_test.cpp delete mode 100644 test/exception_test.1.cpp delete mode 100644 test/exception_test.2.cpp delete mode 100644 test/expect_fail_test.1.cpp delete mode 100644 test/expect_fail_test.2.cpp delete mode 100644 test/expect_test.1.cpp delete mode 100644 test/expect_test.2.cpp delete mode 100644 test/expect_test.3.cpp delete mode 100644 test/expect_test.4.cpp delete mode 100644 test/expect_test.5.cpp create mode 100644 test/handle_all_test.cpp create mode 100644 test/handle_some_basic_test.cpp create mode 100644 test/handle_some_test.cpp rename test/{error_fail_test.cpp => is_error_type_fail_test.cpp} (100%) rename test/{preload_test.6.cpp => preload_basic_test.cpp} (57%) create mode 100644 test/preload_nested_error_exception_test.cpp rename test/{preload_test.1.cpp => preload_nested_error_result_test.cpp} (64%) create mode 100644 test/preload_nested_new_error_exception_test.cpp rename test/{preload_test.5.cpp => preload_nested_new_error_result_test.cpp} (74%) rename test/{preload_test.3.cpp => preload_nested_success_exception_test.cpp} (63%) rename test/{preload_test.2.cpp => preload_nested_success_result_test.cpp} (72%) delete mode 100644 test/preload_test.4.cpp delete mode 100644 test/preload_test.7.cpp create mode 100644 test/result_bad_result_test.cpp delete mode 100644 test/result_capture_test.cpp create mode 100644 test/result_state_test.cpp delete mode 100644 test/result_test.1.cpp delete mode 100644 test/result_test.2.cpp delete mode 100644 test/result_test.3.cpp delete mode 100644 test/result_test.4.cpp delete mode 100644 test/result_test.5.cpp delete mode 100644 test/result_test.6.cpp delete mode 100644 test/result_void_capture_test.cpp create mode 100644 test/static_store_deduction_test.cpp rename test/{basic_test.cpp => static_store_test.cpp} (71%) create mode 100644 test/try_exception_test.cpp create mode 100644 test/try_test.cpp diff --git a/.vscode/launch.json b/.vscode/launch.json index 1bf17a0..bbfb2dc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,8 +8,8 @@ "name": "(lldb) Launch", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/bld/debug/preload_test.2", - "args": [ "test/Jafile.v2" ], + "program": "${workspaceFolder}/bld/debug/result_state_test", + "args": [ ], "cwd": "${workspaceFolder}", "stopAtEntry": false, "externalConsole": false, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 485fbdf..da32954 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -24,288 +24,66 @@ } }, { - "label": "basic_test", + "label": "capture_exception_test.1", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test basic_test", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.1", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test basic_test" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.1" } }, { - "label": "defer_test.1", + "label": "capture_exception_test.2", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.1", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.2", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.1" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.2" } }, { - "label": "defer_test.2", + "label": "capture_exception_test.3", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.2", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.3", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.2" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.3" } }, { - "label": "defer_test.3", + "label": "capture_result_test.1", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.3", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_test.1", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.3" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_test.1" } }, { - "label": "defer_test.4", + "label": "capture_result_test.2", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.4", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_test.2", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.4" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_test.2" } }, { - "label": "defer_test.5", + "label": "capture_result_test.3", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.5", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_test.3", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.5" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_test.3" } }, { - "label": "defer_test.6", + "label": "handle_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.6", + "command": "cd ${workspaceRoot}/bld/debug && meson test handle_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.6" - } - }, - { - "label": "defer_test.7", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.7", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.7" - } - }, - { - "label": "defer_test.8", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.8", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.8" - } - }, - { - "label": "diagnostic_output_test", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test diagnostic_output_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test diagnostic_output_test" - } - }, - { - "label": "error_capture_test.1", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test error_capture_test.1", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test error_capture_test.1" - } - }, - { - "label": "error_capture_test.2", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test error_capture_test.2", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test error_capture_test.2" - } - }, - { - "label": "error_test", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test error_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test error_test" - } - }, - { - "label": "exception_capture_test", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test exception_capture_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test exception_capture_test" - } - }, - { - "label": "exception_test.1", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test exception_test.1", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test exception_test.1" - } - }, - { - "label": "exception_test.2", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test exception_test.2", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test exception_test.2" - } - }, - { - "label": "expect_test.1", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test expect_test.1", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test expect_test.1" - } - }, - { - "label": "expect_test.2", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test expect_test.2", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test expect_test.2" - } - }, - { - "label": "expect_test.3", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test expect_test.3", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test expect_test.3" - } - }, - { - "label": "expect_test.4", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test expect_test.4", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test expect_test.4" - } - }, - { - "label": "expect_test.5", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test expect_test.5", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test expect_test.5" - } - }, - { - "label": "function_traits_test", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test function_traits_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test function_traits_test" - } - }, - { - "label": "is_error_type_test", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test is_error_type_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test is_error_type_test" - } - }, - { - "label": "multiple_errors_test", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test multiple_errors_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test multiple_errors_test" - } - }, - { - "label": "optional_test", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test optional_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] } - }, - { - "label": "preload_test.1", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.1", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test optional_test" - } - }, - { - "label": "preload_test.2", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.2", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.2" - } - }, - { - "label": "preload_test.3", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.3", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.3" - } - }, - { - "label": "preload_test.4", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.4", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.4" - } - }, - { - "label": "preload_test.5", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.5", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.5" - } - }, - { - "label": "preload_test.6", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.6", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.6" - } - }, - { - "label": "preload_test.7", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.7", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.7" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test handle_test" } }, { @@ -318,75 +96,12 @@ } }, { - "label": "result_capture_test", + "label": "try_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_capture_test", + "command": "cd ${workspaceRoot}/bld/debug && meson test try_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_capture_test" - } - }, - { - "label": "result_test.1", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.1", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.1" - } - }, - { - "label": "result_test.2", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.2", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.2" - } - }, - { - "label": "result_test.3", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.3", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.3" - } - }, - { - "label": "result_test.4", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.4", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.4" - } - }, - { - "label": "result_test.5", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.5", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.5" - } - }, - { - "label": "result_test.6", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.6", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.6" - } - }, - { - "label": "result_void_capture_test", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_void_capture_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_void_capture_test" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test try_test" } } ] diff --git a/example/capture_eh.cpp b/example/capture_eh.cpp index 725b4ce..77780f2 100644 --- a/example/capture_eh.cpp +++ b/example/capture_eh.cpp @@ -5,10 +5,11 @@ //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) //This is a simple program that demonstrates the use of LEAF to transport e-objects between threads, -//when using exception handling to report failures. See capture_result.cpp for the variant that doesn't -//use exception handling. +//without using exception handling. See capture_eh.cpp for the exception-handling variant. -#include +#include +#include +#include #include #include #include @@ -22,80 +23,67 @@ namespace leaf = boost::leaf; struct e_thread_id { std::thread::id value; }; struct e_failure_info1 { std::string value; }; struct e_failure_info2 { int value; }; -struct e_failure_info3 { long value; }; -struct e_failure_info4 { float value; }; //A type that represents a successfully returned result from a task. struct task_result { }; -//An exception type thrown in case of task failure. -struct failure : virtual std::exception { }; - -//This is a test task which succeeds or fails depending on its argument. In case of success, it returns -//task_result, in case of error it throws failure. -task_result task( bool succeed ) + //This is our task function. It produces objects of type task_result, but it may fail... +task_result task() { + bool succeed = (rand()%4) !=0; //...at random. if( succeed ) - return task_result(); //Simulate successful result. + return task_result { }; else - throw leaf::exception( - failure(), + throw leaf::exception( std::exception(), e_thread_id{std::this_thread::get_id()}, e_failure_info1{"info"}, - e_failure_info2{42}, - e_failure_info4{42} ); -} - -//Launch the specified number of asynchronous tasks. In case an asynchronous task throws, its e-objects -//(of the type list used to instantiate leaf::capture_exception) are captured and wrapped in a different -//exception, which transports it to the main thread. The original exception is then recovered by leaf::get. -template -std::vector> launch_async_tasks( int thread_count ) -{ - std::vector> fut; - std::generate_n( std::inserter(fut,fut.end()), thread_count, [ ] - { - return std::async( std::launch::async, - leaf::capture_exception( [ ] //returns a wrapper function for the lambda... - { - return task((rand()%4)!=0); //...which transports the E... objects. - } ) ); - } ); - return fut; -} + e_failure_info2{42} ); +}; int main() { - //Launch tasks, transport the specified e-types. For demonstration, note that the task provides - //failure_info4 which we don't care about, and that we say we could use failure_info3, but which the - //task doesn't provide. So, we'll only get failed_thread_id, failure_info1 and failure_info2. - auto fut = launch_async_tasks(42); + int const task_count = 42; - //Collect results or deal with failures. + //Container to collect the generated std::future objects. + std::vector> fut; + + //Launch the tasks, but rather than launching the task function directly, we launch the + //wrapper function returned by leaf::capture_result. It captures the specified error object + //types and automatically transports them in the leaf::result it returns. + std::generate_n( std::inserter(fut,fut.end()), task_count, + [ ] + { + return std::async( + std::launch::async, + leaf::capture_exception(task) ); + } ); + + //Wait on the futures, get the task results, handle errors. for( auto & f : fut ) { f.wait(); - //Storage for e-objects. - leaf::expect exp; + leaf::try_( + [&] + { + task_result r = f.get(); - try - { - //Instead of calling future::get we pass the future object to leaf::get. In case the future finished with an exception, - //this will rethrow that exception, after dropping any captured e-objects into exp. - task_result r = leaf::get(f); + //Success! Use r to access task_result. + std::cout << "Success!" << std::endl; - //Success! - std::cout << "Success!" << std::endl; - } - catch( failure const & e ) - { - //Failure! Handle the error, print failure info. - exp.handle_exception( e, - [ ] ( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid ) - { - std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; - } ); - } + }, + + [ ]( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid ) + { + std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; + }, + + [ ]( leaf::error e ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl; + e.diagnostic_output(std::cerr); + } ); } } diff --git a/example/capture_result.cpp b/example/capture_result.cpp index f8a555e..bdf9b9d 100644 --- a/example/capture_result.cpp +++ b/example/capture_result.cpp @@ -7,7 +7,9 @@ //This is a simple program that demonstrates the use of LEAF to transport e-objects between threads, //without using exception handling. See capture_eh.cpp for the exception-handling variant. -#include +#include +#include + #include #include #include @@ -21,72 +23,67 @@ namespace leaf = boost::leaf; struct e_thread_id { std::thread::id value; }; struct e_failure_info1 { std::string value; }; struct e_failure_info2 { int value; }; -struct e_failure_info3 { long value; }; -struct e_failure_info4 { float value; }; //A type that represents a successfully returned result from a task. struct task_result { }; -//This is a test task which succeeds or fails depending on its argument. -leaf::result task( bool succeed ) + //This is our task function. It produces objects of type task_result, but it may fail... +leaf::result task() { + bool succeed = (rand()%4) !=0; //...at random. if( succeed ) - return task_result(); //Simulate successful result. + return task_result { }; else return leaf::error( e_thread_id{std::this_thread::get_id()}, e_failure_info1{"info"}, - e_failure_info2{42}, - e_failure_info4{42} ); -} - -//Launch the specified number of asynchronous tasks. In case an asynchronous task fails, its e-objects -//(initially stored in exp) are captured in a leaf::result, which transports them to the main thread. -template -std::vector>> launch_async_tasks( int thread_count ) -{ - std::vector>> fut; - std::generate_n( std::inserter(fut,fut.end()), thread_count, [ ] - { - return std::async( std::launch::async, [ ] - { - leaf::expect exp; - return capture(exp,task((rand()%4)!=0)); - } ); - } ); - return fut; -} + e_failure_info2{42} ); +}; int main() { - //Launch tasks, transport the specified e-types. For demonstration, note that the task provides - //failure_info4 which we don't care about, and that we say we could use failure_info3, but which - //the task doesn't provide. So, we'll only get failed_thread_id, failure_info1 and failure_info2. - auto fut = launch_async_tasks(42); + int const task_count = 42; - //Collect results or deal with failures. + //Container to collect the generated std::future objects. + std::vector>> fut; + + //Launch the tasks, but rather than launching the task function directly, we launch the + //wrapper function returned by leaf::capture_result. It captures the specified error object + //types and automatically transports them in the leaf::result it returns. + std::generate_n( std::inserter(fut,fut.end()), task_count, + [ ] + { + return std::async( + std::launch::async, + leaf::capture_result(task) ); + } ); + + //Wait on the futures, get the task results, handle errors. for( auto & f : fut ) { f.wait(); - //Storage for e-objects. - leaf::expect exp; + leaf::handle_all( + [&]() -> leaf::result + { + LEAF_AUTO(r,f.get()); - //Get the task result, check for success. - if( leaf::result r = f.get() ) - { - //Success! Use *r to access task_result. - std::cout << "Success!" << std::endl; - } - else - { - //Failure! Handle error, print failure info. - bool matched = exp.handle_error( r, - [ ] ( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid ) - { - std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; - } ); - assert(matched); - } + //Success! Use r to access task_result. + std::cout << "Success!" << std::endl; + return { }; + }, + + [ ]( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid ) + { + std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; + }, + + [ ]( leaf::error e ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl; + e.diagnostic_output(std::cerr); + } ); } } diff --git a/example/exception_to_result.cpp b/example/exception_to_result.cpp new file mode 100644 index 0000000..7c63099 --- /dev/null +++ b/example/exception_to_result.cpp @@ -0,0 +1,104 @@ +//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 + +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 for error handling. +leaf::result 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( + [ ] + { + return compute_answer_throws(); + } ); +} + + +//Print the answer if the call to compute_answer is successful. +leaf::result print_answer() noexcept +{ + LEAF_AUTO( r, compute_answer()); + std::cout << "Answer: " << r << 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::handle_all( + [ ]() -> leaf::result + { + 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::diagnostic_output(std::cerr,*ep); + } ); + } + + return 0; +} diff --git a/example/lua_callback_eh.cpp b/example/lua_callback_eh.cpp index fd4d9ca..2e18ee9 100644 --- a/example/lua_callback_eh.cpp +++ b/example/lua_callback_eh.cpp @@ -106,7 +106,7 @@ int call_lua( lua_State * L ) int main() noexcept { std::shared_ptr L=init_lua_state(); - leaf::expect exp; + leaf::static_store exp; for( int i=0; i!=10; ++i ) try { diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index b0042b1..a7bec65 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -112,7 +112,7 @@ int main() noexcept { std::shared_ptr L=init_lua_state(); - leaf::expect exp; + leaf::static_store exp; for( int i=0; i!=10; ++i ) if( leaf::result r = call_lua(&*L) ) diff --git a/example/print_file_eh.cpp b/example/print_file_eh.cpp index 1a5e25e..7ef4f14 100644 --- a/example/print_file_eh.cpp +++ b/example/print_file_eh.cpp @@ -8,7 +8,9 @@ //additional information when using exceptions to report failures. See print_file_result.cpp //for the variant that doesn't use exceptions. -#include +#include +#include +#include #include #include #include @@ -74,21 +76,6 @@ void file_read( FILE & f, void * buf, int size ) } -void print_file( char const * file_name ) -{ - std::shared_ptr f = file_open( file_name ); - - auto propagate1 = leaf::preload( e_file_name{file_name} ); - - std::string buffer( 1+file_size(*f), '\0' ); - file_read(*f,&buffer[0],buffer.size()-1); - - auto propagate2 = leaf::defer([ ] { return e_errno{errno}; } ); - std::cout << buffer; - std::cout.flush(); -} - - char const * parse_command_line( int argc, char const * argv[ ] ) { if( argc!=2 ) @@ -102,84 +89,58 @@ int main( int argc, char const * argv[ ] ) //Configure std::cout to throw on error. std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); - //We expect e_file_name and e_errno objects to be associated with errors - //handled in this function. They will be stored inside of exp. - leaf::expect exp; + return leaf::try_( + [&] + { + char const * file_name = parse_command_line(argc,argv); + std::shared_ptr f = file_open( file_name ); - try - { - print_file(parse_command_line(argc,argv)); - return 0; - } - catch( bad_command_line const & ) - { - std::cout << "Bad command line argument" << std::endl; - return 1; - } - catch( input_file_open_error const & e ) - { - //handle_exception takes a list of functions (in this case only one). It attempts to - //match each function (in order) to objects currently available in exp, which - //are associated with the error value stored in e. If no function can be matched, - //handle_exception returns false. Otherwise the matched function is invoked with - //the corresponding available error objects. - exp.handle_exception( e, + auto propagate1 = leaf::preload( e_file_name{file_name} ); - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - if( errn.value==ENOENT ) - std::cerr << "File not found: " << fn.value << std::endl; - else - std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; - } + std::string buffer( 1+file_size(*f), '\0' ); + file_read(*f,&buffer[0],buffer.size()-1); - ); - return 2; - } - catch( input_error const & e ) - { - //In this case handle_exception is given 3 functions. It will first check if both - //e_file_name and e_errno, associated with e, are avialable in exp; if not, it will - //next check if just e_errno is available; and if not, the last function (which - //takes no arguments) will always match to print a generic error message. - exp.handle_exception( e, + auto propagate2 = leaf::defer([ ] { return e_errno{errno}; } ); + std::cout << buffer; + std::cout.flush(); - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Input error, " << fn.value << ", errno=" << errn << std::endl; - }, + return 0; + }, - [ ] ( e_errno const & errn ) - { - std::cerr << "Input error, errno=" << errn << std::endl; - }, + [ ]( leaf::catch_ ) + { + std::cout << "Bad command line argument" << std::endl; + return 1; + }, - [ ] - { - std::cerr << "Input error" << std::endl; - } + [ ]( leaf::catch_, leaf::match, e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 2; + }, - ); - return 3; - } - catch( std::ostream::failure const & e ) - { - //Report failure to write to std::cout, print the relevant errno. - exp.handle_exception( e, + [ ]( leaf::catch_, e_errno const & errn, e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 3; + }, - [ ] ( e_errno const & errn ) - { - std::cerr << "Output error, errno=" << errn << std::endl; - } + [ ]( leaf::catch_, e_errno const & errn, e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; + return 4; + }, - ); - return 4; - } - catch(...) - { - //This catch-all case helps diagnose logic errors (presumably, missing catch). - std::cerr << "Unknown error, cryptic information follows." << std::endl; - leaf::diagnostic_output_current_exception(std::cerr); - return 5; - } + [ ]( leaf::catch_, e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 5; + }, + + [ ]( leaf::error e ) + { + std::cerr << "Unknown error, cryptic information follows." << std::endl; + e.diagnostic_output(std::cerr); + return 6; + } ); } diff --git a/example/print_file_result.cpp b/example/print_file_result.cpp index 1948072..b3415f0 100644 --- a/example/print_file_result.cpp +++ b/example/print_file_result.cpp @@ -8,7 +8,8 @@ //additional information when using result to report failures. See print_file_eh.cpp //for the variant that uses exception handling. -#include +#include +#include #include #include #include @@ -22,17 +23,17 @@ using leaf::e_file_name; using leaf::e_errno; -//Error codes -enum error_code +enum error_codes { - input_file_open_error=1, + bad_command_line = 1, + input_file_open_error, input_file_size_error, input_file_read_error, input_eof_error, cout_error }; namespace boost { namespace leaf { - template<> struct is_error_type: std::true_type { }; + template<> struct is_error_type: std::true_type { }; } } @@ -76,132 +77,76 @@ leaf::result file_read( FILE & f, void * buf, int size ) } -leaf::result print_file( char const * file_name ) +leaf::result parse_command_line( int argc, char const * argv[ ] ) { - LEAF_AUTO(f,file_open(file_name)); - - auto propagate = leaf::preload( e_file_name{file_name} ); - - LEAF_AUTO(s,file_size(*f)); - - std::string buffer( 1+s, '\0' ); - LEAF_CHECK(file_read(*f,&buffer[0],buffer.size()-1)); - - std::cout << buffer; - std::cout.flush(); - if( std::cout.fail() ) - return leaf::error( cout_error ); - - return { }; -} - - -char const * parse_command_line( int argc, char const * argv[ ] ) -{ - if( argc!=2 ) - return 0; - else + if( argc==2 ) return argv[1]; + else + return leaf::error(bad_command_line); } int main( int argc, char const * argv[ ] ) { - char const * fn = parse_command_line(argc,argv); - if( !fn ) - { - std::cout << "Bad command line argument" << std::endl; - return 1; - } + return leaf::handle_all( - //We expect error_code, e_file_name and e_errno objects to be associated - //with errors handled in this function. They will be stored inside of exp. - leaf::expect exp; - - if( auto r = print_file(fn) ) - { - return 0; //Success, we're done! - } - else - { - //Probe exp for the error_code object associated with the error stored in r. - switch( auto ec = *exp.peek(r) ) + [&]() -> leaf::result { - case input_file_open_error: - { - //handle_error takes a list of functions (in this case only one). It attempts to - //match each function (in order) to objects currently available in exp, which - //are associated with the error value stored in r. If no function can be matched, - //handle_error returns false. Otherwise the matched function is invoked with - //the matching corresponding error objects. - bool matched = exp.handle_error( r, + LEAF_AUTO(file_name, parse_command_line(argc,argv)); - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - if( errn.value==ENOENT ) - std::cerr << "File not found: " << fn.value << std::endl; - else - std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; - } + LEAF_AUTO(f, file_open(file_name)); - ); - assert(matched); - return 2; - } + auto propagate1 = leaf::preload( e_file_name{file_name} ); - case input_file_size_error: - case input_file_read_error: - case input_eof_error: - { - //In this case handle_error is given 3 functions. It will first check if both - //e_file_name and e_errno, associated with r, are avialable in exp; if not, it will - //next check if just e_errno is available; and if not, the last function (which - //takes no arguments) will always match to print a generic error message. - bool matched = exp.handle_error( r, + LEAF_AUTO(s, file_size(*f)); - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; - }, + std::string buffer( 1+s, '\0' ); + LEAF_CHECK(file_read(*f,&buffer[0],buffer.size()-1)); - [ ] ( e_errno const & errn ) - { - std::cerr << "I/O error, errno=" << errn << std::endl; - }, + auto propagate2 = leaf::defer([ ] { return e_errno{errno}; } ); + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + return leaf::error( cout_error ); - [ ] - { - std::cerr << "I/O error" << std::endl; - } + return 0; + }, - ); - assert(matched); - return 3; - } + [ ]( leaf::match ) + { + std::cout << "Bad command line argument" << std::endl; + return 1; + }, - case cout_error: - { - //Report failure to write to std::cout, print the relevant errno. - bool matched = exp.handle_error( r, + [ ]( leaf::match, leaf::match, e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 2; + }, - [ ] ( e_errno const & errn ) - { - std::cerr << "Output error, errno=" << errn << std::endl; - } + [ ]( leaf::match, e_errno const & errn, e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 3; + }, - ); - assert(matched); - return 4; - } + [ ]( leaf::match, e_errno const & errn, e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; + return 4; + }, - //This catch-all case helps diagnose logic errors (presumably, missing case labels - //in the switch statement). - default: - { - std::cerr << "Unknown error code " << ec << ", cryptic information follows." << std::endl; //<7> - r.diagnostic_output(std::cerr); - return 5; - } + [ ]( leaf::match, e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 5; + }, + + [ ]( leaf::error e ) + { + std::cerr << "Unknown error, cryptic information follows." << std::endl; + e.diagnostic_output(std::cerr); + return 6; } - } + ); } diff --git a/example/print_half.cpp b/example/print_half.cpp index 201d293..87b566d 100644 --- a/example/print_half.cpp +++ b/example/print_half.cpp @@ -7,8 +7,7 @@ //This program is an adaptation of the following Boost Outcome example: //https://github.com/ned14/outcome/blob/master/doc/src/snippets/using_result.cpp -#include -#include +#include #include #include #include @@ -41,7 +40,7 @@ leaf::result convert(const std::string& str) noexcept return atoi(str.c_str()); } -//Do not expect BigInt to actually work -- it's a stub. +//Do not static_store BigInt to actually work -- it's a stub. struct BigInt { static leaf::result fromString(const std::string& s) { return BigInt{s}; } @@ -53,58 +52,49 @@ struct BigInt //This function handles ConversionErrc::TooLong errors, forwards any other error to the caller. leaf::result print_half(const std::string& text) { - leaf::expect exp; - if (leaf::result r = convert(text)) - { - std::cout << (r.value() / 2) << std::endl; - return { }; - } - else - { - return exp.handle_error( r, - [&]( ConversionErrc ec ) -> leaf::result - { - if( ec != ConversionErrc::TooLong ) - return r.error(); - LEAF_AUTO(i, BigInt::fromString(text)); - std::cout << i.half() << std::endl; - return { }; - } ); - } + return leaf::handle_some( + [&]() -> leaf::result + { + LEAF_AUTO(r,convert(text)); + std::cout << r / 2 << std::endl; + return { }; + }, + [&]( leaf::match ) -> leaf::result + { + LEAF_AUTO(i, BigInt::fromString(text)); + std::cout << i.half() << std::endl; + return { }; + } ); } int main( int argc, char const * argv[ ] ) { - leaf::expect exp; - if( leaf::result r = print_half(argc<2 ? "" : argv[1]) ) - { - std::cout << "ok" << std::endl; - return 0; - } - else - { - return exp.handle_error( r, - [ ]( ConversionErrc ec ) - { - switch(ec) - { - case ConversionErrc::EmptyString: - std::cerr << "Empty string!" << std::endl; - break; - default: - assert(ec==ConversionErrc::IllegalChar); //Because print_half deals with ConversionErrc::TooLong. - std::cerr << "Illegal char!" << std::endl; - } - return 1; - }, - [&r] - { - //This will never execute in this program, but it would detect logic errors where an unknown error reaches main. - //In this case, we print diagnostic information. Consider using leaf::unexpected_diagnostic_output in the - //definition of exp. - std::cerr << "Unknown error, cryptic diagnostic information follows." << std::endl; - r.diagnostic_output(std::cerr); - return 2; - } ); - } + return leaf::handle_all( + [&]() -> leaf::result + { + LEAF_CHECK( print_half(argc<2 ? "" : argv[1]) ); + std::cout << "ok" << std::endl; + return 0; + }, + + [ ]( leaf::match ) + { + std::cerr << "Empty string!" << std::endl; + return 1; + }, + + [ ]( leaf::match ) + { + std::cerr << "Illegal char!" << std::endl; + return 2; + }, + + [ ]( leaf::error e ) + { + //This will never execute in this program, but it would detect logic errors where an unknown error reaches main. + //In this case, we print diagnostic information. + std::cerr << "Unknown error, cryptic diagnostic information follows." << std::endl; + e.diagnostic_output(std::cerr); + return 3; + } ); } diff --git a/example/return_exception.cpp b/example/return_exception.cpp deleted file mode 100644 index 88e3184..0000000 --- a/example/return_exception.cpp +++ /dev/null @@ -1,120 +0,0 @@ -//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 - { - exp.handle_error( 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. - [ ]( std::exception_ptr const & ep ) - { - assert(ep); - try - { - std::rethrow_exception(ep); - } - catch(...) - { - leaf::diagnostic_output_current_exception(std::cerr); - } - } - - ); - } - } - return 0; -} diff --git a/include/boost/leaf/all.hpp b/include/boost/leaf/all.hpp index 1e2bd5f..064d807 100644 --- a/include/boost/leaf/all.hpp +++ b/include/boost/leaf/all.hpp @@ -7,14 +7,15 @@ //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) +#include +#include #include -#include -#include #include -#include -#include -#include +#include +#include #include #include +#include +#include #endif diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp new file mode 100644 index 0000000..3bef4e7 --- /dev/null +++ b/include/boost/leaf/capture_exception.hpp @@ -0,0 +1,116 @@ +#ifndef BOOST_LEAF_BC24FB98B2DE11E884419CF5AD35F1A2 +#define BOOST_LEAF_BC24FB98B2DE11E884419CF5AD35F1A2 + +//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) + +#include +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + class captured_exception_impl: + public captured_exception, + public leaf::error + { + std::exception_ptr ex_; + std::shared_ptr cap_; + bool had_error_; + + public: + + captured_exception_impl( std::exception_ptr && ex, std::shared_ptr && cap, bool had_error ) noexcept: + error(cap->get_error()), + ex_(std::move(ex)), + cap_(std::move(cap)), + had_error_(had_error) + { + assert(ex_); + } + + [[noreturn]] void unload_and_rethrow_original_exception() + { + std::shared_ptr ds; ds.swap(cap_); + assert(ds); + if( had_error_ ) + { + error err = ds->unload(); + assert(err==*this); + } + else + ds->unload(next_error_value()); + std::rethrow_exception(ex_); + } + + void diagnostic_output( std::ostream & os, void (*printer)(std::ostream &) ) const + { + assert(cap_); + assert(printer!=0); + os << "Detected exception_capture" << std::endl; + cap_->diagnostic_output(os); + os << std::endl << "Diagnostic Information about the original exception follows" << std::endl; + try + { + std::rethrow_exception(ex_); + } + catch(...) + { + printer(os); + } + } + }; + + //////////////////////////////////////// + + template + class exception_trap + { + F f_; + + public: + + explicit exception_trap( F && f ) noexcept: + f_(std::move(f)) + { + } + + template + decltype(std::declval()(std::declval()...)) operator()( A && ... a ) const + { + static_store ss; + ss.set_reset(true); + try + { + return f_(std::forward(a)...); + } + catch( captured_exception const & ) + { + throw; + } + catch( error const & e ) + { + throw captured_exception_impl( std::current_exception(), std::make_shared>(e,std::move(ss)), true ); + } + catch(...) + { + throw captured_exception_impl( std::current_exception(), std::make_shared>(error(),std::move(ss)), false ); + } + } + }; + } //leaf_detail + + template + leaf_detail::exception_trap capture_exception( F && f ) noexcept + { + return leaf_detail::exception_trap(std::move(f)); + } + +} } + +#endif diff --git a/include/boost/leaf/capture_result.hpp b/include/boost/leaf/capture_result.hpp new file mode 100644 index 0000000..c122f3e --- /dev/null +++ b/include/boost/leaf/capture_result.hpp @@ -0,0 +1,62 @@ +#ifndef BOOST_LEAF_2416C558123711E9B9D9691F8C7F4AFC +#define BOOST_LEAF_2416C558123711E9B9D9691F8C7F4AFC + +//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) + +#include +#include + +namespace boost { namespace leaf { + + template + result::result( std::shared_ptr && cap ) noexcept: + cap_(std::move(cap)), + which_(leaf_detail::result_variant::cap) + { + } + + inline result::result( std::shared_ptr && cap ) noexcept: + base(std::move(cap)) + { + } + + namespace leaf_detail + { + template + class result_trap + { + F f_; + + public: + + explicit result_trap( F && f ) noexcept: + f_(std::move(f)) + { + } + + template + decltype(std::declval()(std::declval()...)) operator()( A && ... a ) const noexcept + { + static_store ss; + ss.set_reset(true); + if( auto r = f_(std::forward(a)...) ) + return r; + else + return decltype(r)( std::make_shared>(r.error(),std::move(ss)) ); + } + }; + } + + template + leaf_detail::result_trap capture_result( F && f ) noexcept + { + return leaf_detail::result_trap(std::move(f)); + } + +} } + +#endif diff --git a/include/boost/leaf/detail/captured_exception.hpp b/include/boost/leaf/detail/captured_exception.hpp new file mode 100644 index 0000000..7ef9f8f --- /dev/null +++ b/include/boost/leaf/detail/captured_exception.hpp @@ -0,0 +1,39 @@ +#ifndef BOOST_LEAF_E32F3CCC139011E995085E318C7F4AFC +#define BOOST_LEAF_E32F3CCC139011E995085E318C7F4AFC + +//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) + +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + class captured_exception: + public std::exception + { + protected: + + captured_exception() noexcept + { + } + + ~captured_exception() noexcept + { + } + + public: + + [[noreturn]] virtual void unload_and_rethrow_original_exception() = 0; + virtual void diagnostic_output( std::ostream &, void (*)(std::ostream &) ) const = 0; + }; + } + +} } + +#endif diff --git a/include/boost/leaf/detail/dynamic_store.hpp b/include/boost/leaf/detail/dynamic_store.hpp new file mode 100644 index 0000000..a67a333 --- /dev/null +++ b/include/boost/leaf/detail/dynamic_store.hpp @@ -0,0 +1,41 @@ +#ifndef BOOST_LEAF_6CCC5F56124B11E9B6C4CB8C8C7F4AFC +#define BOOST_LEAF_6CCC5F56124B11E9B6C4CB8C8C7F4AFC + +//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) + +#include + +namespace boost { namespace leaf { + + class error; + + namespace leaf_detail + { + class dynamic_store + { + protected: + + dynamic_store() noexcept + { + } + + ~dynamic_store() noexcept + { + } + + public: + + virtual error get_error() const noexcept = 0; + virtual error unload() noexcept = 0; + virtual error unload( error const & ) noexcept = 0; + virtual void diagnostic_output( std::ostream & ) const = 0; + }; + } + +} } + +#endif diff --git a/include/boost/leaf/detail/dynamic_store_impl.hpp b/include/boost/leaf/detail/dynamic_store_impl.hpp new file mode 100644 index 0000000..21ba532 --- /dev/null +++ b/include/boost/leaf/detail/dynamic_store_impl.hpp @@ -0,0 +1,91 @@ +#ifndef BOOST_LEAF_C86E4C4ED0F011E8BB777EB8A659E189 +#define BOOST_LEAF_C86E4C4ED0F011E8BB777EB8A659E189 + +//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) + +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + namespace dynamic_store_internal + { + template + struct tuple_for_each + { + static void unload( error const & e, Tuple && tup ) noexcept + { + tuple_for_each::unload(e,std::move(tup)); + auto && opt = std::get(std::move(tup)); + if( opt.has_value() ) + (void) e.propagate(std::move(opt).value()); + } + + static void print( std::ostream & os, Tuple const & tup ) + { + typedef typename std::tuple_element::type ith_type; + tuple_for_each::print(os,tup); + auto & opt = std::get(tup); + if( opt.has_value() && diagnostic::print(os,opt.value()) ) + os << std::endl; + } + }; + + template + struct tuple_for_each<0, Tuple> + { + static void unload( error const &, Tuple && ) noexcept { } + static void print( std::ostream &, Tuple const & ) noexcept { } + }; + } + + template + class dynamic_store_impl: + public dynamic_store + { + error e_; + std::tuple...> s_; + + error get_error() const noexcept + { + return e_; + } + + error unload() noexcept + { + return unload(e_); + } + + error unload( error const & e ) noexcept + { + dynamic_store_internal::tuple_for_each::unload(e,std::move(s_)); + return e; + } + + void diagnostic_output( std::ostream & os ) const + { + os << "Types being captured: "; + print_types::print(os); + os << std::endl << "Values actually captured:" << std::endl; + dynamic_store_internal::tuple_for_each::print(os,s_); + } + + public: + + dynamic_store_impl( error const & e, static_store && ss ) noexcept: + e_(e), + s_(std::make_tuple( std::get,decltype(ss.s_)>::value>(std::move(ss.s_)).extract_optional(e)... )) + { + } + }; + } //leaf_detail + +} } + +#endif diff --git a/include/boost/leaf/detail/function_traits.hpp b/include/boost/leaf/detail/function_traits.hpp index 91f9a4a..7dca769 100644 --- a/include/boost/leaf/detail/function_traits.hpp +++ b/include/boost/leaf/detail/function_traits.hpp @@ -7,8 +7,8 @@ //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) +#include #include -#include namespace boost { namespace leaf { @@ -17,10 +17,6 @@ namespace boost { namespace leaf { template using void_t = void; - template struct mp_list { }; - template struct mp_rest { }; - template struct mp_rest> { using type = mp_list; }; - template struct function_traits { @@ -30,11 +26,15 @@ namespace boost { namespace leaf { struct function_traits> { private: + typedef function_traits tr; + public: + typedef typename tr::return_type return_type; static constexpr int arity = tr::arity - 1; - using mp_args = typename mp_rest::type; + + using mp_args = typename leaf_detail_mp11::mp_rest; template struct arg: @@ -49,14 +49,13 @@ namespace boost { namespace leaf { typedef R return_type; static constexpr int arity = sizeof...(A); - using mp_args = mp_list; + using mp_args = leaf_detail_mp11::mp_list; template struct arg { static_assert(I < arity, "I out of range"); typedef typename std::tuple_element>::type type; - typedef typename std::remove_cv::type>::type type_; }; }; diff --git a/include/boost/leaf/detail/handle_error.hpp b/include/boost/leaf/detail/handle_error.hpp deleted file mode 100644 index 0339449..0000000 --- a/include/boost/leaf/detail/handle_error.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef BOOST_LEAF_F55070940BFF11E9A3EB73FBF47612F3 -#define BOOST_LEAF_F55070940BFF11E9A3EB73FBF47612F3 - -//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) - -#include -#include -#include - -namespace boost { namespace leaf { - - namespace leaf_detail - { - template ::value> - struct unhandled_error_base_helper; - - template - struct unhandled_error_base_helper - { - static constexpr R value( leaf::error const & ) noexcept { return static_cast(-1); } - }; - - template - struct unhandled_error_base_helper - { - static constexpr R value( leaf::error const & ) noexcept { return R(); } - }; - - template - struct unhandled_error_base: unhandled_error_base_helper - { - }; - - template <> - struct unhandled_error_base - { - static constexpr bool value( leaf::error const & ) noexcept { return false; } - }; - } - - template - struct uhnandled_error: leaf_detail::unhandled_error_base - { - }; - - namespace leaf_detail - { - template ::return_type> - struct handler_wrapper - { - typedef ReturnType return_type; - - F f_; - - explicit handler_wrapper( F && f ) noexcept: - f_(std::forward(f)) - { - } - - template - return_type operator()( E && ... e ) const - { - return f_(std::forward(e)...); - } - }; - - template - struct handler_wrapper - { - typedef bool return_type; - - F f_; - - explicit handler_wrapper( F && f ) noexcept: - f_(std::forward(f)) - { - } - - template - return_type operator()( E && ... e ) const - { - f_(std::forward(e)...); - return true; - } - }; - - template - struct handler_pack_return_type; - - template - struct handler_pack_return_type - { - typedef typename handler_wrapper::return_type return_type; - }; - } //leaf_detail - -} } - -#endif diff --git a/include/boost/leaf/detail/mp11.hpp b/include/boost/leaf/detail/mp11.hpp new file mode 100644 index 0000000..8a19f8a --- /dev/null +++ b/include/boost/leaf/detail/mp11.hpp @@ -0,0 +1,211 @@ +#ifndef BOOST_LEAF_91843B04108711E9AA4E56D98C7F4AFC +#define BOOST_LEAF_91843B04108711E9AA4E56D98C7F4AFC + +// Copyright 2015-2017 Peter Dimov. +// Copyright 2019 Emil Dotchevski. +// +// 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 + +#include +#include + +namespace boost { namespace leaf { namespace leaf_detail_mp11 { + +// mp_list +template struct mp_list +{ +}; + +// mp_identity +template struct mp_identity +{ + using type = T; +}; + +// mp_inherit +template struct mp_inherit: T... {}; + +// mp_if, mp_if_c +namespace detail +{ + +template struct mp_if_c_impl +{ +}; + +template struct mp_if_c_impl +{ + using type = T; +}; + +template struct mp_if_c_impl +{ + using type = E; +}; + +} // namespace detail + +template using mp_if_c = typename detail::mp_if_c_impl::type; +template using mp_if = typename detail::mp_if_c_impl(C::value), T, E...>::type; + +// mp_bool +template using mp_bool = std::integral_constant; + +using mp_true = mp_bool; +using mp_false = mp_bool; + +// mp_to_bool +template using mp_to_bool = mp_bool( T::value )>; + +// mp_not +template using mp_not = mp_bool< !T::value >; + +// mp_int +template using mp_int = std::integral_constant; + +// mp_size_t +template using mp_size_t = std::integral_constant; + +// mp_set_contains +namespace detail +{ + +template struct mp_set_contains_impl; + +template class L, class... T, class V> struct mp_set_contains_impl, V> +{ + using type = mp_to_bool, mp_inherit...> > >; +}; + +} // namespace detail + +template using mp_set_contains = typename detail::mp_set_contains_impl::type; + +// mp_set_push_back +namespace detail +{ + +template struct mp_set_push_back_impl; + +template class L, class... U> struct mp_set_push_back_impl> +{ + using type = L; +}; + +template class L, class... U, class T1, class... T> struct mp_set_push_back_impl, T1, T...> +{ + using S = mp_if, T1>, L, L>; + using type = typename mp_set_push_back_impl::type; +}; + +} // namespace detail + +template using mp_set_push_back = typename detail::mp_set_push_back_impl::type; + +// mp_unique +namespace detail +{ + +template struct mp_unique_impl; + +template class L, class... T> struct mp_unique_impl> +{ + using type = mp_set_push_back, T...>; +}; + +} // namespace detail + +template using mp_unique = typename detail::mp_unique_impl::type; + +// mp_append + +namespace detail +{ + +template struct mp_append_impl; + +template<> struct mp_append_impl<> +{ + using type = mp_list<>; +}; + +template class L, class... T> struct mp_append_impl> +{ + using type = L; +}; + +template class L1, class... T1, template class L2, class... T2, class... Lr> struct mp_append_impl, L2, Lr...> +{ + using type = typename mp_append_impl, Lr...>::type; +}; + +} + +template using mp_append = typename detail::mp_append_impl::type; + +// mp_front +namespace detail +{ + +template struct mp_front_impl +{ +// An error "no type named 'type'" here means that the argument to mp_front +// is either not a list, or is an empty list +}; + +template class L, class T1, class... T> struct mp_front_impl> +{ + using type = T1; +}; + +} // namespace detail + +template using mp_front = typename detail::mp_front_impl::type; + +// mp_pop_front +namespace detail +{ + +template struct mp_pop_front_impl +{ +// An error "no type named 'type'" here means that the argument to mp_pop_front +// is either not a list, or is an empty list +}; + +template class L, class T1, class... T> struct mp_pop_front_impl> +{ + using type = L; +}; + +} // namespace detail + +template using mp_pop_front = typename detail::mp_pop_front_impl::type; + +// mp_first +template using mp_first = mp_front; + +// mp_rest +template using mp_rest = mp_pop_front; + +// mp_remove_if +namespace detail +{ + +template class P> struct mp_remove_if_impl; + +template class L, class... T, template class P> struct mp_remove_if_impl, P> +{ + template using _f = mp_if, mp_list<>, mp_list>; + using type = mp_append, _f...>; +}; + +} // namespace detail + +template class P> using mp_remove_if = typename detail::mp_remove_if_impl::type; + +} } } + +#endif diff --git a/include/boost/leaf/detail/print.hpp b/include/boost/leaf/detail/print.hpp index cf7876a..05a1b7b 100644 --- a/include/boost/leaf/detail/print.hpp +++ b/include/boost/leaf/detail/print.hpp @@ -50,30 +50,50 @@ namespace boost { namespace leaf { namespace leaf_detail { - template - struct is_printable + template + struct print_types; + + template + struct print_types { - static constexpr bool value=false; + static void print( std::ostream & os ) + { + os << type(); + } }; - template - struct is_printable()<(), void())> + template + struct print_types { - static constexpr bool value=true; + static void print( std::ostream & os ) + { + os << type() << ", "; + print_types::print(os); + } }; //////////////////////////////////////// template - struct has_printable_member_value + struct is_printable: std::false_type { - static constexpr bool value=false; }; template - struct has_printable_member_value()<().value, void())> + struct is_printable()<(), void())>: std::true_type + { + }; + + //////////////////////////////////////// + + template + struct has_printable_member_value: std::false_type + { + }; + + template + struct has_printable_member_value()<().value, void())>: std::true_type { - static constexpr bool value=true; }; //////////////////////////////////////// @@ -110,6 +130,15 @@ namespace boost { namespace leaf { return true; } }; + + template <> + struct diagnostic + { + static bool print( std::ostream & os, std::exception_ptr const & ) + { + return false; + } + }; } //leaf_detail } } diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp new file mode 100644 index 0000000..c731b58 --- /dev/null +++ b/include/boost/leaf/detail/static_store.hpp @@ -0,0 +1,465 @@ +#ifndef BOOST_LEAF_AFBBD676B2FF11E8984C7976AE35F1A2 +#define BOOST_LEAF_AFBBD676B2FF11E8984C7976AE35F1A2 + +//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) + +#include +#include +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + template ::value> + struct match_type; + + template + struct match_type + { + using type = decltype(E::value); + + static type const & get( E const & e ) noexcept + { + return e.value; + } + }; + + template + struct match_type + { + using type = E; + + static type const & get( E const & e ) noexcept + { + return e; + } + }; + } + + template ::type... Value> + struct match + { + typename leaf_detail::match_type::type value; + }; + + template + struct catch_ + { + }; + + namespace leaf_detail + { + namespace static_store_internal + { + template + struct tuple_for_each + { + static void reset( Tuple & tup ) noexcept + { + tuple_for_each::reset(tup); + std::get(tup).reset(); + } + }; + + template + struct tuple_for_each<0, Tuple> + { + static void reset( Tuple & ) noexcept { } + }; + + //////////////////////////////////////// + + class enable_any + { + protected: + + enable_any() noexcept + { + ++tl_unexpected_enabled_counter(); + } + + ~enable_any() noexcept + { + --tl_unexpected_enabled_counter(); + } + }; + + template + class static_store_slot: + public slot + { + public: + optional extract_optional( error const & e ) && noexcept + { + slot const & s = *this; + if( s.has_value() && s.value().e==e ) + return optional(std::move(*this).value().v); + else + return optional(); + } + }; + + template <> + class static_store_slot: + public slot, + enable_any + { + }; + + template <> + class static_store_slot: + public slot, + enable_any + { + }; + + //////////////////////////////////////// + + template + struct type_index; + + template + struct type_index + { + static const int value = 0; + }; + + template + struct type_index + { + static const int value = 1 + type_index::value; + }; + + template + struct tuple_type_index; + + template + struct tuple_type_index> + { + static const int value = type_index::value; + }; + + //////////////////////////////////////// + + template + bool check_value_pack( T const & x, T const & v ) noexcept + { + return x==v; + } + + template + bool check_value_pack( T const & x, T const & v1, VRest const & ... v_rest ) noexcept + { + return x==v1 || check_value_pack(x,v_rest...); + } + + template + bool check_exception_pack( std::exception const * stdx, Ex const * ) noexcept + { + return dynamic_cast(stdx)!=0; + } + + template + bool check_exception_pack( std::exception const * stdx, Ex const *, ExRest const * ... ex_rest ) noexcept + { + return dynamic_cast(stdx)!=0 || check_exception_pack(stdx, ex_rest...); + } + + template + struct check_one_argument + { + static bool check( SlotsTuple const & tup, error const & e, std::exception const * ) noexcept + { + auto & sl = std::get,SlotsTuple>::value>(tup); + return sl.has_value() && sl.value().e==e; + } + }; + + template + struct check_one_argument + { + static bool check( SlotsTuple const &, error const &, std::exception const * ) noexcept + { + return true; + } + }; + + template ::type... Value> + struct check_one_argument> + { + static bool check( SlotsTuple const & tup, error const & e, std::exception const * ) noexcept + { + auto & sl = std::get,SlotsTuple>::value>(tup); + if( sl.has_value() ) + { + auto const & v = sl.value(); + return v.e==e && check_value_pack(match_type::get(v.v),Value...); + } + else + return false; + } + }; + + template + struct check_one_argument> + { + static bool check( SlotsTuple const &, error const &, std::exception const * ex ) noexcept + { + assert(ex!=0); + return check_exception_pack(ex,static_cast(0)...); + } + }; + + template + struct check_one_argument + { + static constexpr bool check( SlotsTuple const &, error const &, std::exception const * ) + { + return true; + } + }; + + template + struct check_arguments; + + template + struct check_arguments + { + static bool check( SlotsTuple const & tup, error const & e, std::exception const * ex ) noexcept + { + return check_one_argument::check(tup,e,ex) && check_arguments::check(tup,e,ex); + } + }; + + template + struct check_arguments + { + static constexpr bool check( SlotsTuple const &, error const &, std::exception const * ) noexcept + { + return true; + } + }; + + //////////////////////////////////////// + + template + struct get_one_argument + { + template + static T const & get( StaticStore const & ss, error const & e ) noexcept + { + T const * arg = ss.template peek(e); + assert(arg!=0); + return *arg; + } + }; + + template + struct get_one_argument + { + template + static T const * get( StaticStore const & ss, error const & e ) noexcept + { + return ss.template peek(e); + } + }; + + template ::type... Value> + struct get_one_argument> + { + template + static match get( StaticStore const & ss, error const & e ) noexcept + { + E const * arg = ss.template peek(e); + assert(arg!=0); + return match{match_type::get(*arg)}; + } + }; + + template + struct get_one_argument> + { + template + static constexpr catch_ get( StaticStore const &, error const & ) noexcept + { + return { }; + } + }; + + template <> + struct get_one_argument + { + template + static error get( StaticStore const &, error const & e ) noexcept + { + return e; + } + }; + + //////////////////////////////////////// + + template struct acceptable_last_handler_argument: std::false_type { }; + template struct acceptable_last_handler_argument: is_error_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; + + template + struct ensure_last_handler_matches: std::false_type + { + }; + + template class L, class Car, class... Cdr> + struct ensure_last_handler_matches> + { + constexpr static bool value = acceptable_last_handler_argument::value && ensure_last_handler_matches>::value; + }; + + template class L> + struct ensure_last_handler_matches>: std::true_type + { + }; + } + + template + class dynamic_store_impl; + + template + class static_store + { + template + friend class dynamic_store_impl; + + static_store( static_store const & ) = delete; + static_store & operator=( static_store const & ) = delete; + + std::tuple...> s_; + bool reset_; + + template + bool check_handler( error const & e, std::exception const * ex, leaf_detail_mp11::mp_list ) const noexcept + { + using namespace static_store_internal; + return check_arguments::type>::type...>::check(s_,e,ex); + } + + template + typename function_traits::return_type call_handler( error const & e, std::exception const *, F && f, leaf_detail_mp11::mp_list ) const + { + using namespace static_store_internal; + return std::forward(f)( get_one_argument::type>::type>::get(*this,e)... ); + } + + public: + + constexpr explicit static_store() noexcept: + reset_(false) + { + } + + ~static_store() noexcept + { + if( reset_&& !std::uncaught_exception() ) + static_store_internal::tuple_for_each::reset(s_); + } + + void set_reset( bool r ) noexcept + { + reset_ = r; + } + + template + P const * peek( error const & e ) const noexcept + { + auto & opt = std::get::value>(s_); + if( opt.has_value() ) + { + auto & x = opt.value(); + if( x.e==e ) + return &x.v; + } + return 0; + } + + template + typename function_traits::return_type handle_error( error const & e, std::exception const * ex, F && f ) const + { + using namespace static_store_internal; + static_assert(ensure_last_handler_matches::mp_args>::value, + "The last handler for handle_all may only take arguments of type error, e_unexpected, e_unexpected_diagnostic_output or any number of pointer-to-const arguments."); + return call_handler( e, ex, std::forward(f), typename function_traits::mp_args{ } ); + } + + template + typename function_traits::return_type handle_error( error const & e, std::exception const * ex, CarF && car_f, CdarF && cdar_f, CddrF && ... cddr_f ) const + { + using namespace static_store_internal; + if( check_handler( e, ex, typename function_traits::mp_args{ } ) ) + return call_handler( e, ex, std::forward(car_f), typename function_traits::mp_args{ } ); + else + return handle_error( e, ex, std::forward(cdar_f), std::forward(cddr_f)...); + } + }; + + //Static store deduction + + template struct translate_expect_deduction { typedef T type; }; + template struct translate_expect_deduction { typedef T type; }; + template struct translate_expect_deduction { typedef T type; }; + template struct translate_expect_deduction { typedef T type; }; + template ::type... Value> struct translate_expect_deduction> { typedef E type; }; + template struct translate_expect_deduction> { typedef void type; }; + + template + struct translate_list_impl; + + template class L, class... T> + struct translate_list_impl> + { + using type = leaf_detail_mp11::mp_list::type...>; + }; + + template using translate_list = typename translate_list_impl::type; + + template struct does_not_participate_in_expect_deduction: std::false_type { }; + template <> struct does_not_participate_in_expect_deduction: std::true_type { }; + template <> struct does_not_participate_in_expect_deduction: std::true_type { }; + + template + struct handlers_args_set + { + using type = + leaf_detail_mp11::mp_remove_if< + leaf_detail_mp11::mp_unique< + translate_list< + leaf_detail_mp11::mp_append< + typename function_traits::mp_args... + > + > + >, + does_not_participate_in_expect_deduction + >; + }; + + template + struct deduce_static_store; + + template class L, class... T> + struct deduce_static_store> + { + typedef static_store type; + }; + } //leaf_detail + +} } + +#endif diff --git a/include/boost/leaf/diagnostic_output_current_exception.hpp b/include/boost/leaf/diagnostic_output_current_exception.hpp deleted file mode 100644 index 3ea08ca..0000000 --- a/include/boost/leaf/diagnostic_output_current_exception.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef BOOST_LEAF_17228D24F83C11E8AAC53F8F652D5A5F -#define BOOST_LEAF_17228D24F83C11E8AAC53F8F652D5A5F - -//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) - -#include -#include - -namespace boost { namespace leaf { - - template - void diagnostic_output_current_exception( std::ostream & os ) - { - os << "Current Exception Diagnostic Information:" << std::endl; - - try - { - throw; - } - catch( std::exception const & ex ) - { - os << - "Exception dynamic type: " << leaf_detail::demangle(typeid(ex).name()) << std::endl << - "std::exception::what(): " << ex.what() << std::endl; - } - catch( ... ) - { - os << "Unknown exception type (not a std::exception)" << std::endl; - } - - try - { - throw; - } - catch( leaf_detail::captured_exception const & ce ) - { - ce.diagnostic_output(os); - } - catch( error const & e ) - { - e.diagnostic_output(os); - } - catch( ... ) - { - global_diagnostic_output(os); - } - } - -} } - -#endif diff --git a/include/boost/leaf/error_capture.hpp b/include/boost/leaf/error_capture.hpp deleted file mode 100644 index e61d084..0000000 --- a/include/boost/leaf/error_capture.hpp +++ /dev/null @@ -1,300 +0,0 @@ -#ifndef BOOST_LEAF_C86E4C4ED0F011E8BB777EB8A659E189 -#define BOOST_LEAF_C86E4C4ED0F011E8BB777EB8A659E189 - -//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) - -#include - -namespace boost { namespace leaf { - - class error_capture; - - namespace leaf_detail - { - template - struct all_available; - - template - struct all_available - { - static bool check( error_capture const & cap ) noexcept; - }; - - template <> - struct all_available<> - { - constexpr static bool check( error_capture const & ) noexcept { return true; } - }; - - //////////////////////////////////////// - - template - struct tuple_for_each_capture - { - static void const * dynamic_bind( Tuple const & tup, char const * (*type_id)() ) noexcept - { - assert(type_id!=0); - typedef typename std::tuple_element::type ith_type; - if( &type == type_id ) - return &std::get(tup); - return tuple_for_each_capture::dynamic_bind(tup,type_id); - } - - static void print( std::ostream & os, Tuple const & tup ) - { - typedef typename std::tuple_element::type ith_type; - tuple_for_each_capture::print(os,tup); - auto & opt = std::get(tup); - if( opt.has_value() && diagnostic::print(os,opt.value()) ) - os << std::endl; - } - - static void unload( error const & e, Tuple && tup ) noexcept - { - tuple_for_each_capture::unload(e,std::move(tup)); - auto && opt = std::get(std::move(tup)); - if( opt.has_value() ) - (void) e.propagate(std::move(opt).value()); - } - }; - - template - struct tuple_for_each_capture<0, Tuple> - { - static void const * dynamic_bind( Tuple const &, char const * (*)() ) noexcept { return 0; } - static void print( std::ostream &, Tuple const & ) noexcept { } - static void unload( error const &, Tuple && ) noexcept { } - }; - - } //leaf_detail - - //////////////////////////////////////// - - class error_capture - { - class dynamic_store - { - mutable std::atomic refcount_; - - virtual void const * bind_( char const * (*)() ) const noexcept = 0; - - protected: - - dynamic_store() noexcept: - refcount_(0) - { - } - - public: - - virtual ~dynamic_store() noexcept - { - } - - void addref() const noexcept - { - ++refcount_; - } - - void release() const noexcept - { - if( !--refcount_ ) - delete this; - } - - template - leaf_detail::optional const * bind() const noexcept - { - if( void const * p = bind_(&type) ) - return static_cast const *>(p); - else - return 0; - } - - virtual void diagnostic_output_( std::ostream & ) const = 0; - virtual void unload_( error const & ) noexcept = 0; - }; - - //////////////////////////////////////// - - template - class dynamic_store_impl: - public dynamic_store - { - std::tuple...> s_; - public: - - explicit dynamic_store_impl( std::tuple...> && s ) noexcept: - s_(std::move(s)) - { - } - - void const * bind_( char const * (*type_id)() ) const noexcept - { - using namespace leaf_detail; - assert(type_id!=0); - return tuple_for_each_capture...>>::dynamic_bind(s_,type_id); - } - - void diagnostic_output_( std::ostream & os ) const - { - leaf_detail::tuple_for_each_capture::print(os,s_); - } - - void unload_( error const & e ) noexcept - { - leaf_detail::tuple_for_each_capture::unload(e,std::move(s_)); - } - }; - - //////////////////////////////////////// - - void free() noexcept - { - if( ds_ ) - { - ds_->release(); - ds_=0; - } - } - - template - std::pair::return_type> check_handler_( F && f, leaf_detail::mp_list ) const - { - using namespace leaf_detail; - typedef typename handler_wrapper::return_type return_type; - if( leaf_detail::all_available::type>::type...>::check(*this) ) - return std::make_pair(true, handler_wrapper(std::forward(f))( *this->template peek::type>::type>()... )); - else - return std::make_pair(false, return_type(uhnandled_error::value(e_))); - } - - template - std::pair::return_type> find_handler_( F && f ) const - { - return check_handler_( std::forward(f), typename leaf_detail::function_traits::mp_args{ } ); - } - - template - std::pair::return_type> find_handler_( CarF && car_f, CdrF && ... cdr_f ) const - { - using namespace leaf_detail; - auto r = check_handler_( std::forward(car_f), typename leaf_detail::function_traits::mp_args{ } ); - return r.first ? r : find_handler_(std::forward(cdr_f)...); - } - - dynamic_store * ds_; - error e_; - - protected: - - void set_error( error const & e ) - { - e_ = e; - } - - public: - - error_capture() noexcept: - ds_(0) - { - } - - template - error_capture( error const & e, std::tuple...> && s ) noexcept: - ds_(new dynamic_store_impl(std::move(s))), - e_(e) - { - ds_->addref(); - } - - ~error_capture() noexcept - { - free(); - } - - error_capture( error_capture const & x ) noexcept: - ds_(x.ds_), - e_(x.e_) - { - if( ds_ ) - ds_->addref(); - } - - error_capture( error_capture && x ) noexcept: - ds_(x.ds_), - e_(std::move(x.e_)) - { - x.ds_ = 0; - } - - error_capture & operator=( error_capture const & x ) noexcept - { - ds_ = x.ds_; - ds_->addref(); - e_ = x.e_; - return *this; - } - - error_capture & operator=( error_capture && x ) noexcept - { - ds_ = x.ds_; - x.ds_ = 0; - e_ = x.e_; - return *this; - } - - explicit operator bool() const noexcept - { - return ds_!=0; - } - - error unload() noexcept - { - if( ds_ ) - { - ds_->unload_(e_); - free(); - } - return e_; - } - - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( F && ... f ) const noexcept - { - return this->find_handler_( std::forward(f)... ).second; - } - - template - P const * peek() const noexcept - { - if( *this ) - if( auto * opt = ds_->bind

() ) - if( opt->has_value() ) - return &opt->value(); - return 0; - } - - void diagnostic_output( std::ostream & os ) const - { - if( *this ) - ds_->diagnostic_output_(os); - } - }; - - namespace leaf_detail - { - template - bool all_available::check( error_capture const & cap ) noexcept - { - return cap.template peek() && all_available::check(cap); - } - } - -} } - -#endif diff --git a/include/boost/leaf/exception.hpp b/include/boost/leaf/exception.hpp deleted file mode 100644 index 9ec7d55..0000000 --- a/include/boost/leaf/exception.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef BOOST_LEAF_87F274C4D4BA11E89928D55AC82C3C47 -#define BOOST_LEAF_87F274C4D4BA11E89928D55AC82C3C47 - -//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) - -#include -#include -#include - -namespace boost { namespace leaf { - - inline error get_error( std::exception const & ex ) noexcept - { - if( auto e = dynamic_cast(&ex) ) - return *e; - else - return next_error_value(); - } - - template - template - P const * expect::peek( std::exception const & ex ) const noexcept - { - return this->template peek

(get_error(ex)); - } - - template - template - void expect::handle_exception( std::exception const & ex, F && ... f ) const - { - if( this->handle_error(get_error(ex),f...) ) - (void) error(); - else - throw; - } - - inline void diagnostic_output( std::ostream & os, std::exception const & ex ) - { - os << - "Exception dynamic type: " << leaf_detail::demangle(typeid(ex).name()) << std::endl << - "std::exception::what(): " << ex.what() << std::endl; - get_error(ex).diagnostic_output(os); - } - -} } - -#endif diff --git a/include/boost/leaf/exception_capture.hpp b/include/boost/leaf/exception_capture.hpp deleted file mode 100644 index 75c6c11..0000000 --- a/include/boost/leaf/exception_capture.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef BOOST_LEAF_BC24FB98B2DE11E884419CF5AD35F1A2 -#define BOOST_LEAF_BC24FB98B2DE11E884419CF5AD35F1A2 - -//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) - -#include -#include -#include - -namespace boost { namespace leaf { - - namespace leaf_detail - { - class captured_exception: - public std::exception, - error_capture - { - std::exception_ptr ex_; - bool has_error_; - - public: - - captured_exception( std::exception_ptr && ex, error_capture && cap, bool has_error ) noexcept: - error_capture(std::move(cap)), - ex_(std::move(ex)), - has_error_(has_error) - { - assert(ex_); - } - - [[noreturn]] - void rethrow_original_exception() - { - if( !has_error_ ) - { - set_error(next_error_value()); - has_error_ = true; - } - unload(); - std::rethrow_exception(ex_); - } - - void diagnostic_output( std::ostream & os ) const - { - static_cast(this)->diagnostic_output(os); - } - }; - - //////////////////////////////////////// - - template - class exception_trap - { - F f_; - - public: - - explicit exception_trap( F && f ) noexcept: - f_(std::move(f)) - { - } - - template - decltype(std::declval()(std::declval()...)) operator()( A && ... a ) - { - expect exp; - try - { - return f_(std::forward(a)...); - } - catch( error const & e ) - { - throw captured_exception(std::current_exception(),capture(exp,e),true); - } - catch(...) - { - throw captured_exception(std::current_exception(),capture(exp,error()),false); - } - } - }; - } //leaf_detail - - template - leaf_detail::exception_trap capture_exception( F && f ) noexcept - { - return leaf_detail::exception_trap(std::move(f)); - } - - template - decltype(std::declval().get()) get( Future && f ) - { - try - { - return std::forward(f).get(); - } - catch( leaf_detail::captured_exception & ex ) - { - ex.rethrow_original_exception(); - } - } - -} } - -#endif diff --git a/include/boost/leaf/exception_to_result.hpp b/include/boost/leaf/exception_to_result.hpp new file mode 100644 index 0000000..1c6d293 --- /dev/null +++ b/include/boost/leaf/exception_to_result.hpp @@ -0,0 +1,54 @@ +#ifndef BOOST_LEAF_017BF91412EB11E9926CDCED8B7F4AFC +#define BOOST_LEAF_017BF91412EB11E9926CDCED8B7F4AFC + +//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) + +#include +#include +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + template + leaf::result::return_type> catch_exceptions_helper( F && f, leaf_detail_mp11::mp_list<> ) + { + return std::forward(f)(); + } + + template + leaf::result::return_type> catch_exceptions_helper( F && f, leaf_detail_mp11::mp_list ) + { + try + { + return catch_exceptions_helper(std::forward(f),leaf_detail_mp11::mp_list{ }); + } + catch( Ex1 const & ex1 ) + { + return leaf::error(ex1); + } + } + } + + template + leaf::result::return_type> exception_to_result( F && f ) noexcept + { + try + { + return leaf_detail::catch_exceptions_helper(std::forward(f), leaf_detail_mp11::mp_list()); + } + catch(...) + { + return leaf::error(std::current_exception()); + } + }; + +} } + +#endif diff --git a/include/boost/leaf/expect.hpp b/include/boost/leaf/expect.hpp deleted file mode 100644 index 4b0b4b5..0000000 --- a/include/boost/leaf/expect.hpp +++ /dev/null @@ -1,246 +0,0 @@ -#ifndef BOOST_LEAF_AFBBD676B2FF11E8984C7976AE35F1A2 -#define BOOST_LEAF_AFBBD676B2FF11E8984C7976AE35F1A2 - -//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) - -#include -#include -#include - -namespace boost { namespace leaf { - - class error_capture; - - template - class result; - - namespace leaf_detail - { - template - class expect_slot: - public slot - { - }; - - class expect_slot_enable_unexpected - { - protected: - expect_slot_enable_unexpected() noexcept - { - ++tl_unexpected_enabled_counter(); - } - - ~expect_slot_enable_unexpected() noexcept - { - --tl_unexpected_enabled_counter(); - } - }; - - template <> - class expect_slot: - public slot, - expect_slot_enable_unexpected - { - }; - - template <> - class expect_slot: - public slot, - expect_slot_enable_unexpected - { - }; - - //////////////////////////////////////// - - template - struct type_index; - - template - struct type_index - { - static const int value = 0; - }; - - template - struct type_index - { - static const int value = 1 + type_index::value; - }; - - template - struct tuple_type_index; - - template - struct tuple_type_index> - { - static const int value = type_index::value; - }; - - //////////////////////////////////////// - - template - struct slots_subset; - - template - struct slots_subset - { - static bool have_values( SlotsTuple const & tup, error const & e ) noexcept - { - auto & sl = std::get::value>(tup); - return sl.has_value() && sl.value().e==e && slots_subset::have_values(tup,e); - } - }; - - template - struct slots_subset - { - static constexpr bool have_values( SlotsTuple const &, error const & ) noexcept { return true; } - }; - - //////////////////////////////////////// - - template - optional convert_optional( expect_slot && x, error const & e ) noexcept - { - if( x.has_value() && x.value().e==e ) - return optional(std::move(x).value().v); - else - return optional(); - } - - template - struct dependent_type - { - typedef leaf::error_capture error_capture; - }; - } //leaf_detail - - template - class expect; - - template - typename leaf_detail::dependent_type>::error_capture capture( expect &, error const & ); - - template - class expect - { - friend class error; - - template - friend typename leaf_detail::dependent_type>::error_capture leaf::capture( expect &, error const & ); - - expect( expect const & ) = delete; - expect & operator=( expect const & ) = delete; - - std::tuple...> s_; - - template - std::pair::return_type> check_handler_( error const & e, F && f, leaf_detail::mp_list ) const - { - using namespace leaf_detail; - typedef typename handler_wrapper::return_type return_type; - if( slots_subset::type>::type>...>::have_values(s_,e) ) - return std::make_pair(true, handler_wrapper(std::forward(f))( *peek::type>::type>(e)... )); - else - return std::make_pair(false, uhnandled_error::value(e)); - } - - template - std::pair::return_type> find_handler_( error const & e, F && f ) const - { - return check_handler_( e, std::forward(f), typename leaf_detail::function_traits::mp_args{ } ); - } - - template - std::pair::return_type> find_handler_( error const & e, CarF && car_f, CdrF && ... cdr_f ) const - { - using namespace leaf_detail; - auto r = check_handler_( e, std::forward(car_f), typename leaf_detail::function_traits::mp_args{ } ); - return r.first ? r : find_handler_(e,std::forward(cdr_f)...); - } - - public: - - constexpr expect() noexcept - { - } - - ////////////////////////////////////////////// - - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( error const & e, F && ... f ) const noexcept - { - return this->find_handler_( e, std::forward(f)... ).second; - } - - template - P const * peek( error const & e ) const noexcept - { - auto & opt = std::get::value>(s_); - if( opt.has_value() ) - { - auto & x = opt.value(); - if( x.e==e ) - return &x.v; - } - return 0; - } - - ////////////////////////////////////////////// - - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( result const & r, F && ... f ) const noexcept - { - assert(!r); - if( r.which_ == leaf_detail::result_variant::err ) - return this->handle_error(r.err_,f...); - else - { - assert(r.which_==leaf_detail::result_variant::cap); - return r.cap_.handle_error(f...); - } - } - - template - P const * peek( result const & r ) const noexcept - { - assert(!r); - if( r.which_==leaf_detail::result_variant::err ) - return this->template peek

(r.err_); - else - { - assert(r.which_==leaf_detail::result_variant::cap); - return r.cap_.template peek

(); - } - } - - ////////////////////////////////////////////// - - template - P const * peek( std::exception const & ) const noexcept; - - template - void handle_exception( std::exception const &, F && ... ) const; - }; - - //////////////////////////////////////// - - template - typename leaf_detail::dependent_type>::error_capture capture( expect & exp, error const & e ) - { - using namespace leaf_detail; - typename leaf_detail::dependent_type>::error_capture cap( - e, - std::make_tuple( - convert_optional( - std::move(std::get,decltype(exp.s_)>::value>(exp.s_)),e)...)); - return cap; - } - -} } - -#endif diff --git a/include/boost/leaf/handle.hpp b/include/boost/leaf/handle.hpp new file mode 100644 index 0000000..177b01e --- /dev/null +++ b/include/boost/leaf/handle.hpp @@ -0,0 +1,84 @@ +#ifndef BOOST_LEAF_73685B76115611E9950D61678B7F4AFC +#define BOOST_LEAF_73685B76115611E9950D61678B7F4AFC + +//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) + +#include +#include + +namespace boost { namespace leaf { + + template + typename std::remove_reference::return_type>().value())>::type handle_all( TryBlock && try_block, Handlers && ... handlers ) noexcept + { + using namespace leaf_detail; + typename deduce_static_store::type>::type ss; + ss.set_reset(true); + if( auto r = std::forward(try_block)() ) + return *r; + else + return ss.handle_error(r.error(), 0, std::forward(handlers)...); + } + + namespace leaf_detail + { + template ::mp_args> + struct handler_wrapper; + + template class L, class... A> + struct handler_wrapper> + { + F f_; + explicit handler_wrapper( F && f ) noexcept: + f_(std::forward(f)) + { + } + R operator()( A... a ) noexcept + { + return f_(a...); + } + }; + + template class L, class... A> + struct handler_wrapper, F, L> + { + F f_; + explicit handler_wrapper( F && f ) noexcept: + f_(std::forward(f)) + { + } + result operator()( A... a ) noexcept + { + f_(a...); + return { }; + } + }; + } + + template + typename leaf_detail::function_traits::return_type handle_some( TryBlock && try_block, Handlers && ... handlers ) noexcept + { + using namespace leaf_detail; + using R = typename function_traits::return_type; + typename deduce_static_store::type>::type ss; + if( auto r = std::forward(try_block)() ) + { + ss.set_reset(true); + return r; + } + else if( auto rr = ss.handle_error(r.error(), 0, handler_wrapper(std::forward(handlers))..., [&r] { return r; } ) ) + { + ss.set_reset(true); + return rr; + } + else + return rr; + } + +} } + +#endif diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index 27c75b8..2c1fc3f 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -7,8 +7,8 @@ //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) -#include -#include +#include +#include #define LEAF_AUTO(v,r) auto _r_##v = r; if( !_r_##v ) return _r_##v.error(); auto & v = *_r_##v #define LEAF_CHECK(r) {auto _r = r; if( !_r ) return _r.error();} @@ -17,43 +17,21 @@ namespace boost { namespace leaf { class bad_result: public std::exception { }; - template - class expect; - - template - class result; - - namespace leaf_detail - { - template - struct unhandled_error_base> - { - static leaf::error value( leaf::error const & e ) noexcept { return e; } - }; - } - - template - result capture( expect &, result const & ); - //////////////////////////////////////// template class result { - template - friend class leaf::expect; + using dynamic_store_ptr = std::shared_ptr; template friend class result; - template - friend result leaf::capture( expect &, result const & ); - union { T value_; error err_; - error_capture cap_; + dynamic_store_ptr cap_; }; leaf_detail::result_variant which_; @@ -70,7 +48,7 @@ namespace boost { namespace leaf { break; default: assert(which_==leaf_detail::result_variant::cap); - cap_.~error_capture(); + cap_.~dynamic_store_ptr(); } which_= (leaf_detail::result_variant)-1; } @@ -88,7 +66,7 @@ namespace boost { namespace leaf { break; default: assert(x.which_==leaf_detail::result_variant::cap); - (void) new(&cap_) error_capture(x.cap_); + (void) new(&cap_) dynamic_store_ptr(x.cap_); }; which_ = x.which_; } @@ -100,19 +78,31 @@ namespace boost { namespace leaf { { case leaf_detail::result_variant:: value: (void) new(&value_) T(std::move(x.value_)); + which_ = x.which_; break; case leaf_detail::result_variant::err: (void) new(&err_) leaf::error(std::move(x.err_)); + which_ = x.which_; break; default: assert(x.which_==leaf_detail::result_variant::cap); - (void) new(&cap_) error_capture(std::move(x.cap_)); + if( dynamic_store_ptr cap = std::move(x.cap_) ) + { + x.destroy(); + (void) new(&x.err_) leaf::error(cap->get_error()); + x.which_ = leaf_detail::result_variant::err; + (void) new(&cap_) dynamic_store_ptr(std::move(cap)); + } + else + (void) new(&cap_) dynamic_store_ptr(std::move(x.cap_)); + which_ = leaf_detail::result_variant::cap; }; - which_ = x.which_; } public: + typedef T value_type; + ~result() noexcept { destroy(); @@ -164,17 +154,7 @@ namespace boost { namespace leaf { { } - result( leaf::error_capture const & cap ) noexcept: - cap_(cap), - which_(leaf_detail::result_variant::cap) - { - } - - result( leaf::error_capture && cap ) noexcept: - cap_(std::move(cap)), - which_(leaf_detail::result_variant::cap) - { - } + result( std::shared_ptr && ) noexcept; result & operator=( result const & x ) { @@ -230,9 +210,9 @@ namespace boost { namespace leaf { return leaf::error(std::forward(e)...); case leaf_detail::result_variant::cap: { - error_capture cap = cap_; + dynamic_store_ptr cap = cap_; destroy(); - (void) new(&err_) leaf::error(cap.unload()); + (void) new(&err_) leaf::error(cap->unload()); which_ = leaf_detail::result_variant::err; } default: @@ -240,18 +220,6 @@ namespace boost { namespace leaf { return err_.propagate(std::forward(e)...); } } - - void diagnostic_output( std::ostream & os ) const - { - assert(!(*this)); - if( which_==leaf_detail::result_variant::err ) - return err_.diagnostic_output(os); - else - { - assert(which_==leaf_detail::result_variant::cap); - return cap_.diagnostic_output(os); - } - } }; //////////////////////////////////////// @@ -260,17 +228,11 @@ namespace boost { namespace leaf { class result: result { - template - friend class leaf::expect; - - template - friend result leaf::capture( expect &, result const & ); + typedef result base; template friend class result; - typedef result base; - result( result && rb ): base(std::move(rb)) { @@ -278,6 +240,8 @@ namespace boost { namespace leaf { public: + typedef void value_type; + ~result() noexcept { } @@ -289,32 +253,22 @@ namespace boost { namespace leaf { { } - result( leaf::error_capture const & cap ) noexcept: - base(cap) + result( std::shared_ptr && ) noexcept; + + void value() const { + (void) base::value(); } - result( leaf::error_capture && cap ) noexcept: - base(std::move(cap)) + void operator*() const { + return value(); } using base::operator bool; using base::error; - using base::diagnostic_output; }; - //////////////////////////////////////// - - template - result capture( expect & exp, result const & r ) - { - if( r.which_==leaf_detail::result_variant::err ) - return capture(exp,r.err_); - else - return r; - } - } } #endif diff --git a/include/boost/leaf/detail/throw.hpp b/include/boost/leaf/throw.hpp similarity index 97% rename from include/boost/leaf/detail/throw.hpp rename to include/boost/leaf/throw.hpp index f89a5ab..a859556 100644 --- a/include/boost/leaf/detail/throw.hpp +++ b/include/boost/leaf/throw.hpp @@ -8,6 +8,7 @@ //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include #include #define LEAF_EXCEPTION(...) ::boost::leaf::leaf_detail::exception_(__FILE__,__LINE__,__FUNCTION__,__VA_ARGS__) @@ -15,8 +16,6 @@ namespace boost { namespace leaf { - //////////////////////////////////////// - namespace leaf_detail { inline void enforce_std_exception( std::exception const & ) { } diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp new file mode 100644 index 0000000..b20e5ba --- /dev/null +++ b/include/boost/leaf/try.hpp @@ -0,0 +1,146 @@ +#ifndef BOOST_LEAF_87F274C4D4BA11E89928D55AC82C3C47 +#define BOOST_LEAF_87F274C4D4BA11E89928D55AC82C3C47 + +//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) + +#include +#include +#include +#include + +namespace boost { namespace leaf { + + inline error get_error( std::exception const & ex ) noexcept + { + if( auto e = dynamic_cast(&ex) ) + return *e; + else + return next_error_value(); + } + + template + typename leaf_detail::function_traits::return_type try_( TryBlock && try_block, Handlers && ... handlers ) + { + using namespace leaf_detail; + typename deduce_static_store::type>::type ss; + ss.set_reset(true); + try + { + return std::forward(try_block)(); + } + catch( leaf_detail::captured_exception & cap ) + { + try + { + cap.unload_and_rethrow_original_exception(); + } + catch( std::exception const & ex ) + { + return ss.handle_error(get_error(ex), &ex, std::forward(handlers)..., [ ]() -> typename leaf_detail::function_traits::return_type { throw; }); + } + } + catch( std::exception const & ex ) + { + return ss.handle_error(get_error(ex), &ex, std::forward(handlers)..., [ ]() -> typename leaf_detail::function_traits::return_type { throw; }); + } + } + + namespace leaf_detail + { + inline void diagnostic_output_std_exception( std::ostream & os, std::exception const & ex ) + { + os << + "Exception dynamic type: " << leaf_detail::demangle(typeid(ex).name()) << std::endl << + "std::exception::what(): " << ex.what() << std::endl; + } + + inline void diagnostic_output_current_exception_no_capture( std::ostream & os ) + { + try + { + throw; + } + catch( std::exception const & ex ) + { + diagnostic_output_std_exception(os,ex); + } + catch( ... ) + { + os << "Unknown exception type (not a std::exception)" << std::endl; + } + try + { + throw; + } + catch( error const & e ) + { + e.diagnostic_output(os); + } + catch( ... ) + { + global_diagnostic_output(os); + } + } + + inline void diagnostic_output_current_exception_( std::ostream & os ) + { + try + { + try + { + throw; + } + catch( leaf_detail::captured_exception const & cap ) + { + cap.diagnostic_output( os, &leaf_detail::diagnostic_output_current_exception_no_capture ); + } + } + catch(...) + { + leaf_detail::diagnostic_output_current_exception_no_capture(os); + } + } + } //namespace leaf_detail + + inline void diagnostic_output_current_exception( std::ostream & os ) + { + os << "Current Exception Diagnostic Information:" << std::endl; + leaf_detail::diagnostic_output_current_exception_(os); + } + + inline void diagnostic_output( std::ostream & os, std::exception const & ex ) + { + if( leaf_detail::captured_exception const * cap = dynamic_cast(&ex) ) + cap->diagnostic_output( os, &leaf_detail::diagnostic_output_current_exception_no_capture ); + else + { + leaf_detail::diagnostic_output_std_exception(os,ex); + get_error(ex).diagnostic_output(os); + } + } + + inline void diagnostic_output( std::ostream & os, std::exception_ptr const & ep ) + { + if( ep ) + { + os << "std::exception_ptr Diagnostic Information:" << std::endl; + try + { + std::rethrow_exception(ep); + } + catch(...) + { + leaf_detail::diagnostic_output_current_exception_(os); + } + } + else + os << "Empty" << std::endl; + } + +} } + +#endif diff --git a/meson.build b/meson.build index 18dd219..914473f 100644 --- a/meson.build +++ b/meson.build @@ -14,6 +14,7 @@ if not meson.is_subproject() '-Wno-unused-variable', '-Wno-non-virtual-dtor', '-Wno-dangling-else', + '-Wno-delete-non-virtual-dtor', language:'cpp' ) endif endif @@ -26,55 +27,50 @@ leaf = declare_dependency( include_directories: includes ) tests = [ '_hpp_all_test', + '_hpp_capture_exception_test', + '_hpp_capture_result_test', '_hpp_common_test', - '_hpp_error_capture_test', '_hpp_error_test', - '_hpp_exception_capture_test', - '_hpp_exception_test', - '_hpp_expect_test', + '_hpp_exception_to_result_test', + '_hpp_handle_test', '_hpp_preload_test', '_hpp_result_test', - 'basic_test', - 'defer_test.1', - 'defer_test.2', - 'defer_test.3', - 'defer_test.4', - 'defer_test.5', - 'defer_test.6', - 'defer_test.7', - 'defer_test.8', + '_hpp_throw_test', + '_hpp_try_test', + 'capture_exception_async_test', + 'capture_exception_state_test', + 'capture_exception_unload_test', + 'capture_result_async_test', + 'capture_result_state_test', + 'capture_result_unload_test', + 'defer_basic_test', + 'defer_nested_error_exception_test', + 'defer_nested_error_result_test', + 'defer_nested_new_error_exception_test', + 'defer_nested_new_error_result_test', + 'defer_nested_success_exception_test', + 'defer_nested_success_result_test', 'diagnostic_output_test', - 'error_capture_test.1', - 'error_capture_test.2', - 'error_test', - 'exception_capture_test', - 'exception_test.1', - 'exception_test.2', - 'expect_test.1', - 'expect_test.2', - 'expect_test.3', - 'expect_test.4', - 'expect_test.5', 'function_traits_test', + 'handle_all_test', + 'handle_some_basic_test', + 'handle_some_test', 'is_error_type_test', - 'multiple_errors_test', 'optional_test', - 'preload_test.1', - 'preload_test.2', - 'preload_test.3', - 'preload_test.4', - 'preload_test.5', - 'preload_test.6', - 'preload_test.7', + 'preload_basic_test', + 'preload_nested_error_exception_test', + 'preload_nested_error_result_test', + 'preload_nested_new_error_exception_test', + 'preload_nested_new_error_result_test', + 'preload_nested_success_exception_test', + 'preload_nested_success_result_test', 'print_test', - 'result_capture_test', - 'result_test.1', - 'result_test.2', - 'result_test.3', - 'result_test.4', - 'result_test.5', - 'result_test.6', - 'result_void_capture_test' + 'result_bad_result_test', + 'result_state_test', + 'static_store_deduction_test', + 'static_store_test', + 'try_exception_test', + 'try_test' ] foreach t : tests test(t, executable(t, 'test/'+t+'.cpp', dependencies: [leaf,thread_dep] ) ) @@ -83,17 +79,17 @@ endforeach examples = [ 'capture_result', 'capture_eh', + 'exception_to_result', 'print_file_result', 'print_file_eh', - 'print_half', - 'return_exception' + 'print_half' ] foreach e : examples executable(e, 'example/'+e+'.cpp', dependencies: [leaf,thread_dep] ) endforeach if get_option('build_lua_examples') - lua=subproject('lua').get_variable('all') - executable('lua_callback_result', 'example/lua_callback_result.cpp', dependencies: [ leaf, lua ] ) - executable('lua_callback_eh', 'example/lua_callback_eh.cpp', dependencies: [ leaf, lua ] ) +# lua=subproject('lua').get_variable('all') +# executable('lua_callback_result', 'example/lua_callback_result.cpp', dependencies: [ leaf, lua ] ) +# executable('lua_callback_eh', 'example/lua_callback_eh.cpp', dependencies: [ leaf, lua ] ) endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e1296dc..ce1696f 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -8,70 +8,67 @@ import testing ; project : requirements - gcc:"-std=c++11" + gcc:"-std=c++11 -Wno-delete-non-virtual-dtor" gcc:"-pthread" - darwin:"-std=c++11 -Wno-unused-variable -Wno-non-virtual-dtor -Wno-dangling-else" + darwin:"-std=c++11 -Wno-unused-variable -Wno-delete-non-virtual-dtor -Wno-non-virtual-dtor -Wno-dangling-else" msvc:"-wd 4267 -wd 4996 -wd 4244" ../../.. ; -run _hpp_all_test.cpp ; -run _hpp_common_test.cpp ; -run _hpp_error_capture_test.cpp ; -run _hpp_error_test.cpp ; -run _hpp_exception_capture_test.cpp ; -run _hpp_exception_test.cpp ; -run _hpp_expect_test.cpp ; -run _hpp_preload_test.cpp ; -run _hpp_result_test.cpp ; -run basic_test.cpp ; -run defer_test.1.cpp ; -run defer_test.2.cpp ; -run defer_test.3.cpp ; -run defer_test.4.cpp ; -run defer_test.5.cpp ; -run defer_test.6.cpp ; -run defer_test.7.cpp ; +compile _hpp_all_test.cpp ; +compile _hpp_capture_exception_test.cpp ; +compile _hpp_capture_result_test.cpp ; +compile _hpp_common_test.cpp ; +compile _hpp_error_test.cpp ; +compile _hpp_exception_to_result_test.cpp ; +compile _hpp_handle_test.cpp ; +compile _hpp_preload_test.cpp ; +compile _hpp_result_test.cpp ; +compile _hpp_throw_test.cpp ; +compile _hpp_try_test.cpp ; + +compile-fail is_error_type_fail_test.cpp ; + +run capture_exception_async_test.cpp ; +run capture_exception_state_test.cpp ; +run capture_exception_unload_test.cpp ; +run capture_result_async_test.cpp ; +run capture_result_state_test.cpp ; +run capture_result_unload_test.cpp ; +run defer_basic_test.cpp ; +run defer_nested_error_exception_test.cpp ; +run defer_nested_error_result_test.cpp ; +run defer_nested_new_error_exception_test.cpp ; +run defer_nested_new_error_result_test.cpp ; +run defer_nested_success_exception_test.cpp ; +run defer_nested_success_result_test.cpp ; run diagnostic_output_test.cpp ; -run error_capture_test.1.cpp ; -run error_capture_test.2.cpp ; -run error_test.cpp ; -run exception_capture_test.cpp : : : multi ; -run exception_test.2.cpp ; -run exception_test.1.cpp ; -run expect_test.1.cpp ; -run expect_test.2.cpp ; -run expect_test.3.cpp ; -run expect_test.4.cpp ; -run expect_test.5.cpp ; -run multiple_errors_test.cpp ; run function_traits_test.cpp ; +run handle_all_test.cpp ; +run handle_some_basic_test.cpp ; +run handle_some_test.cpp ; run is_error_type_test.cpp ; run optional_test.cpp ; -run preload_test.1.cpp ; -run preload_test.2.cpp ; -run preload_test.3.cpp ; -run preload_test.4.cpp ; -run preload_test.5.cpp ; -run preload_test.6.cpp ; +run preload_basic_test.cpp ; +run preload_nested_error_exception_test.cpp ; +run preload_nested_error_result_test.cpp ; +run preload_nested_new_error_exception_test.cpp ; +run preload_nested_new_error_result_test.cpp ; +run preload_nested_success_exception_test.cpp ; +run preload_nested_success_result_test.cpp ; run print_test.cpp ; -run result_capture_test.cpp : : : multi ; -run result_test.1.cpp ; -run result_test.2.cpp ; -run result_test.3.cpp ; -run result_test.4.cpp ; -run result_test.5.cpp ; -run result_test.6.cpp ; -run result_void_capture_test.cpp : : : multi ; -compile-fail expect_fail_test.1.cpp ; -compile-fail expect_fail_test.2.cpp ; -compile-fail error_fail_test.cpp ; +run result_bad_result_test.cpp ; +run result_state_test.cpp ; +run static_store_deduction_test.cpp ; +run static_store_test.cpp ; +run try_exception_test.cpp ; +run try_test.cpp ; -exe print_file_result : ../example/print_file_result.cpp ; -exe print_file_eh : ../example/print_file_eh.cpp ; -#exe lua_callback_result : ../example/lua_callback_result.cpp ; -#exe lua_callback_eh : ../example/lua_callback_eh.cpp ; -exe capture_result : ../example/capture_result.cpp : multi ; exe capture_eh : ../example/capture_eh.cpp : multi ; +exe capture_result : ../example/capture_result.cpp : multi ; +exe exception_to_result : ../example/exception_to_result.cpp ; +#exe lua_callback_result : ../example/exception_to_result.cpp ; +#exe lua_callback_eh : ../example/lua_callback_eh.cpp ; +exe print_file_eh : ../example/print_file_eh.cpp ; +exe print_file_result : ../example/print_file_result.cpp ; exe print_half : ../example/print_half.cpp ; -exe return_exception : ../example/return_exception.cpp ; diff --git a/test/_hpp_exception_capture_test.cpp b/test/_hpp_capture_exception_test.cpp similarity index 74% rename from test/_hpp_exception_capture_test.cpp rename to test/_hpp_capture_exception_test.cpp index ca42968..8c9742d 100644 --- a/test/_hpp_exception_capture_test.cpp +++ b/test/_hpp_capture_exception_test.cpp @@ -4,6 +4,6 @@ //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) -#include -#include +#include +#include int main() { return 0; } diff --git a/test/_hpp_capture_result_test.cpp b/test/_hpp_capture_result_test.cpp new file mode 100644 index 0000000..f89e198 --- /dev/null +++ b/test/_hpp_capture_result_test.cpp @@ -0,0 +1,9 @@ +//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) + +#include +#include +int main() { return 0; } diff --git a/test/_hpp_exception_to_result_test.cpp b/test/_hpp_exception_to_result_test.cpp new file mode 100644 index 0000000..7041667 --- /dev/null +++ b/test/_hpp_exception_to_result_test.cpp @@ -0,0 +1,9 @@ +//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) + +#include +#include +int main() { return 0; } diff --git a/test/_hpp_expect_test.cpp b/test/_hpp_handle_test.cpp similarity index 79% rename from test/_hpp_expect_test.cpp rename to test/_hpp_handle_test.cpp index 230473a..d1bb229 100644 --- a/test/_hpp_expect_test.cpp +++ b/test/_hpp_handle_test.cpp @@ -4,6 +4,6 @@ //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) -#include -#include +#include +#include int main() { return 0; } diff --git a/test/_hpp_exception_test.cpp b/test/_hpp_throw_test.cpp similarity index 78% rename from test/_hpp_exception_test.cpp rename to test/_hpp_throw_test.cpp index 10569d6..c982dab 100644 --- a/test/_hpp_exception_test.cpp +++ b/test/_hpp_throw_test.cpp @@ -4,6 +4,6 @@ //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) -#include -#include +#include +#include int main() { return 0; } diff --git a/test/_hpp_error_capture_test.cpp b/test/_hpp_try_test.cpp similarity index 76% rename from test/_hpp_error_capture_test.cpp rename to test/_hpp_try_test.cpp index cef5ac8..4f8eaaf 100644 --- a/test/_hpp_error_capture_test.cpp +++ b/test/_hpp_try_test.cpp @@ -4,6 +4,6 @@ //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) -#include -#include +#include +#include int main() { return 0; } diff --git a/test/capture_exception_async_test.cpp b/test/capture_exception_async_test.cpp new file mode 100644 index 0000000..6b7f40e --- /dev/null +++ b/test/capture_exception_async_test.cpp @@ -0,0 +1,85 @@ +//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) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" +#include +#include +#include +#include + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +struct fut_info +{ + int a; + int b; + int result; + std::future fut; +}; + +template +std::vector launch_tasks( int task_count, F f ) +{ + assert(task_count>0); + std::vector fut; + std::generate_n( std::inserter(fut,fut.end()), task_count, + [=] + { + int const a = rand(); + int const b = rand(); + int const res = (rand()%10) - 5; + return fut_info { a, b, res, std::async( std::launch::async, + [=] + { + return f(a,b,res); + } ) }; + } ); + return fut; +} + +int main() +{ + std::vector fut = launch_tasks, info<2>>( 42, + leaf::capture_exception,info<2>,info<3>>( + [ ]( int a, int b, int res ) + { + if( res>=0 ) + return res; + else + throw leaf::exception( std::exception(), info<1>{a}, info<2>{b}, info<3>{} ); + } ) ); + + for( auto & f : fut ) + { + f.fut.wait(); + int r = leaf::try_( + [&] + { + return f.fut.get(); + }, + [&]( info<1> const & x1, info<2> const & x2 ) + { + BOOST_TEST(x1.value==f.a); + BOOST_TEST(x2.value==f.b); + return -1; + }, + [ ] + { + return -2; + } ); + if( f.result>=0 ) + BOOST_TEST(r==f.result); + else + BOOST_TEST(r==-1); + } + + return boost::report_errors(); +} diff --git a/test/capture_exception_state_test.cpp b/test/capture_exception_state_test.cpp new file mode 100644 index 0000000..8420c3d --- /dev/null +++ b/test/capture_exception_state_test.cpp @@ -0,0 +1,59 @@ +//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) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int count = 0; + +template +struct info +{ + info() noexcept + { + ++count; + } + + info( info const & ) noexcept + { + ++count; + } + + ~info() noexcept + { + --count; + } +}; + +namespace boost { namespace leaf { + template struct is_error_type>: public std::true_type { }; +} } + +int main() +{ + auto f = leaf::capture_exception, info<2>, info<3>>( + [ ] + { + throw leaf::exception( std::exception(), info<1>{}, info<3>{} ); + } ); + leaf::try_( + [&f] + { + BOOST_TEST(count==0); + try { f(); } + catch(...) { BOOST_TEST(count==2); throw; } + + }, + [ ] + { + } ); + BOOST_TEST(count==0); + return boost::report_errors(); +} diff --git a/test/capture_exception_unload_test.cpp b/test/capture_exception_unload_test.cpp new file mode 100644 index 0000000..175a139 --- /dev/null +++ b/test/capture_exception_unload_test.cpp @@ -0,0 +1,171 @@ +//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) + +#include +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +template +void test( F f_ ) +{ + auto f = leaf::capture_exception, info<2>, info<3>>( [=] { return f_(); } ); + + { + int c=0; + leaf::try_( + [&f] + { + return f(); + }, + [&c]( info<1> const & x ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(c==0); + c = 1; + }, + [&c] + { + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==1); + } + + { + int c=0; + leaf::try_( + [&f] + { + return f(); + }, + [&c]( info<2> const & x ) + { + BOOST_TEST(x.value==2); + BOOST_TEST(c==0); + c = 1; + }, + [&c] + { + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + } + + { + int r = leaf::try_( + [&f] + { + (void) f(); return 0; + }, + [ ]( info<1> const & x ) + { + BOOST_TEST(x.value==1); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + { + int r = leaf::try_( + [&f] + { + (void) f(); return 0; + }, + [ ]( info<2> const & x ) + { + BOOST_TEST(x.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==2); + } + + { + bool r = leaf::try_( + [&f] + { + (void) f(); return true; + }, + [ ]( info<1> const & x, info<2> const & ) + { + return true; + }, + [ ]( info<1> const & x, info<3> const & y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==3); + return false; + }, + [ ]( info<1> const & x ) + { + return true; + }, + [ ] + { + return true; + } ); + BOOST_TEST(!r); + } + + { + bool r = leaf::try_( + [&f] + { + (void) f(); return false; + }, + [ ]( info<1> const & x, info<2> const & ) + { + return false; + }, + [ ]( info<1> const & x, info<3> const & y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==3); + return true; + }, + [ ]( info<1> const & x ) + { + return false; + }, + [ ] + { + return false; + } ); + BOOST_TEST(r); + } +} + +int main() +{ + test( + [ ] + { + throw leaf::exception( std::exception(), info<1>{1}, info<3>{3} ); //Derives from leaf::error + } ); + + test( + [ ] + { + auto propagate = leaf::preload( info<1>{1}, info<3>{3} ); + throw std::exception(); //Does not derive from leaf::error + } ); + + return boost::report_errors(); +} diff --git a/test/capture_result_async_test.cpp b/test/capture_result_async_test.cpp new file mode 100644 index 0000000..be62791 --- /dev/null +++ b/test/capture_result_async_test.cpp @@ -0,0 +1,85 @@ +//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) + +#include +#include + +#include "boost/core/lightweight_test.hpp" +#include +#include +#include +#include + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +struct fut_info +{ + int a; + int b; + int result; + std::future> fut; +}; + +template +std::vector launch_tasks( int task_count, F f ) +{ + assert(task_count>0); + std::vector fut; + std::generate_n( std::inserter(fut,fut.end()), task_count, + [=] + { + int const a = rand(); + int const b = rand(); + int const res = (rand()%10) - 5; + return fut_info { a, b, res, std::async( std::launch::async, + [=] + { + return f(a,b,res); + } ) }; + } ); + return fut; +} + +int main() +{ + std::vector fut = launch_tasks, info<2>>( 42, + leaf::capture_result,info<2>,info<3>>( + [ ]( int a, int b, int res ) -> leaf::result + { + if( res>=0 ) + return res; + else + return leaf::error( info<1>{a}, info<2>{b}, info<3>{} ); + } ) ); + + for( auto & f : fut ) + { + f.fut.wait(); + int r = leaf::handle_all( + [&] + { + return f.fut.get(); + }, + [&]( info<1> const & x1, info<2> const & x2 ) + { + BOOST_TEST(x1.value==f.a); + BOOST_TEST(x2.value==f.b); + return -1; + }, + [ ] + { + return -2; + } ); + if( f.result>=0 ) + BOOST_TEST(r==f.result); + else + BOOST_TEST(r==-1); + } + + return boost::report_errors(); +} diff --git a/test/capture_result_state_test.cpp b/test/capture_result_state_test.cpp new file mode 100644 index 0000000..97872bc --- /dev/null +++ b/test/capture_result_state_test.cpp @@ -0,0 +1,59 @@ +//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) + +#include +#include + +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int count = 0; + +template +struct info +{ + info() noexcept + { + ++count; + } + + info( info const & ) noexcept + { + ++count; + } + + ~info() noexcept + { + --count; + } +}; + +namespace boost { namespace leaf { + template struct is_error_type>: public std::true_type { }; +} } + +int main() +{ + auto f = leaf::capture_result, info<2>, info<3>>( + [ ]() -> leaf::result + { + return leaf::error( info<1>{}, info<3>{} ); + } ); + leaf::handle_all( + [&f] + { + BOOST_TEST(count==0); + auto r = f(); + BOOST_TEST(count==2); + return r; + }, + [ ] + { + } ); + BOOST_TEST(count==0); + return boost::report_errors(); +} diff --git a/test/capture_result_unload_test.cpp b/test/capture_result_unload_test.cpp new file mode 100644 index 0000000..8e77f99 --- /dev/null +++ b/test/capture_result_unload_test.cpp @@ -0,0 +1,163 @@ +//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) + +#include +#include +#include "boost/core/lightweight_test.hpp" + + + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +template +void test( F f_ ) +{ + auto f = leaf::capture_result, info<2>, info<3>>( [=] { return f_(); } ); + + { + int c=0; + leaf::handle_all( + [&f] + { + return f(); + }, + [&c]( info<1> const & x ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(c==0); + c = 1; + }, + [&c] + { + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==1); + } + + { + int c=0; + leaf::handle_all( + [&f] + { + return f(); + }, + [&c]( info<2> const & x ) + { + BOOST_TEST(x.value==2); + BOOST_TEST(c==0); + c = 1; + }, + [&c] + { + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + } + + { + int r = leaf::handle_all( + [&f]() -> leaf::result + { + return f().error(); + }, + [ ]( info<1> const & x ) + { + BOOST_TEST(x.value==1); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + { + int r = leaf::handle_all( + [&f]() -> leaf::result + { + return f().error(); + }, + [ ]( info<2> const & x ) + { + BOOST_TEST(x.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==2); + } + + { + bool r = leaf::handle_all( + [&f]() -> leaf::result + { + return f().error(); + }, + [ ]( info<1> const & x, info<2> const & ) + { + return true; + }, + [ ]( info<1> const & x, info<3> const & y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==3); + return false; + }, + [ ]( info<1> const & x ) + { + return true; + }, + [ ] + { + return true; + } ); + BOOST_TEST(!r); + } + + { + bool r = leaf::handle_all( + [&f]() -> leaf::result + { + return f().error(); + }, + [ ]( info<1> const & x, info<2> const & ) + { + return false; + }, + [ ]( info<1> const & x, info<3> const & y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==3); + return true; + }, + [ ]( info<1> const & x ) + { + return false; + }, + [ ] + { + return false; + } ); + BOOST_TEST(r); + } +} + +int main() +{ + test( + [ ]() -> leaf::result + { + return leaf::error( info<1>{1}, info<3>{3} ); + } ); + return boost::report_errors(); +} diff --git a/test/defer_test.1.cpp b/test/defer_basic_test.cpp similarity index 77% rename from test/defer_test.1.cpp rename to test/defer_basic_test.cpp index ea43e91..d7a37f2 100644 --- a/test/defer_test.1.cpp +++ b/test/defer_basic_test.cpp @@ -4,8 +4,8 @@ //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) -#include #include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; @@ -37,15 +37,20 @@ leaf::error f() int main() { - leaf::expect exp; - int c=0; - bool handled = exp.handle_error( f(), - [&c]( info const & i42 ) + int r = leaf::handle_all( + [ ]() -> leaf::result + { + return f(); + }, + [ ]( info const & i42 ) { BOOST_TEST(i42.value==42); - ++c; + return 1; + }, + [ ] + { + return 2; } ); - BOOST_TEST(handled); + BOOST_TEST(r==1); return boost::report_errors(); - BOOST_TEST(c==1); } diff --git a/test/defer_nested_error_exception_test.cpp b/test/defer_nested_error_exception_test.cpp new file mode 100644 index 0000000..43cbbd2 --- /dev/null +++ b/test/defer_nested_error_exception_test.cpp @@ -0,0 +1,68 @@ +//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) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +void f0() +{ + auto propagate = leaf::defer( [ ] { return info<0>{0}; } ); + throw leaf::exception(std::exception(), info<2>{2} ); +} + +void f1() +{ + auto propagate = leaf::defer( [ ] { return info<0>{-1}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{-1}; } ); + f0(); +} + +void f2() +{ + try + { + f1(); + } + catch( leaf::error e ) + { + e.propagate( info<3>{3} ); + throw; + } +} + +int main() +{ + int r = leaf::try_( + [ ] + { + f2(); + return 0; + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/defer_test.2.cpp b/test/defer_nested_error_result_test.cpp similarity index 66% rename from test/defer_test.2.cpp rename to test/defer_nested_error_result_test.cpp index 838ed1b..eb28094 100644 --- a/test/defer_test.2.cpp +++ b/test/defer_nested_error_result_test.cpp @@ -4,13 +4,13 @@ //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) -#include #include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; -template +template struct info { int value; @@ -30,24 +30,29 @@ leaf::error f1() leaf::error f2() { - return f1().propagate( info<4>{4} ); + return f1().propagate( info<3>{3} ); } int main() { - leaf::expect,info<1>,info<2>,info<3>,info<4>> exp; - leaf::error e = f2(); - BOOST_TEST(!exp.peek>(e)); - int c=0; - bool handled = exp.handle_error( e, - [&c]( info<1> const & i1, info<2> const & i2, info<4> const & i4 ) + int r = leaf::handle_all( + [ ]() -> leaf::result { + return f2(); + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); BOOST_TEST(i1.value==1); BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c; + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); + BOOST_TEST(r==1); + return boost::report_errors(); } diff --git a/test/defer_nested_new_error_exception_test.cpp b/test/defer_nested_new_error_exception_test.cpp new file mode 100644 index 0000000..c0b8fb9 --- /dev/null +++ b/test/defer_nested_new_error_exception_test.cpp @@ -0,0 +1,75 @@ +//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) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +void f0() +{ + auto propagate = leaf::defer( [ ] { return info<0>{-1}; } ); + throw leaf::exception( std::exception(), info<1>{-1} ); +} + +void f1() +{ + auto propagate = leaf::defer( [ ] { return info<0>{0}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{2}; } ); + try { f0(); } catch(...) { } + throw leaf::exception(std::exception()); +} + +leaf::error f2() +{ + try + { + f1(); + BOOST_TEST(false); + } + catch( leaf::error e ) + { + e.propagate( info<3>{3} ); + throw; + } + catch(...) + { + BOOST_TEST(false); + } + return leaf::error(); +} + +int main() +{ + int r = leaf::try_( + [ ] + { + f2(); + return 0; + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/defer_test.5.cpp b/test/defer_nested_new_error_result_test.cpp similarity index 75% rename from test/defer_test.5.cpp rename to test/defer_nested_new_error_result_test.cpp index 9450893..f4e1b20 100644 --- a/test/defer_test.5.cpp +++ b/test/defer_nested_new_error_result_test.cpp @@ -4,8 +4,8 @@ //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) -#include #include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; @@ -36,19 +36,24 @@ leaf::error f2() int main() { - leaf::expect,info<1>,info<2>,info<3>> exp; - leaf::error e = f2(); - int c=0; - bool handled = exp.handle_error( e, - [&c]( info<0> const & i0, info<1> const & i1, info<2> const & i2, info<3> const & i3 ) + int r = leaf::handle_all( + [ ]() -> leaf::result + { + return f2(); + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) { BOOST_TEST(i0.value==0); BOOST_TEST(i1.value==1); BOOST_TEST(i2.value==2); BOOST_TEST(i3.value==3); - ++c; + return 1; + }, + [ ] + { + return 2; } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); + BOOST_TEST(r==1); + return boost::report_errors(); } diff --git a/test/defer_test.4.cpp b/test/defer_nested_success_exception_test.cpp similarity index 64% rename from test/defer_test.4.cpp rename to test/defer_nested_success_exception_test.cpp index b3d3a36..a4a8a5c 100644 --- a/test/defer_test.4.cpp +++ b/test/defer_nested_success_exception_test.cpp @@ -4,15 +4,12 @@ //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) -#include -#include #include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; -struct my_error: std::exception { }; - struct info { int value; }; void g1() @@ -22,7 +19,7 @@ void g1() void g2() { - throw my_error(); + throw std::exception(); } void f() @@ -34,21 +31,22 @@ void f() int main() { - leaf::expect exp; - try - { - f(); - } - catch( my_error & e ) - { - int c=0; - exp.handle_exception( e, - [&c]( info const & x ) - { - BOOST_TEST(x.value==2); - ++c; - } ); - BOOST_TEST(c==1); - } + int r = leaf::try_( + [ ] + { + f(); + return 0; + }, + [ ]( info x ) + { + BOOST_TEST(x.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + return boost::report_errors(); } diff --git a/test/defer_test.3.cpp b/test/defer_nested_success_result_test.cpp similarity index 76% rename from test/defer_test.3.cpp rename to test/defer_nested_success_result_test.cpp index 9cc2b0e..b7b300b 100644 --- a/test/defer_test.3.cpp +++ b/test/defer_nested_success_result_test.cpp @@ -4,9 +4,8 @@ //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) -#include -#include #include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; @@ -33,15 +32,22 @@ leaf::result f() int main() { - leaf::expect exp; - leaf::result r = f(); - int c=0; - BOOST_TEST( exp.handle_error( r, - [&c]( info const & x ) + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_CHECK(f()); + return 1; + }, + [ ]( info x ) { BOOST_TEST(x.value==2); - ++c; - } ) ); - BOOST_TEST(c==1); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + return boost::report_errors(); } diff --git a/test/defer_test.6.cpp b/test/defer_test.6.cpp deleted file mode 100644 index 331ce64..0000000 --- a/test/defer_test.6.cpp +++ /dev/null @@ -1,44 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info -{ - int value; -}; - -leaf::error f0() -{ - return leaf::error(); -} - -leaf::error f1() -{ - auto propagate = leaf::defer( [ ] { return info{0}; } ); - (void) f0(); - return leaf::error(); -} - -int main() -{ - leaf::expect exp; - leaf::error e = f1(); - int c=0; - bool handled = exp.handle_error( e, - [&c]( info const & i0 ) - { - BOOST_TEST(i0.value==0); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/defer_test.7.cpp b/test/defer_test.7.cpp deleted file mode 100644 index b399ef0..0000000 --- a/test/defer_test.7.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//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) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -struct info { int value; }; - -void g1() -{ - auto propagate = leaf::defer( [ ] { return info{1}; } ); -} - -void g2() -{ - throw leaf::exception(my_error()); -} - -void f() -{ - auto propagate = leaf::defer( [ ] { return info{2}; } ); - g1(); - g2(); -} - -int main() -{ - leaf::expect exp; - try - { - f(); - } - catch( my_error & e ) - { - int c=0; - exp.handle_exception( e, - [&c]( info const & x ) - { - BOOST_TEST(x.value==2); - ++c; - } ); - BOOST_TEST(c==1); - } - return boost::report_errors(); -} diff --git a/test/defer_test.8.cpp b/test/defer_test.8.cpp deleted file mode 100644 index 4aa7a67..0000000 --- a/test/defer_test.8.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info -{ - int value; -}; - -leaf::error f0() -{ - return leaf::error(); -} - -leaf::error f1() -{ - auto propagate = leaf::defer( [ ] { return info{42}; } ); - return f0(); -} - -int main() -{ - leaf::expect exp; - leaf::error e = f1(); - int c=0; - bool handled = exp.handle_error( e, - [&c]( leaf::e_unexpected const & unx, leaf::e_unexpected_diagnostic_output const & unxdo ) - { - BOOST_TEST(unx.count==1); - BOOST_TEST(unx.first_type==&leaf::type); - BOOST_TEST(unxdo.value.find(": 42")!=std::string::npos); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/diagnostic_output_test.cpp b/test/diagnostic_output_test.cpp index 3a2786e..f165480 100644 --- a/test/diagnostic_output_test.cpp +++ b/test/diagnostic_output_test.cpp @@ -4,9 +4,8 @@ //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) -#include +#include #include -#include #include "boost/core/lightweight_test.hpp" #include @@ -72,20 +71,8 @@ struct non_printable_info_non_printable_payload int main() { BOOST_TEST(leaf::leaf_detail::tl_unexpected_enabled_counter()==0); - { - leaf::expect - < - leaf::e_source_location, - printable_info_printable_payload, - printable_info_non_printable_payload, - non_printable_info_printable_payload, - non_printable_info_non_printable_payload, - leaf::e_errno, - leaf::e_unexpected_diagnostic_output, - leaf::e_unexpected - > exp; - BOOST_TEST(leaf::leaf_detail::tl_unexpected_enabled_counter()==2); - try + leaf::try_( + [ ] { LEAF_THROW( my_error(), printable_info_printable_payload(), @@ -95,8 +82,16 @@ int main() unexpected_test<1>{1}, unexpected_test<2>{2}, leaf::e_errno{ENOENT} ); - } - catch( my_error & e ) + }, + [ ]( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + leaf::e_errno, + leaf::e_unexpected_diagnostic_output, + leaf::e_unexpected ) { std::ostringstream st; leaf::diagnostic_output_current_exception(st); @@ -110,9 +105,6 @@ int main() BOOST_TEST(s.find("Detected 2 attempts to communicate unexpected error objects, the first one of type ")!=s.npos); BOOST_TEST(s.find("unexpected_test<2>")!=s.npos); std::cout << s; - exp.handle_exception( e, [ ]{ } ); - } - } - BOOST_TEST(leaf::leaf_detail::tl_unexpected_enabled_counter()==0); + } ); return boost::report_errors(); } diff --git a/test/error_capture_test.1.cpp b/test/error_capture_test.1.cpp deleted file mode 100644 index 8aa1578..0000000 --- a/test/error_capture_test.1.cpp +++ /dev/null @@ -1,183 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -leaf::error f() -{ - return leaf::error(info<1>{1},info<3>{3}); -} - -leaf::error_capture make_capture() -{ - leaf::expect,info<2>,info<3>> exp; - return capture(exp,f()); -} - -int main() -{ - leaf::error_capture ec = make_capture(); - - { - int c=0; - bool r = ec.handle_error( - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - c = 1; - } ); - BOOST_TEST(r); - BOOST_TEST(c==1); - } - - { - int c=0; - bool r = ec.handle_error( - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - c = 1; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==0); - } - - { - int c=0; - int r = ec.handle_error( - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - return c = 1; - } ); - BOOST_TEST(r==1); - } - - { - int c=0; - int r = ec.handle_error( - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - return c = 1; - } ); - BOOST_TEST(r==-1); - } - - { - int c=0; - bool r = ec.handle_error( - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - return false; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==2); - } - - { - int c=0; - bool r = ec.handle_error( - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - return false; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - return false; - } ); - BOOST_TEST(r); - BOOST_TEST(c==2); - } - - { - leaf::expect,info<2>,info<3>> exp; - leaf::error e1( info<1>{-1}, info<2>{-2}, info<3>{-3} ); - leaf::error e2 = ec.unload(); - { - int c = 0; - bool r = exp.handle_error( e1, - [&c]( info<1>, info<2>, info<3> ) - { - BOOST_TEST(c==0); - c = 1; - }, - [&c]( info<1>, info<3> ) - { - BOOST_TEST(c==0); - c = 2; - }, - [&c]( info<2> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(y.value==-2); - c = 3; - } ); - BOOST_TEST(r); - BOOST_TEST(c==3); - } - { - int c = 0; - bool r = exp.handle_error( e2, - [&c]( info<1>, info<2>, info<3> ) - { - BOOST_TEST(c==0); - c = 1; - }, - [&c]( info<2> const & y ) - { - BOOST_TEST(c==0); - c = 2; - }, - [&c]( info<1> const & x, info<3> const & z ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(z.value==3); - c = 3; - } ); - BOOST_TEST(r); - BOOST_TEST(c==3); - } - } - - return boost::report_errors(); -} diff --git a/test/error_capture_test.2.cpp b/test/error_capture_test.2.cpp deleted file mode 100644 index 705650a..0000000 --- a/test/error_capture_test.2.cpp +++ /dev/null @@ -1,76 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -int count = 0; - -template -struct info -{ - info() noexcept - { - ++count; - } - - info( info const & ) noexcept - { - ++count; - } - - ~info() noexcept - { - --count; - } -}; - -namespace boost { namespace leaf { - template struct is_error_type>: public std::true_type { }; -} } - -leaf::error f() -{ - return leaf::error(info<1>{},info<3>{}); -} - -leaf::error_capture make_capture() -{ - leaf::expect,info<2>,info<3>> exp; - return capture(exp,f()); -} - -int main() -{ - { - leaf::error_capture ec1 = make_capture(); - BOOST_TEST( ec1.handle_error( [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST(count==2); - leaf::error_capture ec2(ec1); - BOOST_TEST(count==2); - BOOST_TEST( ec1.handle_error( [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( ec2.handle_error( [ ]( info<1>, info<3> ) { } ) ); - leaf::error_capture ec3(std::move(ec2)); - BOOST_TEST(count==2); - BOOST_TEST( ec1.handle_error( [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( ec3.handle_error( [ ]( info<1>, info<3> ) { } ) ); - leaf::error_capture ec4; ec4 = ec3; - BOOST_TEST(count==2); - BOOST_TEST( ec1.handle_error( [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( ec3.handle_error( [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( ec4.handle_error( [ ]( info<1>, info<3> ) { } ) ); - leaf::error_capture ec5; ec5 = std::move(ec4); - BOOST_TEST(count==2); - BOOST_TEST( ec1.handle_error( [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( ec3.handle_error( [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( ec5.handle_error( [ ]( info<1>, info<3> ) { } ) ); - } - BOOST_TEST(count==0); - return boost::report_errors(); -} diff --git a/test/error_test.cpp b/test/error_test.cpp deleted file mode 100644 index f3ea124..0000000 --- a/test/error_test.cpp +++ /dev/null @@ -1,91 +0,0 @@ -//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) - -#include -#include "boost/core/lightweight_test.hpp" -#include - -namespace leaf = boost::leaf; - -template -struct unexp -{ - int value; -}; - -template -struct info -{ - int value; -}; - -leaf::error f1() -{ - return LEAF_ERROR( info<1>{1}, unexp<1>{1}, unexp<2>{2} ); -} - -leaf::error f2() -{ - leaf::expect> exp; - return f1().propagate( info<2>{2} ); -} - -leaf::error f3() -{ - leaf::expect,info<3>,unexp<1>> exp; - leaf::error e = f2().propagate( info<4>{4} ); - BOOST_TEST(exp.peek>(e)->value==1); - return e; -} - -leaf::error f4() -{ - leaf::expect,info<2>,info<3>,info<4>> exp; - { - leaf::error e = f3(); - bool handled = exp.handle_error( e, - [ ]( info<1>, info<2>, info<3>, info<4> ){ }, - [ ]( info<1>, info<2>, info<4> ) { } ); - BOOST_TEST(handled); - } - leaf::error e = f3(); - int c1=0, c2=0; - bool handled = exp.handle_error( e, - [&c1]( info<1>,info<2>,info<3>,info<4> ) - { - ++c1; - }, - [&c2]( leaf::e_source_location const & loc, leaf::e_unexpected const & unx, info<1> const & i1, info<2> const & i2, info<4> const & i4 ) - { - BOOST_TEST(loc.line==27); - BOOST_TEST(strcmp(loc.file,__FILE__)==0); - BOOST_TEST(strstr(loc.function,"f1")!=0); - BOOST_TEST(unx.count==2); - BOOST_TEST(unx.first_type==&leaf::type>); - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c2; - } ); - BOOST_TEST(handled); - BOOST_TEST(c1==0); - BOOST_TEST(c2==1); - return leaf::error(); -} - -int main() -{ - leaf::expect,info<3>,info<4>> exp; - leaf::error e=f4(); - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - return boost::report_errors(); -} diff --git a/test/exception_capture_test.cpp b/test/exception_capture_test.cpp deleted file mode 100644 index d232b1d..0000000 --- a/test/exception_capture_test.cpp +++ /dev/null @@ -1,100 +0,0 @@ -//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) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" -#include -#include -#include -#include - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -template struct info { int value; }; - -struct fut_info -{ - int a; - int b; - int result; - std::future fut; -}; - -template -std::vector launch_tasks( int task_count, F && f ) -{ - assert(task_count>0); - std::vector fut; - std::generate_n( std::inserter(fut,fut.end()), task_count, [&f] - { - int const a = rand(); - int const b = rand(); - int const res = (rand()%10) - 5; - return fut_info { a, b, res, std::async( std::launch::async, [f,a,b,res] - { - auto wrapper = leaf::capture_exception,info<2>,info<3>>(std::move(f)); - return wrapper( a, b, res ); - } ) }; - } ); - return fut; -} - -template -void test( int task_count, F && f ) noexcept -{ - std::vector fut = launch_tasks( task_count, std::forward(f) ); - for( auto & f : fut ) - { - using namespace leaf::leaf_detail; - f.fut.wait(); - leaf::expect,info<2>,info<4>> exp; - try - { - int r = leaf::get(f.fut); - BOOST_TEST(r>=0); - BOOST_TEST(r==f.result); - } - catch( my_error const & e ) - { - int c=0; - exp.handle_exception( e, [&f,&c]( info<1> const & x1, info<2> const & x2 ) - { - BOOST_TEST(x1.value==f.a); - BOOST_TEST(x2.value==f.b); - ++c; - } ); - BOOST_TEST(c==1); - } - } -} - -int main() -{ - test( 42, [ ]( int a, int b, int res ) - { - if( res>=0 ) - return res; - else - throw leaf::exception( my_error(), info<1>{a}, info<2>{b}, info<3>{} ); - } ); - - test( 42, [ ]( int a, int b, int res ) - { - if( res>=0 ) - return res; - else - { - auto propagate = leaf::preload( info<1>{a}, info<2>{b}, info<3>{} ); - throw my_error(); - } - } ); - - return boost::report_errors(); -} diff --git a/test/exception_test.1.cpp b/test/exception_test.1.cpp deleted file mode 100644 index 7ce7d70..0000000 --- a/test/exception_test.1.cpp +++ /dev/null @@ -1,122 +0,0 @@ -//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) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" -#include - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -std::function thrower; - -template -struct info -{ - int value; -}; - -void f1() -{ - thrower(); -} - -void f2() -{ - leaf::expect> exp; - try - { - f1(); - BOOST_TEST(false); - } - catch( std::exception const & ex ) - { - leaf::get_error(ex).propagate( info<2>{2} ); - throw; - } -} - -void f3() -{ - leaf::expect,info<3>> exp; - auto propagate = leaf::preload( info<4>{4} ); - f2(); -} - -void f4() -{ - leaf::expect,info<2>,info<3>,info<4>> exp; - try - { - f3(); - BOOST_TEST(false); - } - catch( my_error const & e ) - { - int c1=0, c2=0; - exp.handle_exception( e, - [&c1]( info<1>, info<2>, info<3>, info<4> ) - { - ++c1; - }, - [&c2]( info<1> const & i1, info<2> const & i2, info<4> const & i4 ) - { - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c2; - } - ); - BOOST_TEST(c1==0); - BOOST_TEST(c2==1); - } - throw my_error(); -} - -void test( std::function const & f ) -{ - thrower = f; - leaf::expect,info<3>,info<4>> exp; - try - { - f4(); - BOOST_TEST(false); - } - catch( my_error const & e ) - { - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - } -} - -int main() -{ - test( [ ] - { - LEAF_THROW( my_error(), info<1>{1} ); - } ); - - test( [ ] - { - throw LEAF_EXCEPTION( my_error{}, info<1>{1} ); - } ); - - test( [ ] - { - auto propagate = leaf::preload(info<1>{1}); - throw my_error(); - } ); - - return boost::report_errors(); -} diff --git a/test/exception_test.2.cpp b/test/exception_test.2.cpp deleted file mode 100644 index cfc61a1..0000000 --- a/test/exception_test.2.cpp +++ /dev/null @@ -1,122 +0,0 @@ -//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) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" -#include - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -std::function thrower; - -template -struct info -{ - int value; -}; - -void f1() -{ - thrower(); -} - -void f2() -{ - leaf::expect> exp; - try - { - f1(); - BOOST_TEST(false); - } - catch( std::exception const & ex ) - { - leaf::get_error(ex).propagate( info<2>{2} ); - throw; - } -} - -void f3() -{ - leaf::expect,info<3>> exp; - auto propagate = leaf::defer( [ ] { return info<4>{4}; } ); - f2(); -} - -void f4() -{ - leaf::expect,info<2>,info<3>,info<4>> exp; - try - { - f3(); - BOOST_TEST(false); - } - catch( my_error const & e ) - { - int c1=0, c2=0; - exp.handle_exception( e, - [&c1]( info<1>, info<2>, info<3>, info<4> ) - { - ++c1; - }, - [&c2]( info<1> const & i1, info<2> const & i2, info<4> const & i4 ) - { - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c2; - } - ); - BOOST_TEST(c1==0); - BOOST_TEST(c2==1); - } - throw my_error(); -} - -void test( std::function const & f ) -{ - thrower = f; - leaf::expect,info<3>,info<4>> exp; - try - { - f4(); - BOOST_TEST(false); - } - catch( my_error const & e ) - { - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - } -} - -int main() -{ - test( [ ] - { - throw leaf::exception( my_error(), info<1>{1} ); - } ); - - test( [ ] - { - LEAF_THROW( my_error(), info<1>{1} ); - } ); - - test( [ ] - { - auto propagate = leaf::defer( [ ] { return info<1>{1}; } ); - throw my_error(); - } ); - - return boost::report_errors(); -} diff --git a/test/expect_fail_test.1.cpp b/test/expect_fail_test.1.cpp deleted file mode 100644 index d90c1e2..0000000 --- a/test/expect_fail_test.1.cpp +++ /dev/null @@ -1,33 +0,0 @@ -//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) - -#include - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -leaf::error f() -{ - return leaf::error(info<1>{1},info<3>{3}); -} - -int main() -{ - leaf::expect,info<2>,info<3>> exp; - - (void) exp.handle_error( f(), - [ ]( info<1>, info<2> ) - { - return 42; - }, - [ ]( info<1>, info<3> ) - { - return 42.0f; - } ); - - return 0; -} diff --git a/test/expect_fail_test.2.cpp b/test/expect_fail_test.2.cpp deleted file mode 100644 index 70555d3..0000000 --- a/test/expect_fail_test.2.cpp +++ /dev/null @@ -1,35 +0,0 @@ -//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) - -#include - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -struct test { }; - -leaf::error f() -{ - return leaf::error(info<1>{1},info<3>{3}); -} - -int main() -{ - leaf::expect,info<2>,info<3>> exp; - - (void) exp.handle_error( f(), - [ ]( info<1>, info<2> ) - { - return test { }; - }, - [ ]( info<1>, info<3> ) - { - return 42; - } ); - - return 0; -} diff --git a/test/expect_test.1.cpp b/test/expect_test.1.cpp deleted file mode 100644 index a0a56ec..0000000 --- a/test/expect_test.1.cpp +++ /dev/null @@ -1,36 +0,0 @@ -//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) - -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info { int value; }; - -leaf::error g() -{ - leaf::expect exp; - return leaf::error(); -} - -leaf::error f() -{ - return g(); -} - -int main() -{ - leaf::expect exp; - int c=0; - BOOST_TEST( !exp.handle_error( f(), - [&c]( info ) - { - ++c; - } ) ); - BOOST_TEST(c==0); - return boost::report_errors(); -} diff --git a/test/expect_test.2.cpp b/test/expect_test.2.cpp deleted file mode 100644 index e80a0fc..0000000 --- a/test/expect_test.2.cpp +++ /dev/null @@ -1,55 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info { int value; }; - -struct my_error: std::exception { }; - -void g() -{ - leaf::expect exp; - throw my_error(); -} - -void f() -{ - return g(); -} - -int main() -{ - leaf::expect exp; - try - { - f(); - BOOST_TEST(false); - } - catch( my_error & e ) - { - int c=0; - try - { - exp.handle_exception( e, - [&c]( info ) - { - ++c; - } ); - BOOST_TEST(false); - } - catch( my_error & ) - { - } - BOOST_TEST(c==0); - } - - return boost::report_errors(); -} diff --git a/test/expect_test.3.cpp b/test/expect_test.3.cpp deleted file mode 100644 index a256572..0000000 --- a/test/expect_test.3.cpp +++ /dev/null @@ -1,55 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info { int value; }; - -struct my_error: std::exception { }; - -void g() -{ - leaf::expect exp; - throw leaf::exception(my_error()); -} - -void f() -{ - return g(); -} - -int main() -{ - leaf::expect exp; - try - { - f(); - BOOST_TEST(false); - } - catch( my_error & e ) - { - int c=0; - try - { - exp.handle_exception( e, - [&c]( info ) - { - ++c; - } ); - BOOST_TEST(false); - } - catch( my_error & ) - { - } - BOOST_TEST(c==0); - } - - return boost::report_errors(); -} diff --git a/test/expect_test.4.cpp b/test/expect_test.4.cpp deleted file mode 100644 index da51c41..0000000 --- a/test/expect_test.4.cpp +++ /dev/null @@ -1,124 +0,0 @@ -//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) - -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -class MStatus { }; - -namespace boost { namespace leaf { - template<> struct is_error_type: std::true_type { }; -} } - -int main() -{ - leaf::expect exp; - MStatus ms; - { - MStatus copy = ms; - { - int c=0; - bool handled = exp.handle_error( leaf::error(copy), - [&c]( MStatus ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - int c=0; - bool handled = exp.handle_error( leaf::error(copy), - [&c]( MStatus const ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - int c=0; - bool handled = exp.handle_error( leaf::error(copy), - [&c]( MStatus const & ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - } - { - MStatus & ref = ms; - { - int c=0; - bool handled = exp.handle_error( leaf::error(ref), - [&c]( MStatus ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - int c=0; - bool handled = exp.handle_error( leaf::error(ref), - [&c]( MStatus const ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - int c=0; - bool handled = exp.handle_error( leaf::error(ref), - [&c]( MStatus const & ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - } - { - { - MStatus const & cref = ms; - int c=0; - bool handled = exp.handle_error( leaf::error(cref), - [&c]( MStatus ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - MStatus const & cref = ms; - int c=0; - bool handled = exp.handle_error( leaf::error(cref), - [&c]( MStatus const ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - MStatus const & cref = ms; - int c=0; - bool handled = exp.handle_error( leaf::error(cref), - [&c]( MStatus const & ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - } - return boost::report_errors(); -} diff --git a/test/expect_test.5.cpp b/test/expect_test.5.cpp deleted file mode 100644 index 263f43a..0000000 --- a/test/expect_test.5.cpp +++ /dev/null @@ -1,125 +0,0 @@ -//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) - -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -leaf::error f() -{ - return leaf::error(info<1>{1},info<3>{3}); -} - -int main() -{ - leaf::expect,info<2>,info<3>> exp; - - { - int c=0; - bool r = exp.handle_error( f(), - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - c = 1; - } ); - BOOST_TEST(r); - BOOST_TEST(c==1); - } - - { - int c=0; - bool r = exp.handle_error( f(), - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - c = 1; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==0); - } - - { - int c=0; - int r = exp.handle_error( f(), - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - return c = 1; - } ); - BOOST_TEST(r==1); - } - - { - int c=0; - int r = exp.handle_error( f(), - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - return c = 1; - } ); - BOOST_TEST(r==-1); - } - - { - int c=0; - bool r = exp.handle_error( f(), - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - return false; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==2); - } - - { - int c=0; - bool r = exp.handle_error( f(), - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - return false; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - return false; - } ); - BOOST_TEST(r); - BOOST_TEST(c==2); - } - - return boost::report_errors(); -} diff --git a/test/function_traits_test.cpp b/test/function_traits_test.cpp index 83c9e70..4b736e9 100644 --- a/test/function_traits_test.cpp +++ b/test/function_traits_test.cpp @@ -11,16 +11,13 @@ template void check_traits( F ) { using namespace boost::leaf::leaf_detail; + using boost::leaf::leaf_detail_mp11::mp_list; static_assert(function_traits::arity==4,"arity"); static_assert(std::is_same::return_type,double>::value,"return_type"); static_assert(std::is_same::template arg<0>::type,int>::value,"arg<0>"); - static_assert(std::is_same::template arg<0>::type_,int>::value,"arg<0>"); static_assert(std::is_same::template arg<1>::type,float>::value,"arg<1>"); - static_assert(std::is_same::template arg<1>::type_,float>::value,"arg<1>"); static_assert(std::is_same::template arg<2>::type,int const &>::value,"arg<2>"); - static_assert(std::is_same::template arg<2>::type_,int>::value,"arg<2>"); static_assert(std::is_same::template arg<3>::type,float &&>::value,"arg<3>"); - static_assert(std::is_same::template arg<3>::type_,float>::value,"arg<3>"); static_assert(std::is_same::mp_args,mp_list>::value,"mp_args"); } diff --git a/test/handle_all_test.cpp b/test/handle_all_test.cpp new file mode 100644 index 0000000..c78c439 --- /dev/null +++ b/test/handle_all_test.cpp @@ -0,0 +1,350 @@ +//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) + +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +enum class error_codes +{ + ok, + error1, + error2, + error3 +}; +namespace boost { namespace leaf { + template <> struct is_error_type: std::true_type { }; +} } + +struct error_codes_ { error_codes value; }; + +template +leaf::result f( error_codes ec ) +{ + if( ec==error_codes::ok ) + return R(42); + else + return leaf::error(ec,error_codes_{ec},info<1>{1},info<2>{2},info<3>{3}); +} + +int main() +{ + //void, handle_all (success) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::ok)); + c = answer; + return { }; + }, + [&c] + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(c==42); + } + + //void, handle_all (failure) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( error_codes ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==1); + } + + //void, handle_all (failure), match enum (single enum value) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_all (failure), match enum (multiple enum values) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_all (failure), match value (single value) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_all (failure), match value (multiple values) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + ////////////////////////////////////// + + //int, handle_all (success) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::ok)); + return answer; + }, + [ ] + { + return 1; + } ); + BOOST_TEST(r==42); + } + + //int, handle_all (failure) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( error_codes ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + //int, handle_all (failure), match enum (single enum value) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_all (failure), match enum (multiple enum values) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_all (failure), match value (single value) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_all (failure), match value (multiple values) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + return boost::report_errors(); +} diff --git a/test/handle_some_basic_test.cpp b/test/handle_some_basic_test.cpp new file mode 100644 index 0000000..58c1a48 --- /dev/null +++ b/test/handle_some_basic_test.cpp @@ -0,0 +1,148 @@ +//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) + +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +enum class error_code +{ + error1=1, + error2, + error3 +}; + +namespace boost { namespace leaf { + template <> struct is_error_type: public std::true_type { }; +} } + +leaf::result compute_answer( int what_to_do ) noexcept +{ + switch( what_to_do ) + { + case 0: + return 42; + case 1: + return leaf::error(error_code::error1); + case 2: + return leaf::error(error_code::error2); + default: + assert(what_to_do==3); + return leaf::error(error_code::error3); + } +} + +leaf::result handle_some_errors( int what_to_do ) +{ + return leaf::handle_some( + [=] + { + return compute_answer(what_to_do); + }, + [ ]( leaf::match ) + { + return -42; + } ); +} + +leaf::result handle_some_errors_float( int what_to_do ) +{ + return leaf::handle_some( + [=]() -> leaf::result + { + return compute_answer(what_to_do); + }, + [ ]( leaf::match ) + { + return -42.0f; + } ); +} + +leaf::result handle_some_errors_void( int what_to_do ) +{ + return leaf::handle_some( + [=]() -> leaf::result + { + LEAF_AUTO(answer, compute_answer(what_to_do)); + (void) answer; + return { }; + }, + [ ]( leaf::match ) + { + } ); +} + +int main() +{ + BOOST_TEST(handle_some_errors(0).value()==42); + BOOST_TEST(handle_some_errors(1).value()==-42); + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer,handle_some_errors(3)); + (void) answer; + return 0; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + /////////////////////////// + + BOOST_TEST(handle_some_errors_float(0).value()==42.0f); + BOOST_TEST(handle_some_errors_float(2).value()==-42.0f); + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer,handle_some_errors_float(1)); + (void) answer; + return 0; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + /////////////////////////// + + BOOST_TEST(handle_some_errors_void(0)); + BOOST_TEST(handle_some_errors_void(3)); + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_CHECK(handle_some_errors_void(2)); + return 0; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + return boost::report_errors(); +} diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp new file mode 100644 index 0000000..3e39f81 --- /dev/null +++ b/test/handle_some_test.cpp @@ -0,0 +1,1013 @@ +//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) + +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +enum class error_codes +{ + ok, + error1, + error2, + error3 +}; +namespace boost { namespace leaf { + template <> struct is_error_type: std::true_type { }; +} } + +struct error_codes_ { error_codes value; }; + +template +leaf::result f( error_codes ec ) +{ + if( ec==error_codes::ok ) + return R(42); + else + return leaf::error(ec,error_codes_{ec},info<1>{1},info<2>{2},info<3>{3}); +} + +int main() +{ + + //void, handle_some (success) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::ok)); + c = answer; + return { }; + }, + [&c]( leaf::error e ) + { + BOOST_TEST(c==0); + c = 1; + return e; + } ); + BOOST_TEST(r); + BOOST_TEST(c==42); + } + + //void, handle_some (failure, matched) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( error_codes ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(c==1); + BOOST_TEST(r); + } + + //void, handle_some (failure, matched), match enum (single enum value) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + BOOST_TEST(r); + } + + //void, handle_some (failure, matched), match enum (multiple enum values) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + BOOST_TEST(r); + } + + //void, handle_some (failure, matched), match value (single value) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + BOOST_TEST(r); + } + + //void, handle_some (failure, matched), match value (multiple values) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + BOOST_TEST(r); + } + + //void, handle_some (failure, initially not matched) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( info<4> ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST(c==0); + return r; + }, + [&c]( error_codes ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_some (failure, initially not matched), match enum (single enum value) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST(c==0); + return r; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_some (failure, initially not matched), match enum (multiple enum values) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST(c==0); + return r; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_some (failure, initially not matched), match value (single value) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST(c==0); + return r; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_some (failure, initially not matched), match value (multiple values) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST(c==0); + return r; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_some (failure, initially matched) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( error_codes ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST(c==1); + return r; + }, + [&c]( info<4> ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + //void, handle_some (failure, initially matched), match enum (single enum value) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST(c==1); + return r; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + //void, handle_some (failure, initially matched), match enum (multiple enum values) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST(c==1); + return r; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + //void, handle_some (failure, initially matched), match value (single value) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST(c==1); + return r; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + //void, handle_some (failure, initially matched), match value (multiple values) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST(c==1); + return r; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + ////////////////////////////////////// + + //int, handle_some (success) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::ok)); + return answer; + }, + [ ]( leaf::error e ) + { + return e; + } ); + BOOST_TEST(r && *r==42); + } + + //int, handle_some (failure, matched) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( error_codes ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r && *r==1); + } + + //int, handle_some (failure, matched), match enum (single enum value) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r && *r==2); + } + + //int, handle_some (failure, matched), match enum (multiple enum values) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r && *r==2); + } + + //int, handle_some (failure, matched), match value (single value) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r && *r==2); + } + + //int, handle_some (failure, matched), match value (multiple values) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r && *r==2); + } + + //int, handle_some (failure, initially not matched) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( info<4> ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + [ ]( error_codes ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_some (failure, initially not matched), match enum (single enum value) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_some (failure, initially not matched), match enum (multiple enum values) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_some (failure, initially not matched), match value (single value) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_some (failure, initially not matched), match value (multiple values) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_some (failure, initially matched) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( error_codes ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + [ ]( info<4> ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + //int, handle_some (failure, initially matched), match enum (single enum value) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + [ ]( leaf::match ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + //int, handle_some (failure, initially matched), match enum (multiple enum values) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + [ ]( leaf::match ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + //int, handle_some (failure, initially matched), match value (single value) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + [ ]( leaf::match ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + //int, handle_some (failure, initially matched), match value (multiple values) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_codes::error1)); + return answer; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + [ ]( leaf::match ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + return boost::report_errors(); +} diff --git a/test/error_fail_test.cpp b/test/is_error_type_fail_test.cpp similarity index 100% rename from test/error_fail_test.cpp rename to test/is_error_type_fail_test.cpp diff --git a/test/multiple_errors_test.cpp b/test/multiple_errors_test.cpp index f7174ec..92c0e3b 100644 --- a/test/multiple_errors_test.cpp +++ b/test/multiple_errors_test.cpp @@ -4,7 +4,7 @@ //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) -#include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; @@ -27,7 +27,7 @@ leaf::error f34() int main() { - leaf::expect,info<2>,info<3>,info<4>> exp; + leaf::static_store,info<2>,info<3>,info<4>> exp; leaf::error e1=f12(); leaf::error e2=f34(); { diff --git a/test/preload_test.6.cpp b/test/preload_basic_test.cpp similarity index 57% rename from test/preload_test.6.cpp rename to test/preload_basic_test.cpp index ffceb60..28d1b70 100644 --- a/test/preload_test.6.cpp +++ b/test/preload_basic_test.cpp @@ -4,8 +4,8 @@ //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) -#include #include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; @@ -15,30 +15,33 @@ struct info int value; }; -leaf::error f0() +leaf::error g() { + auto propagate = leaf::preload( info{42} ); return leaf::error(); } -leaf::error f1() +leaf::error f() { - auto propagate = leaf::preload( info{0} ); - (void) f0(); - return leaf::error(); + return g(); } int main() { - leaf::expect exp; - leaf::error e = f1(); - int c=0; - bool handled = exp.handle_error( e, - [&c]( info const & i0 ) + int r = leaf::handle_all( + [ ]() -> leaf::result { - BOOST_TEST(i0.value==0); - ++c; + return f(); + }, + [ ]( info const & i42 ) + { + BOOST_TEST(i42.value==42); + return 1; + }, + [ ] + { + return 2; } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); + BOOST_TEST(r==1); return boost::report_errors(); } diff --git a/test/preload_nested_error_exception_test.cpp b/test/preload_nested_error_exception_test.cpp new file mode 100644 index 0000000..e2db1d1 --- /dev/null +++ b/test/preload_nested_error_exception_test.cpp @@ -0,0 +1,68 @@ +//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) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +void f0() +{ + auto propagate = leaf::preload( info<0>{0} ); + throw leaf::exception(std::exception(), info<2>{2} ); +} + +void f1() +{ + auto propagate = leaf::preload( info<0>{-1}, info<1>{1}, info<2>{-1} ); + f0(); +} + +void f2() +{ + try + { + f1(); + } + catch( leaf::error e ) + { + e.propagate( info<3>{3} ); + throw; + } +} + +int main() +{ + int r = leaf::try_( + [ ] + { + f2(); + return 0; + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/preload_test.1.cpp b/test/preload_nested_error_result_test.cpp similarity index 64% rename from test/preload_test.1.cpp rename to test/preload_nested_error_result_test.cpp index 84a7793..85f140e 100644 --- a/test/preload_test.1.cpp +++ b/test/preload_nested_error_result_test.cpp @@ -4,13 +4,13 @@ //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) -#include #include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; -template +template struct info { int value; @@ -30,25 +30,29 @@ leaf::error f1() leaf::error f2() { - return f1().propagate( info<4>{4} ); + return f1().propagate( info<3>{3} ); } int main() { - leaf::expect,info<1>,info<2>,info<3>,info<4>> exp; - leaf::error e = f2(); - BOOST_TEST(!exp.peek>(e)); - int c=0; - bool handled = exp.handle_error( e, - [&c]( info<0> const & i0, info<1> const & i1, info<2> const & i2, info<4> const & i4 ) + int r = leaf::handle_all( + [ ]() -> leaf::result + { + return f2(); + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) { BOOST_TEST(i0.value==0); BOOST_TEST(i1.value==1); BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c; + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); + BOOST_TEST(r==1); + return boost::report_errors(); } diff --git a/test/preload_nested_new_error_exception_test.cpp b/test/preload_nested_new_error_exception_test.cpp new file mode 100644 index 0000000..2b3038b --- /dev/null +++ b/test/preload_nested_new_error_exception_test.cpp @@ -0,0 +1,75 @@ +//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) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +void f0() +{ + auto propagate = leaf::preload( info<0>{-1} ); + throw leaf::exception( std::exception(), info<1>{-1} ); +} + +void f1() +{ + auto propagate = leaf::preload( info<0>{0}, info<1>{1}, info<2>{2} ); + try { f0(); } catch(...) { } + throw leaf::exception(std::exception()); +} + +leaf::error f2() +{ + try + { + f1(); + BOOST_TEST(false); + } + catch( leaf::error e ) + { + e.propagate( info<3>{3} ); + throw; + } + catch(...) + { + BOOST_TEST(false); + } + return leaf::error(); +} + +int main() +{ + int r = leaf::try_( + [ ] + { + f2(); + return 0; + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/preload_test.5.cpp b/test/preload_nested_new_error_result_test.cpp similarity index 74% rename from test/preload_test.5.cpp rename to test/preload_nested_new_error_result_test.cpp index 9e6219c..1255e16 100644 --- a/test/preload_test.5.cpp +++ b/test/preload_nested_new_error_result_test.cpp @@ -4,8 +4,8 @@ //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) -#include #include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; @@ -36,19 +36,24 @@ leaf::error f2() int main() { - leaf::expect,info<1>,info<2>,info<3>> exp; - leaf::error e = f2(); - int c=0; - bool handled = exp.handle_error( e, - [&c]( info<0> const & i0, info<1> const & i1, info<2> const & i2, info<3> const & i3 ) + int r = leaf::handle_all( + [ ]() -> leaf::result + { + return f2(); + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) { BOOST_TEST(i0.value==0); BOOST_TEST(i1.value==1); BOOST_TEST(i2.value==2); BOOST_TEST(i3.value==3); - ++c; + return 1; + }, + [ ] + { + return 2; } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); + BOOST_TEST(r==1); + return boost::report_errors(); } diff --git a/test/preload_test.3.cpp b/test/preload_nested_success_exception_test.cpp similarity index 63% rename from test/preload_test.3.cpp rename to test/preload_nested_success_exception_test.cpp index 0f0c978..4545040 100644 --- a/test/preload_test.3.cpp +++ b/test/preload_nested_success_exception_test.cpp @@ -4,15 +4,12 @@ //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) -#include -#include #include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; -struct my_error: std::exception { }; - struct info { int value; }; void g1() @@ -22,7 +19,7 @@ void g1() void g2() { - throw my_error(); + throw std::exception(); } void f() @@ -34,21 +31,22 @@ void f() int main() { - leaf::expect exp; - try - { - f(); - } - catch( my_error & e ) - { - int c=0; - exp.handle_exception( e, - [&c]( info const & x ) - { - BOOST_TEST(x.value==2); - ++c; - } ); - BOOST_TEST(c==1); - } + int r = leaf::try_( + [ ] + { + f(); + return 0; + }, + [ ]( info x ) + { + BOOST_TEST(x.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + return boost::report_errors(); } diff --git a/test/preload_test.2.cpp b/test/preload_nested_success_result_test.cpp similarity index 72% rename from test/preload_test.2.cpp rename to test/preload_nested_success_result_test.cpp index f6b8bf5..2b1142a 100644 --- a/test/preload_test.2.cpp +++ b/test/preload_nested_success_result_test.cpp @@ -4,9 +4,8 @@ //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) -#include -#include #include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; @@ -31,19 +30,24 @@ leaf::result f() return g2(); } -int -main() +int main() { - leaf::expect exp; - leaf::result r = f(); - int c=0; - bool handled = exp.handle_error( r, - [&c]( info const & x ) + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_CHECK(f()); + return 1; + }, + [ ]( info x ) { BOOST_TEST(x.value==2); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + return boost::report_errors(); } diff --git a/test/preload_test.4.cpp b/test/preload_test.4.cpp deleted file mode 100644 index 9f07a7c..0000000 --- a/test/preload_test.4.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//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) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -struct info { int value; }; - -void g1() -{ - auto propagate = leaf::preload( info{1} ); -} - -void g2() -{ - throw leaf::exception(my_error()); -} - -void f() -{ - auto propagate = leaf::preload( info{2} ); - g1(); - g2(); -} - -int main() -{ - leaf::expect exp; - try - { - f(); - } - catch( my_error & e ) - { - int c=0; - exp.handle_exception( e, - [&c]( info const & x ) - { - BOOST_TEST(x.value==2); - ++c; - } ); - BOOST_TEST(c==1); - } - return boost::report_errors(); -} diff --git a/test/preload_test.7.cpp b/test/preload_test.7.cpp deleted file mode 100644 index 71585f5..0000000 --- a/test/preload_test.7.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info -{ - int value; -}; - -leaf::error f0() -{ - return leaf::error(); -} - -leaf::error f1() -{ - auto propagate = leaf::preload( info{42} ); - return f0(); -} - -int main() -{ - leaf::expect exp; - leaf::error e = f1(); - int c=0; - bool handled = exp.handle_error( e, - [&c]( leaf::e_unexpected const & unx, leaf::e_unexpected_diagnostic_output const & unxdo ) - { - BOOST_TEST(unx.count==1); - BOOST_TEST(unx.first_type==&leaf::type); - BOOST_TEST(unxdo.value.find(": 42")!=std::string::npos); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/result_bad_result_test.cpp b/test/result_bad_result_test.cpp new file mode 100644 index 0000000..f531426 --- /dev/null +++ b/test/result_bad_result_test.cpp @@ -0,0 +1,68 @@ +//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) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int check( leaf::catch_, leaf::e_source_location const & x ) +{ + BOOST_TEST(strstr(x.file,"result.hpp")!=0); + BOOST_TEST(x.line>0); + BOOST_TEST(strstr(x.function,"value")!=0); + return 1; +} + +int main() +{ + { + int r = leaf::try_( + [ ] + { + leaf::result r((leaf::error())); + (void) r.value(); + return 0; + }, + check ); + BOOST_TEST(r==1); + } + { + int r = leaf::try_( + [ ] + { + leaf::result const r((leaf::error())); + (void) r.value(); + return 0; + }, + check ); + BOOST_TEST(r==1); + } + { + int r = leaf::try_( + [ ] + { + leaf::result r((leaf::error())); + (void) *r; + return 0; + }, + check ); + BOOST_TEST(r==1); + } + { + int r = leaf::try_( + [ ] + { + leaf::result const r((leaf::error())); + (void) *r; + return 0; + }, + check ); + BOOST_TEST(r==1); + } + return boost::report_errors(); +} diff --git a/test/result_capture_test.cpp b/test/result_capture_test.cpp deleted file mode 100644 index 4e7c893..0000000 --- a/test/result_capture_test.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" -#include -#include -#include -#include - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -struct fut_info -{ - int a; - int b; - int result; - std::future> fut; -}; - -template -std::vector launch_tasks( int task_count, F f ) -{ - assert(task_count>0); - std::vector fut; - std::generate_n( std::inserter(fut,fut.end()), task_count, [f] - { - int const a = rand(); - int const b = rand(); - int const res = (rand()%10) - 5; - return fut_info { a, b, res, std::async( std::launch::async, [f,a,b,res] - { - leaf::expect,info<2>,info<3>> exp; - return capture(exp,f(a,b,res)); - } ) }; - } ); - return fut; -} - -int main() -{ - std::vector fut = launch_tasks( 42, [ ]( int a, int b, int res ) noexcept -> leaf::result - { - if( res>=0 ) - return res; - else - return leaf::error(info<1>{a},info<2>{b},info<3>{}); - } ); - for( auto & f : fut ) - { - using namespace leaf::leaf_detail; - f.fut.wait(); - leaf::expect,info<2>,info<4>> exp; - if( leaf::result r = f.fut.get() ) - { - BOOST_TEST(*r>=0); - BOOST_TEST(*r==f.result); - } - else - { - int c=0; - bool handled = exp.handle_error( r, [&f,&c]( info<1> const & x1, info<2> const & x2 ) - { - BOOST_TEST(x1.value==f.a); - BOOST_TEST(x2.value==f.b); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - } - return boost::report_errors(); -} diff --git a/test/result_state_test.cpp b/test/result_state_test.cpp new file mode 100644 index 0000000..721e04b --- /dev/null +++ b/test/result_state_test.cpp @@ -0,0 +1,844 @@ +//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) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct val +{ + static int id_count; + static int count; + int id; + + val(): + id(++id_count) + { + ++count; + } + + val( val const & x ): + id(x.id) + { + ++count; + } + + val( val && x ): + id(x.id) + { + ++count; + } + + ~val() + { + --count; + } + + friend bool operator==( val const & a, val const & b ) + { + return a.id==b.id; + } +}; +int val::count = 0; +int val::id_count = 0; + +struct err +{ + static int count; + + err() + { + ++count; + } + + err( err const & ) + { + ++count; + } + + err( err && ) + { + ++count; + } + + ~err() + { + --count; + } +}; +int err::count = 0; +struct e_err { err value; }; + +bool eq_value( leaf::result & r1, leaf::result & r2 ) +{ + leaf::result const & cr1 = r1; + leaf::result const & cr2 = r2; + return + r1.value()==r2.value() && + cr1.value()==cr2.value() && + *r1==*r2 && + *cr1==*cr2; +} + +int main() +{ + using leaf::leaf_detail::static_store; + using leaf::capture_result; + + { //value default -> copy + leaf::result r1; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value move -> copy + leaf::result r1 = val(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value copy -> copy + val v; + leaf::result r1 = v; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==3); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //value default -> move + leaf::result r1; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value move -> move + leaf::result r1 = val(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value copy -> move + val v; + leaf::result r1 = v; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==3); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //value default -> assign-copy + leaf::result r1; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value move -> assign-copy + leaf::result r1 = val(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value copy -> assign-copy + val v; + leaf::result r1 = v; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==3); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //value default -> assign-move + leaf::result r1; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value move -> assign-move + leaf::result r1 = val(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value copy -> assign-move + val v; + leaf::result r1 = v; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==3); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //value default -> capture -> copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value move -> capture -> copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value copy -> capture -> copy + leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //value default -> capture -> move + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value move -> capture -> move + leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value copy -> capture -> move + leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //value default -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value move -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value copy -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //value default -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value move -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //value copy -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + // ^^ value ^^ + // vv error vv + + { //error move -> copy + static_store exp; + exp.set_reset(true); + leaf::result r1 = leaf::error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //error copy -> copy + static_store exp; + exp.set_reset(true); + leaf::error e( e_err{ } ); + leaf::result r1 = e; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //error move -> move + static_store exp; + exp.set_reset(true); + leaf::result r1 = leaf::error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //error copy -> move + static_store exp; + leaf::error e( e_err{ } ); + leaf::result r1 = e; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //error move -> assign copy + static_store exp; + leaf::result r1 = leaf::error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //error copy -> assign copy + static_store exp; + leaf::error e( e_err{ } ); + leaf::result r1 = e; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //error move -> assign move + static_store exp; + leaf::result r1 = leaf::error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + exp.handle_error(r2.error(),0,[ ]{ }); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //error copy -> assign move + static_store exp; + leaf::error e( e_err{ } ); + leaf::result r1 = e; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //error move -> capture -> copy + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //error copy -> capture -> copy + leaf::result r1 = capture_result( [ ] { leaf::error e( e_err{ } ); return leaf::result(e); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //error move -> capture -> move + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //error copy -> capture -> move + leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //error move -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //error copy -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { //error move -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { //error copy -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + // ^^ result ^^ + + ///////////////////////////////////////////////////////////// + + // vv result vv + + { //void default -> copy + leaf::result r1; + BOOST_TEST(r1); + leaf::result r2 = r1; + BOOST_TEST(r2); + } + + { //void default -> move + leaf::result r1; + BOOST_TEST(r1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + } + + { //void default -> assign-copy + leaf::result r1; + BOOST_TEST(r1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + } + + { //void default -> assign-move + leaf::result r1; + BOOST_TEST(r1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + } + + { //void default -> capture -> copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + leaf::result r2 = r1; + BOOST_TEST(r2); + } + + { //void default -> capture -> move + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + } + + { //void default -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + } + + { //void default -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + } + + // ^^ void default ^^ + // vv void error vv + + { //void error move -> copy + static_store exp; + exp.set_reset(true); + leaf::result r1 = leaf::error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { //void error copy -> copy + static_store exp; + exp.set_reset(true); + leaf::error e( e_err{ } ); + leaf::result r1 = e; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { //void error move -> move + static_store exp; + exp.set_reset(true); + leaf::result r1 = leaf::error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { //void error copy -> move + static_store exp; + leaf::error e( e_err{ } ); + leaf::result r1 = e; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { //void error move -> assign copy + static_store exp; + leaf::result r1 = leaf::error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { //void error copy -> assign copy + static_store exp; + leaf::error e( e_err{ } ); + leaf::result r1 = e; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { //void error move -> assign move + static_store exp; + leaf::result r1 = leaf::error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + exp.handle_error(r2.error(),0,[ ]{ }); + BOOST_TEST(err::count==1); + } + BOOST_TEST(err::count==0); + { //void error copy -> assign move + static_store exp; + leaf::error e( e_err{ } ); + leaf::result r1 = e; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { //void error move -> capture -> copy + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { //void error copy -> capture -> copy + leaf::result r1 = capture_result( [ ] { leaf::error e( e_err{ } ); return leaf::result(e); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { //void error move -> capture -> move + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { //void error copy -> capture -> move + leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { //void error move -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { //void error copy -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { //void error move -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { //void error copy -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + return boost::report_errors(); +} diff --git a/test/result_test.1.cpp b/test/result_test.1.cpp deleted file mode 100644 index 76066e2..0000000 --- a/test/result_test.1.cpp +++ /dev/null @@ -1,90 +0,0 @@ -//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) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct my_value -{ -}; - -template -struct info -{ - int value; -}; - -leaf::result f1( bool success ) -{ - if( success ) - return { }; - else - return leaf::error( info<1>{1} ); -} - -leaf::result f2( bool success ) -{ - leaf::expect> exp; - if( leaf::result r=f1(success) ) - return r; - else - return r.error( info<2>{2} ); -} - -leaf::result f3( bool success ) -{ - leaf::expect,info<3>> exp; - auto propagate = leaf::preload( info<4>{4} ); - return f2(success); -} - -leaf::result f4( bool success ) -{ - leaf::expect,info<2>,info<3>,info<4>> exp; - if( leaf::result r = f3( success ) ) - return r; - else - { - int c1=0, c2=0; - bool handled = exp.handle_error( r, - [&c1]( info<1>, info<2>, info<3>, info<4> ) - { - ++c1; - }, - [&c2]( info<1> const & i1, info<2> const & i2, info<4> const & i4 ) - { - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c2; - } ); - BOOST_TEST(handled); - BOOST_TEST(c1==0); - BOOST_TEST(c2==1); - return leaf::error(); - } -} - -int main() -{ - leaf::expect,info<3>,info<4>> exp; - BOOST_TEST(f4(true)); - leaf::result r=f4(false); - BOOST_TEST(!r); - leaf::error e = r.error(); - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(!exp.peek>(e)); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - return boost::report_errors(); -} diff --git a/test/result_test.2.cpp b/test/result_test.2.cpp deleted file mode 100644 index 2ef6566..0000000 --- a/test/result_test.2.cpp +++ /dev/null @@ -1,638 +0,0 @@ -//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) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct val -{ - static int count; - - val() - { - ++count; - } - - val( val const & ) - { - ++count; - } - - val( val && ) - { - ++count; - } - - ~val() - { - --count; - } -}; -int val::count = 0; - -struct err -{ - static int count; - - err() - { - ++count; - } - - err( err const & ) - { - ++count; - } - - err( err && ) - { - ++count; - } - - ~err() - { - --count; - } -}; -int err::count = 0; -struct e_err { err value; }; - -int main() -{ - { //value default-copy - leaf::expect exp; - leaf::result r1; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-copy - leaf::expect exp; - leaf::result r1 = val(); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value copy-copy - leaf::expect exp; - val v; - leaf::result r1 = v; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default-move - leaf::expect exp; - leaf::result r1; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-move - leaf::expect exp; - leaf::result r1 = val(); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-move - leaf::expect exp; - val v; - leaf::result r1 = v; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default-assign copy - leaf::expect exp; - leaf::result r1; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-assign copy - leaf::expect exp; - leaf::result r1 = val(); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-assign copy - leaf::expect exp; - val v; - leaf::result r1 = v; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default-assign move - leaf::expect exp; - leaf::result r1; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-assign move - leaf::expect exp; - leaf::result r1 = val(); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-assign move - leaf::expect exp; - val v; - leaf::result r1 = v; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default - capture - copy - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result()); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - copy - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result(val())); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value copy - capture - copy - leaf::expect exp; - val v; - leaf::result r1 = capture(exp,leaf::result(v)); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default - capture - move - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result()); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - move - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result(val())); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - move - leaf::expect exp; - val v; - leaf::result r1 = capture(exp,leaf::result(v)); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default - capture - assign copy - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result()); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - assign copy - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result(val())); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - assign copy - leaf::expect exp; - val v; - leaf::result r1 = capture(exp,leaf::result(v)); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default - capture - assign move - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result()); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - assign move - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result(val())); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - assign move - leaf::expect exp; - val v; - leaf::result r1 = capture(exp,leaf::result(v)); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - copy - leaf::expect exp; - leaf::result r1 = leaf::error( e_err { } ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = r1; - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - copy - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = e; - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = r1; - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - move - leaf::expect exp; - leaf::result r1 = leaf::error( e_err { } ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - move - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = e; - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - assign copy - leaf::expect exp; - leaf::result r1 = leaf::error( e_err { } ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=r1; - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - assign copy - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = e; - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=r1; - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - assign move - leaf::expect exp; - leaf::result r1 = leaf::error( e_err { } ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - assign move - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = e; - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - capture - copy - leaf::expect exp; - leaf::result r1 = capture( exp, leaf::result( leaf::error( e_err { } ) ) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = r1; - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - capture - copy - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = capture( exp, leaf::result(e) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = r1; - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - capture - move - leaf::expect exp; - leaf::result r1 = capture( exp, leaf::result( leaf::error( e_err { } ) ) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - capture - move - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = capture( exp, leaf::result(e) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - capture - assign copy - leaf::expect exp; - leaf::result r1 = capture( exp, leaf::result( leaf::error( e_err { } ) ) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=r1; - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - capture - assign copy - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = capture( exp, leaf::result(e) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=r1; - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - capture - assign move - leaf::expect exp; - leaf::result r1 = capture( exp, leaf::result( leaf::error( e_err { } ) ) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - capture - assign move - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = capture( exp, leaf::result(e) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(exp.handle_error(r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - return boost::report_errors(); -} diff --git a/test/result_test.3.cpp b/test/result_test.3.cpp deleted file mode 100644 index 25d2545..0000000 --- a/test/result_test.3.cpp +++ /dev/null @@ -1,59 +0,0 @@ -//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) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct err -{ - static int count; - - err() - { - ++count; - } - - err( err const & ) - { - ++count; - } - - err( err && ) - { - ++count; - } - - ~err() - { - --count; - } -}; -int err::count = 0; -struct e_err1 { err value; }; -struct e_err2 { err value; }; - -int main() -{ - leaf::result r0; - { - leaf::expect exp; - leaf::result r1 = leaf::error( e_err1{ } ); - BOOST_TEST(!r1); - r0 = capture(exp,r1); - BOOST_TEST(err::count==1); - } - BOOST_TEST(err::count==1); - BOOST_TEST(!r0); - leaf::expect exp; - leaf::result r2 = r0.error( e_err2{ } ); - BOOST_TEST(err::count==2); - BOOST_TEST(exp.handle_error(r2,[ ](e_err1,e_err2){ })); - return boost::report_errors(); -} diff --git a/test/result_test.4.cpp b/test/result_test.4.cpp deleted file mode 100644 index 76af89f..0000000 --- a/test/result_test.4.cpp +++ /dev/null @@ -1,81 +0,0 @@ -//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) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template -void check( Expect & exp, leaf::bad_result const & e ) -{ - exp.handle_exception( e, - [ ]( leaf::e_source_location const & x ) - { - BOOST_TEST(strstr(x.file,"result.hpp")!=0); - BOOST_TEST(x.line>0); - BOOST_TEST(strstr(x.function,"value")!=0); - } ); -} - -int main() -{ - { - leaf::expect exp; - try - { - leaf::result r((leaf::error())); - (void) r.value(); - BOOST_TEST(false); - } - catch( leaf::bad_result const & e ) - { - check(exp,e); - } - } - { - leaf::expect exp; - try - { - leaf::result const r((leaf::error())); - (void) r.value(); - BOOST_TEST(false); - } - catch( leaf::bad_result const & e ) - { - check(exp,e); - } - } - { - leaf::expect exp; - try - { - leaf::result r((leaf::error())); - (void) *r; - BOOST_TEST(false); - } - catch( leaf::bad_result const & e ) - { - check(exp,e); - } - } - { - leaf::expect exp; - try - { - leaf::result const r((leaf::error())); - (void) *r; - BOOST_TEST(false); - } - catch( leaf::bad_result const & e ) - { - check(exp,e); - } - } - return boost::report_errors(); -} diff --git a/test/result_test.5.cpp b/test/result_test.5.cpp deleted file mode 100644 index 1eefb84..0000000 --- a/test/result_test.5.cpp +++ /dev/null @@ -1,186 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -leaf::result f() -{ - return leaf::error(info<1>{1},info<3>{3}); -} - -int main() -{ - leaf::expect,info<2>,info<3>> exp; - - { - int c=0; - bool r = exp.handle_error( f(), - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - c = 1; - } ); - BOOST_TEST(r); - BOOST_TEST(c==1); - } - - { - int c=0; - bool r = exp.handle_error( f(), - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - c = 1; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==0); - } - - { - int c=0; - int r = exp.handle_error( f(), - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - return c = 1; - } ); - BOOST_TEST(r==1); - } - - { - int c=0; - int r = exp.handle_error( f(), - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - return c = 1; - } ); - BOOST_TEST(r==-1); - } - - { - int c=0; - bool r = exp.handle_error( f(), - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - return false; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==2); - } - - { - int c=0; - bool r = exp.handle_error( f(), - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - return false; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - return false; - } ); - BOOST_TEST(r); - BOOST_TEST(c==2); - } - - { - int c=0; - leaf::result r = exp.handle_error( f(), - [&c]( info<1> const & x, info<3> const & ) -> leaf::result - { - BOOST_TEST(c==0); - c = 1; - return 42; - } ); - BOOST_TEST(*r==42); - BOOST_TEST(c==1); - } - - { - int c=0; - leaf::result r1 = f(); - leaf::expect> exp1; - leaf::result r2 = exp.handle_error( r1, - [&c]( info<1> const & x, info<3> const & ) -> leaf::result - { - BOOST_TEST(c==0); - c = 1; - return leaf::error(info<42>{42}); - } ); - BOOST_TEST(c==1); - c = 0; - bool b = exp1.handle_error( r2, - [&c]( info<42> const & x ) - { - BOOST_TEST(c==0); - c = 1; - BOOST_TEST(x.value==42); - } ); - BOOST_TEST(b); - BOOST_TEST(c==1); - } - - { - int c=0; - leaf::result r1 = f(); - leaf::result r2 = exp.handle_error( r1, - [&c]( info<2> const & ) -> leaf::result - { - BOOST_TEST(c==0); - c = 1; - return 42; - } ); - BOOST_TEST(c==0); - bool b = exp.handle_error( r2, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 1; - } ); - BOOST_TEST(b); - BOOST_TEST(c==1); - } - - return boost::report_errors(); -} diff --git a/test/result_test.6.cpp b/test/result_test.6.cpp deleted file mode 100644 index 89490b8..0000000 --- a/test/result_test.6.cpp +++ /dev/null @@ -1,182 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -enum class error_code -{ - error1=1, - error2, - error3 -}; - -namespace boost { namespace leaf { - template <> struct is_error_type: public std::true_type { }; -} } - -leaf::result compute_answer( int what_to_do ) noexcept -{ - switch( what_to_do ) - { - case 0: - return 42; - case 1: - return leaf::error(error_code::error1); - case 2: - return leaf::error(error_code::error2); - default: - assert(what_to_do==3); - return leaf::error(error_code::error3); - } -} - -leaf::result handle_some_errors( int what_to_do ) -{ - leaf::expect exp; - if( leaf::result r = compute_answer(what_to_do) ) - return r; - else - return exp.handle_error( r, - [&r]( error_code ec ) -> leaf::result - { - if( ec==error_code::error1 ) - return 42; - else - return r; - } ); -} - -leaf::result handle_some_errors_float( int what_to_do ) -{ - leaf::expect exp; - if( leaf::result r = compute_answer(what_to_do) ) - return r; - else - return exp.handle_error( r, - [&r]( error_code ec ) -> leaf::result - { - if( ec==error_code::error2 ) - return 42.0f; - else - return r; - } ); -} - -leaf::result handle_some_errors_void( int what_to_do ) -{ - leaf::expect exp; - if( leaf::result r = compute_answer(what_to_do) ) - return { }; - else - return exp.handle_error( r, - [&r]( error_code ec ) -> leaf::result - { - if( ec==error_code::error3 ) - return { }; - else - return r.error(); - } ); -} - -int main() -{ - leaf::expect exp; - - /////////////////////////// - - BOOST_TEST(handle_some_errors(0).value()==42); - BOOST_TEST(handle_some_errors(1).value()==42); - { - leaf::result r = handle_some_errors(2); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( exp.handle_error( r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error2); - ++c; - } ) ); - BOOST_TEST(c==1); - } - { - leaf::result r = handle_some_errors(3); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( exp.handle_error( r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error3); - ++c; - } ) ); - BOOST_TEST(c==1); - } - - /////////////////////////// - - BOOST_TEST(handle_some_errors_float(0).value()==42); - { - leaf::result r = handle_some_errors_float(1); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( exp.handle_error( r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error1); - ++c; - } ) ); - BOOST_TEST(c==1); - } - BOOST_TEST(handle_some_errors_float(2).value()==42.0f); - { - leaf::result r = handle_some_errors_float(3); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( exp.handle_error( r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error3); - ++c; - } ) ); - BOOST_TEST(c==1); - } - - /////////////////////////// - - BOOST_TEST(handle_some_errors_void(0)); - { - leaf::result r = handle_some_errors_void(1); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( exp.handle_error( r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error1); - ++c; - } ) ); - BOOST_TEST(c==1); - } - { - leaf::result r = handle_some_errors_void(2); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( exp.handle_error( r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error2); - ++c; - } ) ); - BOOST_TEST(c==1); - } - BOOST_TEST(handle_some_errors_void(3)); - - /////////////////////////// - - return boost::report_errors(); -} diff --git a/test/result_void_capture_test.cpp b/test/result_void_capture_test.cpp deleted file mode 100644 index 43b2807..0000000 --- a/test/result_void_capture_test.cpp +++ /dev/null @@ -1,78 +0,0 @@ -//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) - -#include -#include -#include "boost/core/lightweight_test.hpp" -#include -#include -#include -#include - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -struct fut_info -{ - int a; - int b; - int result; - std::future> fut; -}; - -template -std::vector launch_tasks( int task_count, F f ) -{ - assert(task_count>0); - std::vector fut; - std::generate_n( std::inserter(fut,fut.end()), task_count, [f] - { - int const a = rand(); - int const b = rand(); - int const res = (rand()%10) - 5; - return fut_info { a, b, res, std::async( std::launch::async, [f,a,b,res] - { - leaf::expect,info<2>,info<3>> exp; - return capture(exp,f(a,b,res)); - } ) }; - } ); - return fut; -} - -int main() -{ - std::vector fut = launch_tasks( 42, [ ]( int a, int b, int res ) noexcept -> leaf::result - { - if( res>=0 ) - return { }; - else - return leaf::error(info<1>{a},info<2>{b},info<3>{}); - } ); - - for( auto & f : fut ) - { - using namespace leaf::leaf_detail; - f.fut.wait(); - leaf::expect,info<2>,info<4>> exp; - if( leaf::result r = f.fut.get() ) - { - } - else - { - int c=0; - bool handled = exp.handle_error( r, [&f,&c]( info<1> const & x1, info<2> const & x2 ) - { - BOOST_TEST(x1.value==f.a); - BOOST_TEST(x2.value==f.b); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - } - return boost::report_errors(); -} diff --git a/test/static_store_deduction_test.cpp b/test/static_store_deduction_test.cpp new file mode 100644 index 0000000..d6c3257 --- /dev/null +++ b/test/static_store_deduction_test.cpp @@ -0,0 +1,95 @@ +//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) + +#include + +namespace leaf = boost::leaf; + +template +typename leaf::leaf_detail::deduce_static_store::type>::type * expd( Handlers && ... ) +{ + return 0; +} + +template +void test( U * ) +{ + static_assert(std::is_same::value,"static_store_deduction"); +} + +template struct info { int value; }; + +enum class my_error_code +{ + ok, + error1, + error2, + error3 +}; +namespace boost { namespace leaf { + template <> struct is_error_type: std::true_type { }; +} } + +void not_called_on_purpose() +{ + using leaf::leaf_detail::static_store; + + test< static_store> >( expd([ ]( info<1> ){ }) ); + test< static_store> >( expd([ ]( info<1> const ){ }) ); + test< static_store> >( expd([ ]( info<1> const & ){ }) ); + test< static_store> >( expd([ ]( info<1>, leaf::error ){ }) ); + test< static_store> >( expd([ ]( info<1>, leaf::error const ){ }) ); + test< static_store> >( expd([ ]( info<1>, leaf::error const & ){ }) ); + + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); + + test< static_store,info<2>> >( expd([ ]( leaf::error, info<1> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( leaf::error, info<1> ){ }, [ ]( info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( leaf::error, info<1>, info<2> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( leaf::error, info<1>, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); + + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error ){ }, [ ]( info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error, info<2> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); + + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( leaf::error, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( leaf::error, info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( leaf::error, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( leaf::error, info<1>, info<2> ){ }) ); + + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<2>, leaf::error ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, leaf::error, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2>, leaf::error ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<1>, leaf::error, info<2> ){ }) ); + + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, info<2> ){ }, [ ]( info<1>, info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, info<2> ){ }, [ ]( info<1>, info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2> ){ }, [ ]( info<3>, info<2> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2> ){ }, [ ]( info<3>, info<2> ){ }) ); + test< static_store,info<3>,info<2>> >( expd([ ]( info<1>, info<3> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<3>,info<2>> >( expd([ ]( info<1>, info<3> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2>, info<3> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2>, info<3> ){ }, [ ]( info<3> ){ }) ); + + test< static_store >( expd([ ]( leaf::match ){ }) ); + test< static_store> >( expd([ ]( leaf::match,42> ){ }) ); + test< static_store> >( expd([ ]( leaf::catch_, info<1> ){ }) ); + + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> const *, info<2> ){ }, [ ]( info<1>, info<3> const * ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> const, info<2> ){ }, [ ]( info<1> const *, info<3> ){ }) ); +} + +int main() +{ + return 0; +} diff --git a/test/basic_test.cpp b/test/static_store_test.cpp similarity index 71% rename from test/basic_test.cpp rename to test/static_store_test.cpp index 013c6e5..c073c5d 100644 --- a/test/basic_test.cpp +++ b/test/static_store_test.cpp @@ -4,7 +4,7 @@ //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) -#include +#include #include "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; @@ -37,7 +37,9 @@ leaf::error f4() int main() { - leaf::expect,info<2>,info<4>> exp0; + using leaf::leaf_detail::static_store; + + static_store,info<2>,info<4>> exp0; leaf::error e0 = f4(); { info<1> const * p = exp0.peek>(e0); @@ -50,7 +52,7 @@ int main() } BOOST_TEST(!exp0.peek>(e0)); - leaf::expect,info<2>,info<4>> exp; + static_store,info<2>,info<4>> exp; leaf::error e1 = f4(); { @@ -79,7 +81,18 @@ int main() } BOOST_TEST(!exp.peek>(e1)); - BOOST_TEST( !exp.handle_error( e1, [ ](info<1>,info<2>,info<4>)->void { } ) ); + { + int r = exp.handle_error( e1, 0, + [ ](info<1>,info<2>,info<4>) + { + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==2); + } leaf::error e2 = f4(); { @@ -95,39 +108,41 @@ int main() BOOST_TEST(!exp.peek>(e2)); { - int c1=0, c2=0, c3=0; - bool handled = exp.handle_error( e2, - [&c1]( info<1>, info<2>, info<4> ) + int r = exp.handle_error( e2, 0, + [ ]( info<1>, info<2>, info<4> ) { - ++c1; + return 1; }, - [&c2]( info<1>, info<2>, info<4> ) + [ ]( info<1>, info<2>, info<4> ) { - ++c2; + return 2; }, - [&c3]( info<2> const & i2, info<1> const & i1 ) + [ ]( info<2> const & i2, info<1> const & i1 ) { BOOST_TEST(i1.value==1); BOOST_TEST(i2.value==2); - ++c3; + return 3; + }, + [ ] + { + return 4; } ); - BOOST_TEST(handled); - BOOST_TEST(c1==0); - BOOST_TEST(c2==0); - BOOST_TEST(c3==1); + BOOST_TEST(r==3); } { - int c=0; - bool handled = exp0.handle_error( e0, - [&c]( info<2> const & i2, info<1> const & i1 ) + int r = exp0.handle_error( e0, 0, + [ ]( info<2> const & i2, info<1> const & i1 ) { BOOST_TEST(i1.value==1); BOOST_TEST(i2.value==2); - ++c; + return 1; + }, + [ ] + { + return 2; } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); + BOOST_TEST(r==1); } return boost::report_errors(); diff --git a/test/try_exception_test.cpp b/test/try_exception_test.cpp new file mode 100644 index 0000000..47629b1 --- /dev/null +++ b/test/try_exception_test.cpp @@ -0,0 +1,62 @@ +//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) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +struct my_error: std::exception { }; + +template +int test( F && f ) +{ + return leaf::try_( + [&]() -> int + { + f(); + return 1; + }, + [ ]( leaf::catch_, info, leaf::e_source_location ) + { + return 2; + }, + [ ]( leaf::catch_, info x ) + { + return 3; + }, + [ ]( leaf::catch_, leaf::e_source_location ) + { + return 4; + }, + [ ]( leaf::catch_ ) + { + return 5; + }, + [ ] + { + return 6; + } ); +} + +int main() +{ + BOOST_TEST( 5==test( [ ] { throw my_error(); } ) ); + + BOOST_TEST( 5==test( [ ] { throw leaf::exception(my_error()); } ) ); + BOOST_TEST( 3==test( [ ] { throw leaf::exception(my_error(),info{42}); } ) ); + + BOOST_TEST( 4==test( [ ] { throw LEAF_EXCEPTION(my_error()); } ) ); + BOOST_TEST( 2==test( [ ] { throw LEAF_EXCEPTION(my_error(),info{42}); } ) ); + + BOOST_TEST( 4==test( [ ] { LEAF_THROW(my_error()); } ) ); + BOOST_TEST( 2==test( [ ] { LEAF_THROW(my_error(),info{42}); } ) ); + + return boost::report_errors(); +} diff --git a/test/try_test.cpp b/test/try_test.cpp new file mode 100644 index 0000000..735fe91 --- /dev/null +++ b/test/try_test.cpp @@ -0,0 +1,522 @@ +//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) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +struct error1: std::exception { }; +struct error2: std::exception { }; +struct error3: std::exception { }; + +template +R f( Ex && ex ) +{ + throw leaf::exception(std::move(ex),info<1>{1},info<2>{2},info<3>{3}); +} + +template +R f() +{ + return R(42); +} + +int main() +{ + //void, handle_all (success) + { + int c=0; + leaf::try_( + [&c] + { + c = f(); + }, + [&c] + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(c==42); + } + + //void, handle_all (failure), match_enum (single enum value) + { + int c=0; + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_all (failure), match_enum (multiple enum values) + { + int c=0; + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_all (failure), match_value (single value) + { + int c=0; + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_all (failure), match_value (multiple values) + { + int c=0; + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + ////////////////////////////////////// + + //void, handle_some (failure, initially not matched), match_enum (single enum value) + { + int c=0; + leaf::try_( + [&c] + { + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(false); + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_some (failure, initially not matched), match_enum (multiple enum values) + { + int c=0; + leaf::try_( + [&c] + { + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(false); + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + //void, handle_some (failure, initially matched), match_enum (single enum value) + { + int c=0; + leaf::try_( + [&c] + { + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + //void, handle_some (failure, initially matched), match_enum (multiple enum values) + { + int c=0; + leaf::try_( + [&c] + { + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + ////////////////////////////////////// + + //int, handle_all (success) + { + int r = leaf::try_( + [ ] + { + return f(); + }, + [ ] + { + return 1; + } ); + BOOST_TEST(r==42); + } + + //int, handle_all (failure), match_enum (single enum value) + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_all (failure), match_enum (multiple enum values) + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + ////////////////////////////////////// + + //int, handle_some (failure, matched), match_enum (single enum value) + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r==2); + } + + //int, handle_some (failure, matched), match_enum (multiple enum values) + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r==2); + } + + //int, handle_some (failure, initially not matched), match_enum (single enum value) + { + int r = leaf::try_( + [ ] + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + } ); + BOOST_TEST(false); + return r; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_some (failure, initially not matched), match_enum (multiple enum values) + { + int r = leaf::try_( + [ ] + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + } ); + BOOST_TEST(false); + return r; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + //int, handle_some (failure, initially matched), match_enum (single enum value) + { + int r = leaf::try_( + [ ] + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r==1); + return r; + }, + [ ]( leaf::catch_ ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + //int, handle_some (failure, initially matched), match_enum (multiple enum values) + { + int r = leaf::try_( + [ ] + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r==1); + return r; + }, + [ ]( leaf::catch_ ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + ////////////////////////////////////// + + return boost::report_errors(); +} From b385bbf67cad6d24cefae65078b9353930657ac9 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Wed, 9 Jan 2019 17:38:23 -0800 Subject: [PATCH 07/34] warnings/errors --- include/boost/leaf/capture_exception.hpp | 1 + test/Jamfile.v2 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp index 3bef4e7..af0aba4 100644 --- a/include/boost/leaf/capture_exception.hpp +++ b/include/boost/leaf/capture_exception.hpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace boost { namespace leaf { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index ce1696f..473fd07 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -8,7 +8,7 @@ import testing ; project : requirements - gcc:"-std=c++11 -Wno-delete-non-virtual-dtor" + gcc:"-std=c++11 -Wno-delete-non-virtual-dtor -Wno-parentheses" gcc:"-pthread" darwin:"-std=c++11 -Wno-unused-variable -Wno-delete-non-virtual-dtor -Wno-non-virtual-dtor -Wno-dangling-else" msvc:"-wd 4267 -wd 4996 -wd 4244" From 05e1e56ad8d5f1fb1af8c31573d094100c7b6f06 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Wed, 9 Jan 2019 18:24:18 -0800 Subject: [PATCH 08/34] compile warnings/errors --- include/boost/leaf/handle.hpp | 2 +- test/Jamfile.v2 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/leaf/handle.hpp b/include/boost/leaf/handle.hpp index 177b01e..8fa33b9 100644 --- a/include/boost/leaf/handle.hpp +++ b/include/boost/leaf/handle.hpp @@ -51,7 +51,7 @@ namespace boost { namespace leaf { f_(std::forward(f)) { } - result operator()( A... a ) noexcept + result operator()( A... a ) { f_(a...); return { }; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 473fd07..3ea460e 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -10,6 +10,7 @@ project : requirements gcc:"-std=c++11 -Wno-delete-non-virtual-dtor -Wno-parentheses" gcc:"-pthread" + clang:"-Wno-dangling-else" darwin:"-std=c++11 -Wno-unused-variable -Wno-delete-non-virtual-dtor -Wno-non-virtual-dtor -Wno-dangling-else" msvc:"-wd 4267 -wd 4996 -wd 4244" ../../.. From 6e466d8ce3f945fd0ac37e121fe363be62cecd3b Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Wed, 9 Jan 2019 22:43:50 -0800 Subject: [PATCH 09/34] warnings, errors --- example/capture_eh.cpp | 1 + example/capture_result.cpp | 1 + include/boost/leaf/capture_exception.hpp | 1 + include/boost/leaf/handle.hpp | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/example/capture_eh.cpp b/example/capture_eh.cpp index 77780f2..7213e19 100644 --- a/example/capture_eh.cpp +++ b/example/capture_eh.cpp @@ -70,6 +70,7 @@ int main() //Success! Use r to access task_result. std::cout << "Success!" << std::endl; + (void) r; }, diff --git a/example/capture_result.cpp b/example/capture_result.cpp index bdf9b9d..8eaf3cb 100644 --- a/example/capture_result.cpp +++ b/example/capture_result.cpp @@ -70,6 +70,7 @@ int main() //Success! Use r to access task_result. std::cout << "Success!" << std::endl; + (void) r; return { }; }, diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp index af0aba4..8429d4e 100644 --- a/include/boost/leaf/capture_exception.hpp +++ b/include/boost/leaf/capture_exception.hpp @@ -43,6 +43,7 @@ namespace boost { namespace leaf { { error err = ds->unload(); assert(err==*this); + (void) err; } else ds->unload(next_error_value()); diff --git a/include/boost/leaf/handle.hpp b/include/boost/leaf/handle.hpp index 8fa33b9..5f002c5 100644 --- a/include/boost/leaf/handle.hpp +++ b/include/boost/leaf/handle.hpp @@ -37,7 +37,7 @@ namespace boost { namespace leaf { f_(std::forward(f)) { } - R operator()( A... a ) noexcept + R operator()( A... a ) { return f_(a...); } From 4f8e4d032ad957ca855e722d38ccfd7188835653 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Wed, 9 Jan 2019 23:05:18 -0800 Subject: [PATCH 10/34] warnings, compile errors --- include/boost/leaf/result.hpp | 1 + include/boost/leaf/try.hpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index 2c1fc3f..af2210b 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -9,6 +9,7 @@ #include #include +#include #define LEAF_AUTO(v,r) auto _r_##v = r; if( !_r_##v ) return _r_##v.error(); auto & v = *_r_##v #define LEAF_CHECK(r) {auto _r = r; if( !_r ) return _r.error();} diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp index b20e5ba..0eb1db9 100644 --- a/include/boost/leaf/try.hpp +++ b/include/boost/leaf/try.hpp @@ -47,6 +47,10 @@ namespace boost { namespace leaf { { return ss.handle_error(get_error(ex), &ex, std::forward(handlers)..., [ ]() -> typename leaf_detail::function_traits::return_type { throw; }); } + catch( ... ) + { + return ss.handle_error(next_error_value(), 0, std::forward(handlers)..., [ ]() -> typename leaf_detail::function_traits::return_type { throw; }); + } } namespace leaf_detail From c5441b9c522a875bf2aef09719a4c5d5cad3fc63 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Wed, 9 Jan 2019 23:19:49 -0800 Subject: [PATCH 11/34] compile errors, warnings --- include/boost/leaf/preload.hpp | 1 + include/boost/leaf/try.hpp | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/boost/leaf/preload.hpp b/include/boost/leaf/preload.hpp index ffde506..803efde 100644 --- a/include/boost/leaf/preload.hpp +++ b/include/boost/leaf/preload.hpp @@ -8,6 +8,7 @@ //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include namespace boost { namespace leaf { diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp index 0eb1db9..b20e5ba 100644 --- a/include/boost/leaf/try.hpp +++ b/include/boost/leaf/try.hpp @@ -47,10 +47,6 @@ namespace boost { namespace leaf { { return ss.handle_error(get_error(ex), &ex, std::forward(handlers)..., [ ]() -> typename leaf_detail::function_traits::return_type { throw; }); } - catch( ... ) - { - return ss.handle_error(next_error_value(), 0, std::forward(handlers)..., [ ]() -> typename leaf_detail::function_traits::return_type { throw; }); - } } namespace leaf_detail From 882d62d2c784e5bb7a6225833a91a99b3837a136 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Wed, 9 Jan 2019 23:39:24 -0800 Subject: [PATCH 12/34] compile errors --- include/boost/leaf/detail/static_store.hpp | 2 +- include/boost/leaf/error.hpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index c731b58..d04ac3a 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -296,7 +296,7 @@ namespace boost { namespace leaf { struct get_one_argument { template - static error get( StaticStore const &, error const & e ) noexcept + static error const & get( StaticStore const &, error const & e ) noexcept { return e; } diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index a9b398f..e12329a 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -166,6 +166,9 @@ namespace boost { namespace leaf { public: + error( error const & ) noexcept = default; + error( error && ) noexcept = default; + error() noexcept: id_(id_factory::tl_instance().get()) { From b1da5131ea5040795df6a732a9502a17bf6ae34c Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Wed, 9 Jan 2019 23:54:24 -0800 Subject: [PATCH 13/34] tryin to fix gcc compile errors --- include/boost/leaf/error.hpp | 7 +++++++ include/boost/leaf/try.hpp | 2 ++ 2 files changed, 9 insertions(+) diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index e12329a..3bdca90 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -174,6 +174,13 @@ namespace boost { namespace leaf { { } + template + explicit error( E && e ) noexcept: + id_(id_factory::tl_instance().get()) + { + propagate(std::forward(e)); + } + template explicit error( E && ... e ) noexcept: id_(id_factory::tl_instance().get()) diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp index b20e5ba..a832dc1 100644 --- a/include/boost/leaf/try.hpp +++ b/include/boost/leaf/try.hpp @@ -37,6 +37,8 @@ namespace boost { namespace leaf { try { cap.unload_and_rethrow_original_exception(); + assert(0); + throw; } catch( std::exception const & ex ) { From a16600a9893f2cbb1e024b181752f6cb5fbabfb4 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Thu, 10 Jan 2019 09:29:48 -0800 Subject: [PATCH 14/34] error constructor -> new_error --- example/capture_result.cpp | 2 +- example/lua_callback_result.cpp | 2 +- example/print_file_result.cpp | 16 ++--- example/print_half.cpp | 6 +- include/boost/leaf/capture_exception.hpp | 2 +- include/boost/leaf/error.hpp | 27 +++----- include/boost/leaf/exception_to_result.hpp | 4 +- include/boost/leaf/result.hpp | 2 +- include/boost/leaf/throw.hpp | 2 +- test/capture_result_async_test.cpp | 2 +- test/capture_result_state_test.cpp | 2 +- test/capture_result_unload_test.cpp | 2 +- test/defer_basic_test.cpp | 2 +- test/defer_nested_error_result_test.cpp | 2 +- .../defer_nested_new_error_exception_test.cpp | 2 +- test/defer_nested_new_error_result_test.cpp | 4 +- test/defer_nested_success_result_test.cpp | 2 +- test/handle_all_test.cpp | 2 +- test/handle_some_basic_test.cpp | 6 +- test/handle_some_test.cpp | 2 +- test/is_error_type_fail_test.cpp | 2 +- test/multiple_errors_test.cpp | 4 +- test/optional_test.cpp | 7 +- test/preload_basic_test.cpp | 2 +- test/preload_nested_error_result_test.cpp | 2 +- ...reload_nested_new_error_exception_test.cpp | 2 +- test/preload_nested_new_error_result_test.cpp | 4 +- test/preload_nested_success_result_test.cpp | 2 +- test/result_bad_result_test.cpp | 8 +-- test/result_state_test.cpp | 64 +++++++++---------- test/static_store_test.cpp | 2 +- 31 files changed, 89 insertions(+), 101 deletions(-) diff --git a/example/capture_result.cpp b/example/capture_result.cpp index 8eaf3cb..5825ba3 100644 --- a/example/capture_result.cpp +++ b/example/capture_result.cpp @@ -34,7 +34,7 @@ leaf::result task() if( succeed ) return task_result { }; else - return leaf::error( + return leaf::new_error( e_thread_id{std::this_thread::get_id()}, e_failure_info1{"info"}, e_failure_info2{42} ); diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index a7bec65..98758be 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -97,7 +97,7 @@ leaf::result call_lua( lua_State * L ) //error code and an error message. auto propagate = leaf::preload( e_lua_error_message{lua_tostring(L,1)} ); lua_pop(L,1); - return leaf::error( e_lua_pcall_error{err} ); + return leaf::new_error( e_lua_pcall_error{err} ); } else { diff --git a/example/print_file_result.cpp b/example/print_file_result.cpp index b3415f0..deaa20d 100644 --- a/example/print_file_result.cpp +++ b/example/print_file_result.cpp @@ -42,7 +42,7 @@ leaf::result> file_open( char const * file_name ) if( FILE * f = fopen(file_name,"rb") ) return std::shared_ptr(f,&fclose); else - return leaf::error( input_file_open_error, e_file_name{file_name}, e_errno{errno} ); + return leaf::new_error( input_file_open_error, e_file_name{file_name}, e_errno{errno} ); } @@ -51,14 +51,14 @@ leaf::result file_size( FILE & f ) auto propagate = leaf::defer([ ] { return e_errno{errno}; } ); if( fseek(&f,0,SEEK_END) ) - return leaf::error( input_file_size_error ); + return leaf::new_error( input_file_size_error ); int s = ftell(&f); if( s==-1L ) - return leaf::error( input_file_size_error ); + return leaf::new_error( input_file_size_error ); if( fseek(&f,0,SEEK_SET) ) - return leaf::error( input_file_size_error ); + return leaf::new_error( input_file_size_error ); return s; } @@ -68,10 +68,10 @@ leaf::result file_read( FILE & f, void * buf, int size ) { int n = fread(buf,1,size,&f); if( ferror(&f) ) - return leaf::error( input_file_read_error, e_errno{errno} ); + return leaf::new_error( input_file_read_error, e_errno{errno} ); if( n!=size ) - return leaf::error( input_eof_error ); + return leaf::new_error( input_eof_error ); return { }; } @@ -82,7 +82,7 @@ leaf::result parse_command_line( int argc, char const * argv[ ] ) if( argc==2 ) return argv[1]; else - return leaf::error(bad_command_line); + return leaf::new_error(bad_command_line); } @@ -107,7 +107,7 @@ int main( int argc, char const * argv[ ] ) std::cout << buffer; std::cout.flush(); if( std::cout.fail() ) - return leaf::error( cout_error ); + return leaf::new_error( cout_error ); return 0; }, diff --git a/example/print_half.cpp b/example/print_half.cpp index 87b566d..1608e68 100644 --- a/example/print_half.cpp +++ b/example/print_half.cpp @@ -29,13 +29,13 @@ namespace boost { namespace leaf { leaf::result convert(const std::string& str) noexcept { if (str.empty()) - return leaf::error(ConversionErrc::EmptyString); + return leaf::new_error(ConversionErrc::EmptyString); if (!std::all_of(str.begin(), str.end(), ::isdigit)) - return leaf::error(ConversionErrc::IllegalChar); + return leaf::new_error(ConversionErrc::IllegalChar); if (str.length() > 9) - return leaf::error(ConversionErrc::TooLong); + return leaf::new_error(ConversionErrc::TooLong); return atoi(str.c_str()); } diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp index 8429d4e..ec95658 100644 --- a/include/boost/leaf/capture_exception.hpp +++ b/include/boost/leaf/capture_exception.hpp @@ -101,7 +101,7 @@ namespace boost { namespace leaf { } catch(...) { - throw captured_exception_impl( std::current_exception(), std::make_shared>(error(),std::move(ss)), false ); + throw captured_exception_impl( std::current_exception(), std::make_shared>(new_error(),std::move(ss)), false ); } } }; diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 3bdca90..25a5a4c 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -108,6 +108,8 @@ namespace boost { namespace leaf { class error { + template + friend error new_error( E && ... ) noexcept; friend error leaf::next_error_value() noexcept; friend error leaf::last_error_value() noexcept; @@ -169,25 +171,6 @@ namespace boost { namespace leaf { error( error const & ) noexcept = default; error( error && ) noexcept = default; - error() noexcept: - id_(id_factory::tl_instance().get()) - { - } - - template - explicit error( E && e ) noexcept: - id_(id_factory::tl_instance().get()) - { - propagate(std::forward(e)); - } - - template - explicit error( E && ... e ) noexcept: - id_(id_factory::tl_instance().get()) - { - propagate(std::forward(e)...); - } - friend bool operator==( error const & e1, error const & e2 ) noexcept { return e1.id_==e2.id_; @@ -215,6 +198,12 @@ namespace boost { namespace leaf { void diagnostic_output( std::ostream & os ) const; }; + template + error new_error( E && ... e ) noexcept + { + return error(error::id_factory::tl_instance().get()).propagate(std::forward(e)...); + } + inline error next_error_value() noexcept { return error(error::id_factory::tl_instance().next_id()); diff --git a/include/boost/leaf/exception_to_result.hpp b/include/boost/leaf/exception_to_result.hpp index 1c6d293..e6120b6 100644 --- a/include/boost/leaf/exception_to_result.hpp +++ b/include/boost/leaf/exception_to_result.hpp @@ -31,7 +31,7 @@ namespace boost { namespace leaf { } catch( Ex1 const & ex1 ) { - return leaf::error(ex1); + return leaf::new_error(ex1); } } } @@ -45,7 +45,7 @@ namespace boost { namespace leaf { } catch(...) { - return leaf::error(std::current_exception()); + return leaf::new_error(std::current_exception()); } }; diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index af2210b..d6eea25 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -208,7 +208,7 @@ namespace boost { namespace leaf { switch( which_ ) { case leaf_detail::result_variant::value: - return leaf::error(std::forward(e)...); + return leaf::new_error(std::forward(e)...); case leaf_detail::result_variant::cap: { dynamic_store_ptr cap = cap_; diff --git a/include/boost/leaf/throw.hpp b/include/boost/leaf/throw.hpp index a859556..048c4bc 100644 --- a/include/boost/leaf/throw.hpp +++ b/include/boost/leaf/throw.hpp @@ -33,7 +33,7 @@ namespace boost { namespace leaf { template exception( Ex && ex, E && ... e ) noexcept: Ex(std::move(ex)), - error(std::forward(e)...) + error(new_error(std::forward(e)...)) { leaf_detail::enforce_std_exception(*this); } diff --git a/test/capture_result_async_test.cpp b/test/capture_result_async_test.cpp index be62791..324162b 100644 --- a/test/capture_result_async_test.cpp +++ b/test/capture_result_async_test.cpp @@ -54,7 +54,7 @@ int main() if( res>=0 ) return res; else - return leaf::error( info<1>{a}, info<2>{b}, info<3>{} ); + return leaf::new_error( info<1>{a}, info<2>{b}, info<3>{} ); } ) ); for( auto & f : fut ) diff --git a/test/capture_result_state_test.cpp b/test/capture_result_state_test.cpp index 97872bc..211e466 100644 --- a/test/capture_result_state_test.cpp +++ b/test/capture_result_state_test.cpp @@ -41,7 +41,7 @@ int main() auto f = leaf::capture_result, info<2>, info<3>>( [ ]() -> leaf::result { - return leaf::error( info<1>{}, info<3>{} ); + return leaf::new_error( info<1>{}, info<3>{} ); } ); leaf::handle_all( [&f] diff --git a/test/capture_result_unload_test.cpp b/test/capture_result_unload_test.cpp index 8e77f99..0324e91 100644 --- a/test/capture_result_unload_test.cpp +++ b/test/capture_result_unload_test.cpp @@ -157,7 +157,7 @@ int main() test( [ ]() -> leaf::result { - return leaf::error( info<1>{1}, info<3>{3} ); + return leaf::new_error( info<1>{1}, info<3>{3} ); } ); return boost::report_errors(); } diff --git a/test/defer_basic_test.cpp b/test/defer_basic_test.cpp index d7a37f2..24a7113 100644 --- a/test/defer_basic_test.cpp +++ b/test/defer_basic_test.cpp @@ -27,7 +27,7 @@ leaf::error g() global = 0; auto propagate = leaf::defer( [ ] { return info{get_global()}; } ); global = 42; - return leaf::error(); + return leaf::new_error(); } leaf::error f() diff --git a/test/defer_nested_error_result_test.cpp b/test/defer_nested_error_result_test.cpp index eb28094..af76317 100644 --- a/test/defer_nested_error_result_test.cpp +++ b/test/defer_nested_error_result_test.cpp @@ -19,7 +19,7 @@ struct info leaf::error f0() { auto propagate = leaf::defer( [ ] { return info<0>{0}; } ); - return leaf::error( info<2>{2} ); + return leaf::new_error( info<2>{2} ); } leaf::error f1() diff --git a/test/defer_nested_new_error_exception_test.cpp b/test/defer_nested_new_error_exception_test.cpp index c0b8fb9..e780996 100644 --- a/test/defer_nested_new_error_exception_test.cpp +++ b/test/defer_nested_new_error_exception_test.cpp @@ -46,7 +46,7 @@ leaf::error f2() { BOOST_TEST(false); } - return leaf::error(); + return leaf::new_error(); } int main() diff --git a/test/defer_nested_new_error_result_test.cpp b/test/defer_nested_new_error_result_test.cpp index f4e1b20..41e77c7 100644 --- a/test/defer_nested_new_error_result_test.cpp +++ b/test/defer_nested_new_error_result_test.cpp @@ -19,14 +19,14 @@ struct info leaf::error f0() { auto propagate = leaf::defer( [ ] { return info<0>{-1}; } ); - return leaf::error( info<1>{-1} ); + return leaf::new_error( info<1>{-1} ); } leaf::error f1() { auto propagate = leaf::defer( [ ] { return info<0>{0}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{2}; } ); (void) f0(); - return leaf::error(); + return leaf::new_error(); } leaf::error f2() diff --git a/test/defer_nested_success_result_test.cpp b/test/defer_nested_success_result_test.cpp index b7b300b..877aab4 100644 --- a/test/defer_nested_success_result_test.cpp +++ b/test/defer_nested_success_result_test.cpp @@ -20,7 +20,7 @@ leaf::result g1() leaf::result g2() { - return leaf::error(); + return leaf::new_error(); } leaf::result f() diff --git a/test/handle_all_test.cpp b/test/handle_all_test.cpp index c78c439..5744fab 100644 --- a/test/handle_all_test.cpp +++ b/test/handle_all_test.cpp @@ -30,7 +30,7 @@ leaf::result f( error_codes ec ) if( ec==error_codes::ok ) return R(42); else - return leaf::error(ec,error_codes_{ec},info<1>{1},info<2>{2},info<3>{3}); + return leaf::new_error(ec,error_codes_{ec},info<1>{1},info<2>{2},info<3>{3}); } int main() diff --git a/test/handle_some_basic_test.cpp b/test/handle_some_basic_test.cpp index 58c1a48..3236de8 100644 --- a/test/handle_some_basic_test.cpp +++ b/test/handle_some_basic_test.cpp @@ -27,12 +27,12 @@ leaf::result compute_answer( int what_to_do ) noexcept case 0: return 42; case 1: - return leaf::error(error_code::error1); + return leaf::new_error(error_code::error1); case 2: - return leaf::error(error_code::error2); + return leaf::new_error(error_code::error2); default: assert(what_to_do==3); - return leaf::error(error_code::error3); + return leaf::new_error(error_code::error3); } } diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp index 3e39f81..2818c39 100644 --- a/test/handle_some_test.cpp +++ b/test/handle_some_test.cpp @@ -30,7 +30,7 @@ leaf::result f( error_codes ec ) if( ec==error_codes::ok ) return R(42); else - return leaf::error(ec,error_codes_{ec},info<1>{1},info<2>{2},info<3>{3}); + return leaf::new_error(ec,error_codes_{ec},info<1>{1},info<2>{2},info<3>{3}); } int main() diff --git a/test/is_error_type_fail_test.cpp b/test/is_error_type_fail_test.cpp index 813730d..6cc9bf7 100644 --- a/test/is_error_type_fail_test.cpp +++ b/test/is_error_type_fail_test.cpp @@ -13,5 +13,5 @@ struct no_member_value { }; leaf::error f() { //Note: the line below should trigger a compile error (via static_assert). - return leaf::error( no_member_value{ } ); + return leaf::new_error( no_member_value{ } ); } diff --git a/test/multiple_errors_test.cpp b/test/multiple_errors_test.cpp index 92c0e3b..c7d74d9 100644 --- a/test/multiple_errors_test.cpp +++ b/test/multiple_errors_test.cpp @@ -17,12 +17,12 @@ struct info leaf::error f12() { - return leaf::error( info<1>{1}, info<2>{2} ); + return leaf::new_error( info<1>{1}, info<2>{2} ); } leaf::error f34() { - return leaf::error( info<3>{3}, info<4>{4} ); + return leaf::new_error( info<3>{3}, info<4>{4} ); } int main() diff --git a/test/optional_test.cpp b/test/optional_test.cpp index 7cd5a6b..a3b7d0d 100644 --- a/test/optional_test.cpp +++ b/test/optional_test.cpp @@ -9,7 +9,6 @@ namespace leaf = boost::leaf; -struct error { }; int object_count=0; int value_count=0; @@ -64,7 +63,7 @@ public: throws_on_copy( throws_on_copy const & ) { - throw error(); + throw std::exception(); } throws_on_copy( throws_on_copy && ) @@ -107,7 +106,7 @@ void run_tests() optional x(a); BOOST_TEST(false); } - catch( error & ) + catch( std::exception & ) { } } @@ -201,7 +200,7 @@ void run_tests() { (void) (y=x); } - catch( error & ) + catch( std::exception & ) { } BOOST_TEST(object_count==1); diff --git a/test/preload_basic_test.cpp b/test/preload_basic_test.cpp index 28d1b70..8f4e160 100644 --- a/test/preload_basic_test.cpp +++ b/test/preload_basic_test.cpp @@ -18,7 +18,7 @@ struct info leaf::error g() { auto propagate = leaf::preload( info{42} ); - return leaf::error(); + return leaf::new_error(); } leaf::error f() diff --git a/test/preload_nested_error_result_test.cpp b/test/preload_nested_error_result_test.cpp index 85f140e..a7f8304 100644 --- a/test/preload_nested_error_result_test.cpp +++ b/test/preload_nested_error_result_test.cpp @@ -19,7 +19,7 @@ struct info leaf::error f0() { auto propagate = leaf::preload( info<0>{0} ); - return leaf::error( info<2>{2} ); + return leaf::new_error( info<2>{2} ); } leaf::error f1() diff --git a/test/preload_nested_new_error_exception_test.cpp b/test/preload_nested_new_error_exception_test.cpp index 2b3038b..806bc8d 100644 --- a/test/preload_nested_new_error_exception_test.cpp +++ b/test/preload_nested_new_error_exception_test.cpp @@ -46,7 +46,7 @@ leaf::error f2() { BOOST_TEST(false); } - return leaf::error(); + return leaf::new_error(); } int main() diff --git a/test/preload_nested_new_error_result_test.cpp b/test/preload_nested_new_error_result_test.cpp index 1255e16..6ca24ad 100644 --- a/test/preload_nested_new_error_result_test.cpp +++ b/test/preload_nested_new_error_result_test.cpp @@ -19,14 +19,14 @@ struct info leaf::error f0() { auto propagate = leaf::preload( info<0>{-1} ); - return leaf::error( info<1>{-1} ); + return leaf::new_error( info<1>{-1} ); } leaf::error f1() { auto propagate = leaf::preload( info<0>{0}, info<1>{1}, info<2>{2} ); (void) f0(); - return leaf::error(); + return leaf::new_error(); } leaf::error f2() diff --git a/test/preload_nested_success_result_test.cpp b/test/preload_nested_success_result_test.cpp index 2b1142a..ee5dce6 100644 --- a/test/preload_nested_success_result_test.cpp +++ b/test/preload_nested_success_result_test.cpp @@ -20,7 +20,7 @@ leaf::result g1() leaf::result g2() { - return leaf::error(); + return leaf::new_error(); } leaf::result f() diff --git a/test/result_bad_result_test.cpp b/test/result_bad_result_test.cpp index f531426..7520f48 100644 --- a/test/result_bad_result_test.cpp +++ b/test/result_bad_result_test.cpp @@ -24,7 +24,7 @@ int main() int r = leaf::try_( [ ] { - leaf::result r((leaf::error())); + leaf::result r = leaf::new_error(); (void) r.value(); return 0; }, @@ -35,7 +35,7 @@ int main() int r = leaf::try_( [ ] { - leaf::result const r((leaf::error())); + leaf::result const r = leaf::new_error(); (void) r.value(); return 0; }, @@ -46,7 +46,7 @@ int main() int r = leaf::try_( [ ] { - leaf::result r((leaf::error())); + leaf::result r = leaf::new_error(); (void) *r; return 0; }, @@ -57,7 +57,7 @@ int main() int r = leaf::try_( [ ] { - leaf::result const r((leaf::error())); + leaf::result const r = leaf::new_error(); (void) *r; return 0; }, diff --git a/test/result_state_test.cpp b/test/result_state_test.cpp index 721e04b..d70447e 100644 --- a/test/result_state_test.cpp +++ b/test/result_state_test.cpp @@ -409,7 +409,7 @@ int main() { //error move -> copy static_store exp; exp.set_reset(true); - leaf::result r1 = leaf::error( e_err { } ); + leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -422,7 +422,7 @@ int main() { //error copy -> copy static_store exp; exp.set_reset(true); - leaf::error e( e_err{ } ); + leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -437,7 +437,7 @@ int main() { //error move -> move static_store exp; exp.set_reset(true); - leaf::result r1 = leaf::error( e_err { } ); + leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -449,7 +449,7 @@ int main() BOOST_TEST(val::count==0); { //error copy -> move static_store exp; - leaf::error e( e_err{ } ); + leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -463,7 +463,7 @@ int main() { //error move -> assign copy static_store exp; - leaf::result r1 = leaf::error( e_err { } ); + leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -475,7 +475,7 @@ int main() BOOST_TEST(val::count==0); { //error copy -> assign copy static_store exp; - leaf::error e( e_err{ } ); + leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -489,7 +489,7 @@ int main() { //error move -> assign move static_store exp; - leaf::result r1 = leaf::error( e_err { } ); + leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -503,7 +503,7 @@ int main() BOOST_TEST(val::count==0); { //error copy -> assign move static_store exp; - leaf::error e( e_err{ } ); + leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -516,7 +516,7 @@ int main() BOOST_TEST(val::count==0); { //error move -> capture -> copy - leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -527,7 +527,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { //error copy -> capture -> copy - leaf::result r1 = capture_result( [ ] { leaf::error e( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ] { leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -539,7 +539,7 @@ int main() BOOST_TEST(val::count==0); { //error move -> capture -> move - leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -550,7 +550,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { //error copy -> capture -> move - leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -562,7 +562,7 @@ int main() BOOST_TEST(val::count==0); { //error move -> capture -> assign-copy - leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -573,7 +573,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { //error copy -> capture -> assign-copy - leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -585,7 +585,7 @@ int main() BOOST_TEST(val::count==0); { //error move -> capture -> assign-move - leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -596,7 +596,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { //error copy -> capture -> assign-move - leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -675,7 +675,7 @@ int main() { //void error move -> copy static_store exp; exp.set_reset(true); - leaf::result r1 = leaf::error( e_err { } ); + leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = r1; @@ -686,7 +686,7 @@ int main() { //void error copy -> copy static_store exp; exp.set_reset(true); - leaf::error e( e_err{ } ); + leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -699,7 +699,7 @@ int main() { //void error move -> move static_store exp; exp.set_reset(true); - leaf::result r1 = leaf::error( e_err { } ); + leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = std::move(r1); @@ -709,7 +709,7 @@ int main() BOOST_TEST(err::count==0); { //void error copy -> move static_store exp; - leaf::error e( e_err{ } ); + leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -721,7 +721,7 @@ int main() { //void error move -> assign copy static_store exp; - leaf::result r1 = leaf::error( e_err { } ); + leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=r1; @@ -731,7 +731,7 @@ int main() BOOST_TEST(err::count==0); { //void error copy -> assign copy static_store exp; - leaf::error e( e_err{ } ); + leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -743,7 +743,7 @@ int main() { //void error move -> assign move static_store exp; - leaf::result r1 = leaf::error( e_err { } ); + leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=std::move(r1); @@ -754,7 +754,7 @@ int main() BOOST_TEST(err::count==0); { //void error copy -> assign move static_store exp; - leaf::error e( e_err{ } ); + leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -765,7 +765,7 @@ int main() BOOST_TEST(err::count==0); { //void error move -> capture -> copy - leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = r1; @@ -774,7 +774,7 @@ int main() } BOOST_TEST(err::count==0); { //void error copy -> capture -> copy - leaf::result r1 = capture_result( [ ] { leaf::error e( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ] { leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = r1; @@ -784,7 +784,7 @@ int main() BOOST_TEST(err::count==0); { //void error move -> capture -> move - leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = std::move(r1); @@ -793,7 +793,7 @@ int main() } BOOST_TEST(err::count==0); { //void error copy -> capture -> move - leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = std::move(r1); @@ -803,7 +803,7 @@ int main() BOOST_TEST(err::count==0); { //void error move -> capture -> assign-copy - leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=r1; @@ -812,7 +812,7 @@ int main() } BOOST_TEST(err::count==0); { //void error copy -> capture -> assign-copy - leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=r1; @@ -822,7 +822,7 @@ int main() BOOST_TEST(err::count==0); { //void error move -> capture -> assign-move - leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::error( e_err { } ) ); } )(); + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=std::move(r1); @@ -831,7 +831,7 @@ int main() } BOOST_TEST(err::count==0); { //void error copy -> capture -> assign-move - leaf::result r1 = capture_result( [ ]{ leaf::error e( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=std::move(r1); diff --git a/test/static_store_test.cpp b/test/static_store_test.cpp index c073c5d..ec708a4 100644 --- a/test/static_store_test.cpp +++ b/test/static_store_test.cpp @@ -17,7 +17,7 @@ struct info leaf::error f1() { - return leaf::error( info<1>{1} ); + return leaf::new_error( info<1>{1} ); } leaf::error f2() From f11e3b248ef73c4c060d0af0d5e9785b747c90ba Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Thu, 10 Jan 2019 20:51:26 -0800 Subject: [PATCH 15/34] diagnostic info refactor --- .vscode/tasks.json | 267 ++++++++++++++++-- example/capture_eh.cpp | 6 +- example/capture_result.cpp | 6 +- example/exception_to_result.cpp | 10 +- example/print_file_eh.cpp | 8 +- example/print_file_result.cpp | 8 +- example/print_half.cpp | 8 +- include/boost/leaf/capture_exception.hpp | 27 +- .../boost/leaf/detail/captured_exception.hpp | 2 +- include/boost/leaf/detail/dynamic_store.hpp | 2 +- .../boost/leaf/detail/dynamic_store_impl.hpp | 2 +- .../leaf/detail/print_exception_info.hpp | 40 +++ include/boost/leaf/detail/static_store.hpp | 107 +++---- include/boost/leaf/error.hpp | 247 +++++++++++----- include/boost/leaf/handle.hpp | 21 +- include/boost/leaf/preload.hpp | 8 +- include/boost/leaf/try.hpp | 109 +------ meson.build | 2 +- ...tput_test.cpp => diagnostic_info_test.cpp} | 6 +- test/handle_some_test.cpp | 8 +- test/result_state_test.cpp | 6 +- test/static_store_deduction_test.cpp | 36 ++- test/static_store_test.cpp | 6 +- 23 files changed, 619 insertions(+), 323 deletions(-) create mode 100644 include/boost/leaf/detail/print_exception_info.hpp rename test/{diagnostic_output_test.cpp => diagnostic_info_test.cpp} (95%) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index da32954..5e23d46 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -24,66 +24,246 @@ } }, { - "label": "capture_exception_test.1", + "label": "capture_exception_async_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.1", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_async_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.1" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_async_test" } }, { - "label": "capture_exception_test.2", + "label": "capture_exception_state_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.2", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_state_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.2" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_state_test" } }, { - "label": "capture_exception_test.3", + "label": "capture_exception_unload_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.3", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_unload_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_test.3" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_unload_test" } }, { - "label": "capture_result_test.1", + "label": "capture_result_async_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_test.1", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_async_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_test.1" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_async_test" } }, { - "label": "capture_result_test.2", + "label": "capture_result_state_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_test.2", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_state_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_test.2" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_state_test" } }, { - "label": "capture_result_test.3", + "label": "capture_result_unload_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_test.3", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_unload_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_test.3" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_unload_test" } }, { - "label": "handle_test", + "label": "defer_basic_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test handle_test", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_basic_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test handle_test" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_basic_test" + } + }, + { + "label": "defer_nested_error_exception_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_error_exception_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_error_exception_test" + } + }, + { + "label": "defer_nested_error_result_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_error_result_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_error_result_test" + } + }, + { + "label": "defer_nested_new_error_exception_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_new_error_exception_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_new_error_exception_test" + } + }, + { + "label": "defer_nested_new_error_result_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_new_error_result_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_new_error_result_test" + } + }, + { + "label": "defer_nested_success_exception_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_success_exception_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_success_exception_test" + } + }, + { + "label": "defer_nested_success_result_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_success_result_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_success_result_test" + } + }, + { + "label": "diagnostic_info_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test diagnostic_info_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test diagnostic_info_test" + } + }, + { + "label": "function_traits_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test function_traits_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test function_traits_test" + } + }, + { + "label": "handle_all_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test handle_all_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test handle_all_test" + } + }, + { + "label": "handle_some_basic_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test handle_some_basic_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test handle_some_basic_test" + } + }, + { + "label": "handle_some_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test handle_some_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test handle_some_test" + } + }, + { + "label": "is_error_type_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test is_error_type_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test is_error_type_test" + } + }, + { + "label": "optional_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test optional_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test optional_test" + } + }, + { + "label": "preload_basic_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_basic_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_basic_test" + } + }, + { + "label": "preload_nested_error_exception_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_error_exception_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_error_exception_test" + } + }, + { + "label": "preload_nested_error_result_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_error_result_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_error_result_test" + } + }, + { + "label": "preload_nested_new_error_exception_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_new_error_exception_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_new_error_exception_test" + } + }, + { + "label": "preload_nested_new_error_result_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_new_error_result_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_new_error_result_test" + } + }, + { + "label": "preload_nested_success_exception_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_success_exception_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_success_exception_test" + } + }, + { + "label": "preload_nested_success_result_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_success_result_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_success_result_test" } }, { @@ -95,6 +275,51 @@ "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test print_test" } }, + { + "label": "result_bad_result_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test result_bad_result_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_bad_result_test" + } + }, + { + "label": "result_state_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test result_state_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_state_test" + } + }, + { + "label": "static_store_deduction_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test static_store_deduction_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test static_store_deduction_test" + } + }, + { + "label": "static_store_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test static_store_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test static_store_test" + } + }, + { + "label": "try_exception_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test try_exception_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test try_exception_test" + } + }, { "label": "try_test", "type": "shell", diff --git a/example/capture_eh.cpp b/example/capture_eh.cpp index 7213e19..fcb52ba 100644 --- a/example/capture_eh.cpp +++ b/example/capture_eh.cpp @@ -79,12 +79,12 @@ int main() std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; }, - [ ]( leaf::error e ) + [ ]( leaf::error_info const & ei ) { std::cerr << "Unknown failure detected" << std::endl << - "Cryptic diagnostic information follows" << std::endl; - e.diagnostic_output(std::cerr); + "Cryptic diagnostic information follows" << std::endl << + ei; } ); } } diff --git a/example/capture_result.cpp b/example/capture_result.cpp index 5825ba3..e1ec86e 100644 --- a/example/capture_result.cpp +++ b/example/capture_result.cpp @@ -79,12 +79,12 @@ int main() std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; }, - [ ]( leaf::error e ) + [ ]( leaf::error_info const & ei ) { std::cerr << "Unknown failure detected" << std::endl << - "Cryptic diagnostic information follows" << std::endl; - e.diagnostic_output(std::cerr); + "Cryptic diagnostic information follows" << std::endl << + ei; } ); } } diff --git a/example/exception_to_result.cpp b/example/exception_to_result.cpp index 7c63099..9aa0866 100644 --- a/example/exception_to_result.cpp +++ b/example/exception_to_result.cpp @@ -96,7 +96,15 @@ int main() //(with 0 passed for ep). Had we taken it by value or by const &, the program //would not compile. if( ep ) - leaf::diagnostic_output(std::cerr,*ep); + leaf::try_( + [&] + { + std::rethrow_exception(*ep); + }, + [ ]( leaf::error_info const & ei ) + { + std::cerr << ei << std::endl; + } ); } ); } diff --git a/example/print_file_eh.cpp b/example/print_file_eh.cpp index 7ef4f14..9e4c6d1 100644 --- a/example/print_file_eh.cpp +++ b/example/print_file_eh.cpp @@ -137,10 +137,12 @@ int main( int argc, char const * argv[ ] ) return 5; }, - [ ]( leaf::error e ) + [ ]( leaf::error_info const & ei ) { - std::cerr << "Unknown error, cryptic information follows." << std::endl; - e.diagnostic_output(std::cerr); + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + ei; return 6; } ); } diff --git a/example/print_file_result.cpp b/example/print_file_result.cpp index deaa20d..1187795 100644 --- a/example/print_file_result.cpp +++ b/example/print_file_result.cpp @@ -142,10 +142,12 @@ int main( int argc, char const * argv[ ] ) return 5; }, - [ ]( leaf::error e ) + [ ]( leaf::error_info const & ei ) { - std::cerr << "Unknown error, cryptic information follows." << std::endl; - e.diagnostic_output(std::cerr); + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + ei; return 6; } ); diff --git a/example/print_half.cpp b/example/print_half.cpp index 1608e68..0ce11e6 100644 --- a/example/print_half.cpp +++ b/example/print_half.cpp @@ -89,12 +89,14 @@ int main( int argc, char const * argv[ ] ) return 2; }, - [ ]( leaf::error e ) + [ ]( leaf::error_info const & ei ) { //This will never execute in this program, but it would detect logic errors where an unknown error reaches main. //In this case, we print diagnostic information. - std::cerr << "Unknown error, cryptic diagnostic information follows." << std::endl; - e.diagnostic_output(std::cerr); + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + ei; return 3; } ); } diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp index ec95658..b412362 100644 --- a/include/boost/leaf/capture_exception.hpp +++ b/include/boost/leaf/capture_exception.hpp @@ -21,15 +21,15 @@ namespace boost { namespace leaf { public leaf::error { std::exception_ptr ex_; - std::shared_ptr cap_; + std::shared_ptr ds_; bool had_error_; public: - captured_exception_impl( std::exception_ptr && ex, std::shared_ptr && cap, bool had_error ) noexcept: - error(cap->get_error()), + captured_exception_impl( std::exception_ptr && ex, std::shared_ptr && ds, bool had_error ) noexcept: + error(ds->get_error()), ex_(std::move(ex)), - cap_(std::move(cap)), + ds_(std::move(ds)), had_error_(had_error) { assert(ex_); @@ -37,7 +37,7 @@ namespace boost { namespace leaf { [[noreturn]] void unload_and_rethrow_original_exception() { - std::shared_ptr ds; ds.swap(cap_); + std::shared_ptr ds; ds.swap(ds_); assert(ds); if( had_error_ ) { @@ -50,21 +50,10 @@ namespace boost { namespace leaf { std::rethrow_exception(ex_); } - void diagnostic_output( std::ostream & os, void (*printer)(std::ostream &) ) const + void print( std::ostream & os ) const { - assert(cap_); - assert(printer!=0); - os << "Detected exception_capture" << std::endl; - cap_->diagnostic_output(os); - os << std::endl << "Diagnostic Information about the original exception follows" << std::endl; - try - { - std::rethrow_exception(ex_); - } - catch(...) - { - printer(os); - } + assert(ds_); + ds_->print(os); } }; diff --git a/include/boost/leaf/detail/captured_exception.hpp b/include/boost/leaf/detail/captured_exception.hpp index 7ef9f8f..8468bf5 100644 --- a/include/boost/leaf/detail/captured_exception.hpp +++ b/include/boost/leaf/detail/captured_exception.hpp @@ -30,7 +30,7 @@ namespace boost { namespace leaf { public: [[noreturn]] virtual void unload_and_rethrow_original_exception() = 0; - virtual void diagnostic_output( std::ostream &, void (*)(std::ostream &) ) const = 0; + virtual void print( std::ostream & ) const = 0; }; } diff --git a/include/boost/leaf/detail/dynamic_store.hpp b/include/boost/leaf/detail/dynamic_store.hpp index a67a333..3b703bd 100644 --- a/include/boost/leaf/detail/dynamic_store.hpp +++ b/include/boost/leaf/detail/dynamic_store.hpp @@ -32,7 +32,7 @@ namespace boost { namespace leaf { virtual error get_error() const noexcept = 0; virtual error unload() noexcept = 0; virtual error unload( error const & ) noexcept = 0; - virtual void diagnostic_output( std::ostream & ) const = 0; + virtual void print( std::ostream & ) const = 0; }; } diff --git a/include/boost/leaf/detail/dynamic_store_impl.hpp b/include/boost/leaf/detail/dynamic_store_impl.hpp index 21ba532..ca73b41 100644 --- a/include/boost/leaf/detail/dynamic_store_impl.hpp +++ b/include/boost/leaf/detail/dynamic_store_impl.hpp @@ -68,7 +68,7 @@ namespace boost { namespace leaf { return e; } - void diagnostic_output( std::ostream & os ) const + void print( std::ostream & os ) const { os << "Types being captured: "; print_types::print(os); diff --git a/include/boost/leaf/detail/print_exception_info.hpp b/include/boost/leaf/detail/print_exception_info.hpp new file mode 100644 index 0000000..4823bc0 --- /dev/null +++ b/include/boost/leaf/detail/print_exception_info.hpp @@ -0,0 +1,40 @@ +#ifndef BOOST_LEAF_E823AAD6151311E9A430DDBB67511AEB +#define BOOST_LEAF_E823AAD6151311E9A430DDBB67511AEB + +//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) + +#include +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + void print_exception_info( std::ostream & os, std::exception const * ex, captured_exception const * cap ) + { + if( cap ) + { + os << "Detected exception_capture" << std::endl; + cap->print(os); + os << std::endl << "Diagnostic Information about the original exception follows" << std::endl; + } + if( ex ) + { + assert(!dynamic_cast(ex)); + os << + "Exception dynamic type: " << leaf_detail::demangle(typeid(*ex).name()) << std::endl << + "std::exception::what(): " << ex->what() << std::endl; + } + else + os << "Unknown exception type (not a std::exception)" << std::endl; + } + } //namespace leaf_detail + +} } + +#endif diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index d04ac3a..431f230 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -106,15 +106,15 @@ namespace boost { namespace leaf { }; template <> - class static_store_slot: - public slot, + class static_store_slot: + public slot, enable_any { }; template <> - class static_store_slot: - public slot, + class static_store_slot: + public slot, enable_any { }; @@ -160,31 +160,31 @@ namespace boost { namespace leaf { } template - bool check_exception_pack( std::exception const * stdx, Ex const * ) noexcept + bool check_exception_pack( error_info const & ei, Ex const * ) noexcept { - return dynamic_cast(stdx)!=0; + return dynamic_cast(ei.get_exception())!=0; } template - bool check_exception_pack( std::exception const * stdx, Ex const *, ExRest const * ... ex_rest ) noexcept + bool check_exception_pack( error_info const & ei, Ex const *, ExRest const * ... ex_rest ) noexcept { - return dynamic_cast(stdx)!=0 || check_exception_pack(stdx, ex_rest...); + return dynamic_cast(ei.get_exception())!=0 || check_exception_pack(ei, ex_rest...); } template struct check_one_argument { - static bool check( SlotsTuple const & tup, error const & e, std::exception const * ) noexcept + static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept { auto & sl = std::get,SlotsTuple>::value>(tup); - return sl.has_value() && sl.value().e==e; + return sl.has_value() && sl.value().e==ei.get_error(); } }; template struct check_one_argument { - static bool check( SlotsTuple const &, error const &, std::exception const * ) noexcept + static bool check( SlotsTuple const &, error_info const & ) noexcept { return true; } @@ -193,13 +193,13 @@ namespace boost { namespace leaf { template ::type... Value> struct check_one_argument> { - static bool check( SlotsTuple const & tup, error const & e, std::exception const * ) noexcept + static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept { auto & sl = std::get,SlotsTuple>::value>(tup); if( sl.has_value() ) { auto const & v = sl.value(); - return v.e==e && check_value_pack(match_type::get(v.v),Value...); + return v.e==ei.get_error() && check_value_pack(match_type::get(v.v),Value...); } else return false; @@ -209,17 +209,16 @@ namespace boost { namespace leaf { template struct check_one_argument> { - static bool check( SlotsTuple const &, error const &, std::exception const * ex ) noexcept + static bool check( SlotsTuple const &, error_info const & ei ) noexcept { - assert(ex!=0); - return check_exception_pack(ex,static_cast(0)...); + return check_exception_pack(ei,static_cast(0)...); } }; template - struct check_one_argument + struct check_one_argument { - static constexpr bool check( SlotsTuple const &, error const &, std::exception const * ) + static constexpr bool check( SlotsTuple const &, error_info const & ) { return true; } @@ -231,16 +230,16 @@ namespace boost { namespace leaf { template struct check_arguments { - static bool check( SlotsTuple const & tup, error const & e, std::exception const * ex ) noexcept + static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept { - return check_one_argument::check(tup,e,ex) && check_arguments::check(tup,e,ex); + return check_one_argument::check(tup,ei) && check_arguments::check(tup,ei); } }; template struct check_arguments { - static constexpr bool check( SlotsTuple const &, error const &, std::exception const * ) noexcept + static constexpr bool check( SlotsTuple const &, error_info const & ) noexcept { return true; } @@ -252,9 +251,9 @@ namespace boost { namespace leaf { struct get_one_argument { template - static T const & get( StaticStore const & ss, error const & e ) noexcept + static T const & get( StaticStore const & ss, error_info const & ei ) noexcept { - T const * arg = ss.template peek(e); + T const * arg = ss.template peek(ei.get_error()); assert(arg!=0); return *arg; } @@ -264,9 +263,9 @@ namespace boost { namespace leaf { struct get_one_argument { template - static T const * get( StaticStore const & ss, error const & e ) noexcept + static T const * get( StaticStore const & ss, error_info const & ei ) noexcept { - return ss.template peek(e); + return ss.template peek(ei.get_error()); } }; @@ -274,9 +273,9 @@ namespace boost { namespace leaf { struct get_one_argument> { template - static match get( StaticStore const & ss, error const & e ) noexcept + static match get( StaticStore const & ss, error_info const & ei ) noexcept { - E const * arg = ss.template peek(e); + E const * arg = ss.template peek(ei.get_error()); assert(arg!=0); return match{match_type::get(*arg)}; } @@ -286,19 +285,32 @@ namespace boost { namespace leaf { struct get_one_argument> { template - static constexpr catch_ get( StaticStore const &, error const & ) noexcept + static constexpr catch_ get( StaticStore const &, error_info const & ) noexcept { return { }; } }; template <> - struct get_one_argument + struct get_one_argument { template - static error const & get( StaticStore const &, error const & e ) noexcept + static error_info const & get( StaticStore const &, error_info const & ei ) noexcept { - return e; + return ei; + } + }; + + template <> + struct get_one_argument + { + template + static complete_diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept + { + complete_diagnostic_info const * cdi = ss.template peek(ei.get_error()); + assert(cdi!=0); + cdi->set_error_info(ei); + return *cdi; } }; @@ -306,12 +318,9 @@ namespace boost { namespace leaf { template struct acceptable_last_handler_argument: std::false_type { }; template struct acceptable_last_handler_argument: is_error_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; template struct ensure_last_handler_matches: std::false_type @@ -346,17 +355,17 @@ namespace boost { namespace leaf { bool reset_; template - bool check_handler( error const & e, std::exception const * ex, leaf_detail_mp11::mp_list ) const noexcept + bool check_handler( error_info const & ei, leaf_detail_mp11::mp_list ) const noexcept { using namespace static_store_internal; - return check_arguments::type>::type...>::check(s_,e,ex); + return check_arguments::type>::type...>::check(s_,ei); } template - typename function_traits::return_type call_handler( error const & e, std::exception const *, F && f, leaf_detail_mp11::mp_list ) const + typename function_traits::return_type call_handler( error_info const & ei, F && f, leaf_detail_mp11::mp_list ) const { using namespace static_store_internal; - return std::forward(f)( get_one_argument::type>::type>::get(*this,e)... ); + return std::forward(f)( get_one_argument::type>::type>::get(*this,ei)... ); } public: @@ -391,22 +400,22 @@ namespace boost { namespace leaf { } template - typename function_traits::return_type handle_error( error const & e, std::exception const * ex, F && f ) const + typename function_traits::return_type handle_error( error_info const & ei, F && f ) const { using namespace static_store_internal; static_assert(ensure_last_handler_matches::mp_args>::value, - "The last handler for handle_all may only take arguments of type error, e_unexpected, e_unexpected_diagnostic_output or any number of pointer-to-const arguments."); - return call_handler( e, ex, std::forward(f), typename function_traits::mp_args{ } ); + "The last handler for handle_all may only take arguments of type error_info const &, complete_diagnostic_info const &, unexpected_error_info const &, or any number of pointer-to-const arguments."); + return call_handler( ei, std::forward(f), typename function_traits::mp_args{ } ); } template - typename function_traits::return_type handle_error( error const & e, std::exception const * ex, CarF && car_f, CdarF && cdar_f, CddrF && ... cddr_f ) const + typename function_traits::return_type handle_error( error_info const & ei, CarF && car_f, CdarF && cdar_f, CddrF && ... cddr_f ) const { using namespace static_store_internal; - if( check_handler( e, ex, typename function_traits::mp_args{ } ) ) - return call_handler( e, ex, std::forward(car_f), typename function_traits::mp_args{ } ); + if( check_handler( ei, typename function_traits::mp_args{ } ) ) + return call_handler( ei, std::forward(car_f), typename function_traits::mp_args{ } ); else - return handle_error( e, ex, std::forward(cdar_f), std::forward(cddr_f)...); + return handle_error( ei, std::forward(cdar_f), std::forward(cddr_f)...); } }; @@ -431,7 +440,7 @@ namespace boost { namespace leaf { template using translate_list = typename translate_list_impl::type; template struct does_not_participate_in_expect_deduction: std::false_type { }; - template <> struct does_not_participate_in_expect_deduction: std::true_type { }; + template <> struct does_not_participate_in_expect_deduction: std::true_type { }; template <> struct does_not_participate_in_expect_deduction: std::true_type { }; template diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 25a5a4c..5a01c97 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -34,12 +34,12 @@ namespace boost { namespace leaf { } }; - struct e_unexpected + struct unexpected_error_info { char const * (*first_type)() noexcept; int count; - friend std::ostream & operator<<( std::ostream & os, e_unexpected const & x ) + friend std::ostream & operator<<( std::ostream & os, unexpected_error_info const & x ) { assert(x.first_type!=0); assert(x.count>0); @@ -51,29 +51,8 @@ namespace boost { namespace leaf { } }; - struct e_unexpected_diagnostic_output - { - std::string value; - std::set already; - }; - namespace leaf_detail { - template <> - struct diagnostic - { - static bool print( std::ostream & os, e_unexpected_diagnostic_output const & x ) noexcept - { - if( x.value.empty() ) - return false; - else - { - os << x.value; - return true; - } - } - }; - template struct has_data_member_value { @@ -96,7 +75,7 @@ namespace boost { namespace leaf { template <> struct is_error_type: std::true_type { }; template <> struct is_error_type: std::true_type { }; template <> struct is_error_type: std::true_type { }; - template <> struct is_error_type: std::true_type { }; + template <> struct is_error_type: std::true_type { }; template <> struct is_error_type: std::true_type { }; //////////////////////////////////////// @@ -194,8 +173,6 @@ namespace boost { namespace leaf { template error propagate( E && ... ) const noexcept; - - void diagnostic_output( std::ostream & os ) const; }; template @@ -219,10 +196,22 @@ namespace boost { namespace leaf { namespace leaf_detail { template - struct error_info + struct ev_type { - E v; error e; + E v; + + ev_type( error const & e, E const & v ): + e(e), + v(v) + { + } + + ev_type( error const & e, E && v ) noexcept: + e(e), + v(std::forward(v)) + { + } }; inline int & tl_unexpected_enabled_counter() noexcept @@ -236,14 +225,14 @@ namespace boost { namespace leaf { slot_base( slot_base const & ) = delete; slot_base & operator=( slot_base const & ) = delete; - virtual bool slot_diagnostic_output( std::ostream &, error const * e ) const = 0; + virtual bool slot_print( std::ostream &, error const & e ) const = 0; public: - static void diagnostic_output( std::ostream & os, error const * e ) + static void print( std::ostream & os, error const & e ) { for( slot_base const * p = first(); p; p=p->next_ ) - if( p->slot_diagnostic_output(os,e) ) + if( p->slot_print(os,e) ) os << std::endl; } @@ -270,19 +259,150 @@ namespace boost { namespace leaf { p = next_; } }; + } + //////////////////////////////////////// + + namespace leaf_detail { class captured_exception; } + + class error_info + { + error_info( error_info const & ) = delete; + error_info & operator=( error_info const & ) = delete; + + error const & e_; + std::exception const * ex_; + leaf_detail::captured_exception const * cap_; + void (*print_ex_)( std::ostream &, std::exception const *, leaf_detail::captured_exception const * ); + + public: + + explicit error_info( error const & e ) noexcept: + e_(e), + ex_(0), + cap_(0), + print_ex_(0) + { + } + + error_info( error const & e, std::exception const * ex, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: + e_(e), + ex_(ex), + cap_(0), + print_ex_(print_ex) + { + assert(print_ex_!=0); + } + + error_info( error const & e, std::exception const * ex, leaf_detail::captured_exception const * cap, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: + e_(e), + ex_(ex), + cap_(cap), + print_ex_(print_ex) + { + assert(print_ex_!=0); + } + + error const & get_error() const noexcept + { + return e_; + } + + std::exception const * get_exception() const noexcept + { + assert(print_ex_!=0); + return ex_; + } + + friend std::ostream & operator<<( std::ostream & os, error_info const & x ) + { + os << "leaf::error serial number: " << x.e_ << std::endl; + if( x.print_ex_ ) + x.print_ex_(os,x.ex_,x.cap_); + leaf_detail::slot_base::print(os,x.e_); + return os; + } + }; + + class complete_diagnostic_info + { + complete_diagnostic_info( complete_diagnostic_info const & ) = delete; + complete_diagnostic_info & operator=( complete_diagnostic_info const & ) = delete; + mutable error_info const * ei_; + std::string value_; + std::set already_; + + public: + + complete_diagnostic_info( complete_diagnostic_info && x ): + ei_(0), + value_(std::move(x.value_)), + already_(std::move(x.already_)) + { + x.ei_ = 0; + } + + explicit complete_diagnostic_info( std::stringstream const & s ): + ei_(0), + value_(s.str()+" {unexpected}") + { + } + + template + void add( std::stringstream const & s ) + { + if( already_.insert(&type).second ) + { + value_ += '\n'; + value_ += s.str(); + value_ += " {unexpected}"; + } + } + + void set_error_info( error_info const & ei ) const + { + ei_ = &ei; + } + + friend std::ostream & operator<<( std::ostream & os, complete_diagnostic_info const & x ) + { + os << *x.ei_; + if( !x.value_.empty() ) + os << x.value_ << std::endl; + return os; + } + }; + + template <> struct is_error_type: std::true_type { }; + + namespace leaf_detail + { + template <> + struct diagnostic + { + static bool print( std::ostream & os, complete_diagnostic_info const & ) + { + return false; + } + }; + } + + //////////////////////////////////////// + + namespace leaf_detail + { template class slot: slot_base, - optional> + optional> { slot( slot const & ) = delete; slot & operator=( slot const & ) = delete; - typedef optional> base; + typedef optional> base; slot * prev_; static_assert(is_error_type::value,"Not an error type"); - bool slot_diagnostic_output( std::ostream & os, error const * e ) const; + bool slot_print( std::ostream &, error const & ) const; protected: @@ -305,9 +425,9 @@ namespace boost { namespace leaf { } template - void put_unexpected( error_info const & ev ) noexcept + void put_unexpected( ev_type const & ev ) noexcept { - if( slot * p = tl_slot_ptr() ) + if( slot * p = tl_slot_ptr() ) { if( p->has_value() ) { @@ -318,14 +438,14 @@ namespace boost { namespace leaf { return; } } - (void) p->put( error_info{e_unexpected{&type,1},ev.e} ); + (void) p->put( ev_type(ev.e,unexpected_error_info{&type,1}) ); } } template - void put_unexpected_diagnostic_output( error_info const & ev ) noexcept + void put_complete_diagnostic_info( ev_type const & ev ) noexcept { - if( slot * p = tl_slot_ptr() ) + if( slot * p = tl_slot_ptr() ) { std::stringstream s; if( !diagnostic::print(s,ev.v) ) @@ -335,25 +455,19 @@ namespace boost { namespace leaf { auto & p_ev = p->value(); if( p_ev.e==ev.e ) { - if( p_ev.v.already.insert(&type).second ) - { - std::string & value = p_ev.v.value; - value += '\n'; - value += s.str(); - value += " {unexpected}"; - } + p_ev.v.add(s); return; } } - (void) p->put( error_info{e_unexpected_diagnostic_output{s.str()+" {unexpected}"},ev.e} ); + (void) p->put( ev_type(ev.e,complete_diagnostic_info(s)) ); } } template - void no_expect_slot( error_info const & ev ) noexcept + void no_expect_slot( ev_type const & ev ) noexcept { put_unexpected(ev); - put_unexpected_diagnostic_output(ev); + put_complete_diagnostic_info(ev); } template @@ -361,13 +475,13 @@ namespace boost { namespace leaf { { using T = typename std::remove_cv::type>::type; if( slot * p = tl_slot_ptr() ) - (void) p->put( error_info{std::forward(v),e} ); + (void) p->put( ev_type(e,std::forward(v)) ); else { int c = tl_unexpected_enabled_counter(); assert(c>=0); if( c ) - no_expect_slot( error_info{std::forward(v),e} ); + no_expect_slot( ev_type(e,std::forward(v)) ); } return 0; } @@ -385,7 +499,7 @@ namespace boost { namespace leaf { { if( prev_ ) { - optional> & p = *prev_; + optional> & p = *prev_; p = std::move(*this); } else @@ -399,23 +513,12 @@ namespace boost { namespace leaf { } template - bool slot::slot_diagnostic_output( std::ostream & os, error const * e ) const + bool slot::slot_print( std::ostream & os, error const & e ) const { if( tl_slot_ptr()==this ) - if( error_info const * ev = has_value() ) - if( e ) - { - if( ev->e==*e ) - return diagnosticv)>::print(os,ev->v); - } - else - { - if( diagnosticv)>::print(os,ev->v) ) - { - os << " {" << ev->e << '}'; - return true; - } - } + if( ev_type const * ev = has_value() ) + if( ev->e==e ) + return diagnosticv)>::print(os,ev->v); return false; } @@ -445,16 +548,6 @@ namespace boost { namespace leaf { return *this; } - inline void error::diagnostic_output( std::ostream & os ) const - { - os << "leaf::error serial number: " << *this << std::endl; - leaf_detail::slot_base::diagnostic_output(os,this); - } - - inline void global_diagnostic_output( std::ostream & os ) - { - leaf_detail::slot_base::diagnostic_output(os,0); - } } } #endif diff --git a/include/boost/leaf/handle.hpp b/include/boost/leaf/handle.hpp index 5f002c5..7b591b6 100644 --- a/include/boost/leaf/handle.hpp +++ b/include/boost/leaf/handle.hpp @@ -21,7 +21,10 @@ namespace boost { namespace leaf { if( auto r = std::forward(try_block)() ) return *r; else - return ss.handle_error(r.error(), 0, std::forward(handlers)...); + { + error const e = r.error(); + return ss.handle_error(error_info(e), std::forward(handlers)...); + } } namespace leaf_detail @@ -70,13 +73,17 @@ namespace boost { namespace leaf { ss.set_reset(true); return r; } - else if( auto rr = ss.handle_error(r.error(), 0, handler_wrapper(std::forward(handlers))..., [&r] { return r; } ) ) - { - ss.set_reset(true); - return rr; - } else - return rr; + { + error const e = r.error(); + if( auto rr = ss.handle_error(error_info(e), handler_wrapper(std::forward(handlers))..., [&r] { return r; } ) ) + { + ss.set_reset(true); + return rr; + } + else + return rr; + } } } } diff --git a/include/boost/leaf/preload.hpp b/include/boost/leaf/preload.hpp index 803efde..d526a05 100644 --- a/include/boost/leaf/preload.hpp +++ b/include/boost/leaf/preload.hpp @@ -52,7 +52,7 @@ namespace boost { namespace leaf { if( s_ ) { if( !s_->has_value() || s_->value().e!=e ) - s_->put( leaf_detail::error_info{std::move(v_),e} ); + s_->put( leaf_detail::ev_type(e,std::move(v_)) ); } else { @@ -60,7 +60,7 @@ namespace boost { namespace leaf { int c = tl_unexpected_enabled_counter(); assert(c>=0); if( c ) - no_expect_slot( error_info{std::forward(v_),e} ); + no_expect_slot( ev_type(e,std::forward(v_)) ); } } }; @@ -137,7 +137,7 @@ namespace boost { namespace leaf { if( s_ ) { if( !s_->has_value() || s_->value().e!=e ) - s_->put( leaf_detail::error_info{f_(),e} ); + s_->put( leaf_detail::ev_type(e,f_()) ); } else { @@ -145,7 +145,7 @@ namespace boost { namespace leaf { int c = tl_unexpected_enabled_counter(); assert(c>=0); if( c ) - no_expect_slot( error_info{std::forward(f_()),e} ); + no_expect_slot( ev_type(e,std::forward(f_())) ); } } }; diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp index a832dc1..b7b7c1d 100644 --- a/include/boost/leaf/try.hpp +++ b/include/boost/leaf/try.hpp @@ -9,8 +9,7 @@ #include #include -#include -#include +#include namespace boost { namespace leaf { @@ -32,7 +31,7 @@ namespace boost { namespace leaf { { return std::forward(try_block)(); } - catch( leaf_detail::captured_exception & cap ) + catch( captured_exception & cap ) { try { @@ -42,105 +41,25 @@ namespace boost { namespace leaf { } catch( std::exception const & ex ) { - return ss.handle_error(get_error(ex), &ex, std::forward(handlers)..., [ ]() -> typename leaf_detail::function_traits::return_type { throw; }); + error const e = get_error(ex); + return ss.handle_error(error_info(e,&ex,&cap,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); + } + catch( ... ) + { + error const e = next_error_value(); + return ss.handle_error(error_info(e,0,&cap,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); } } catch( std::exception const & ex ) { - return ss.handle_error(get_error(ex), &ex, std::forward(handlers)..., [ ]() -> typename leaf_detail::function_traits::return_type { throw; }); + error const e = get_error(ex); + return ss.handle_error(error_info(e,&ex,0,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); } - } - - namespace leaf_detail - { - inline void diagnostic_output_std_exception( std::ostream & os, std::exception const & ex ) + catch( ... ) { - os << - "Exception dynamic type: " << leaf_detail::demangle(typeid(ex).name()) << std::endl << - "std::exception::what(): " << ex.what() << std::endl; + error const e = next_error_value(); + return ss.handle_error(error_info(e,0,0,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); } - - inline void diagnostic_output_current_exception_no_capture( std::ostream & os ) - { - try - { - throw; - } - catch( std::exception const & ex ) - { - diagnostic_output_std_exception(os,ex); - } - catch( ... ) - { - os << "Unknown exception type (not a std::exception)" << std::endl; - } - try - { - throw; - } - catch( error const & e ) - { - e.diagnostic_output(os); - } - catch( ... ) - { - global_diagnostic_output(os); - } - } - - inline void diagnostic_output_current_exception_( std::ostream & os ) - { - try - { - try - { - throw; - } - catch( leaf_detail::captured_exception const & cap ) - { - cap.diagnostic_output( os, &leaf_detail::diagnostic_output_current_exception_no_capture ); - } - } - catch(...) - { - leaf_detail::diagnostic_output_current_exception_no_capture(os); - } - } - } //namespace leaf_detail - - inline void diagnostic_output_current_exception( std::ostream & os ) - { - os << "Current Exception Diagnostic Information:" << std::endl; - leaf_detail::diagnostic_output_current_exception_(os); - } - - inline void diagnostic_output( std::ostream & os, std::exception const & ex ) - { - if( leaf_detail::captured_exception const * cap = dynamic_cast(&ex) ) - cap->diagnostic_output( os, &leaf_detail::diagnostic_output_current_exception_no_capture ); - else - { - leaf_detail::diagnostic_output_std_exception(os,ex); - get_error(ex).diagnostic_output(os); - } - } - - inline void diagnostic_output( std::ostream & os, std::exception_ptr const & ep ) - { - if( ep ) - { - os << "std::exception_ptr Diagnostic Information:" << std::endl; - try - { - std::rethrow_exception(ep); - } - catch(...) - { - leaf_detail::diagnostic_output_current_exception_(os); - } - } - else - os << "Empty" << std::endl; } } } diff --git a/meson.build b/meson.build index 914473f..3477db0 100644 --- a/meson.build +++ b/meson.build @@ -50,7 +50,7 @@ tests = [ 'defer_nested_new_error_result_test', 'defer_nested_success_exception_test', 'defer_nested_success_result_test', - 'diagnostic_output_test', + 'diagnostic_info_test', 'function_traits_test', 'handle_all_test', 'handle_some_basic_test', diff --git a/test/diagnostic_output_test.cpp b/test/diagnostic_info_test.cpp similarity index 95% rename from test/diagnostic_output_test.cpp rename to test/diagnostic_info_test.cpp index f165480..b298003 100644 --- a/test/diagnostic_output_test.cpp +++ b/test/diagnostic_info_test.cpp @@ -90,11 +90,11 @@ int main() non_printable_info_printable_payload, non_printable_info_non_printable_payload, leaf::e_errno, - leaf::e_unexpected_diagnostic_output, - leaf::e_unexpected ) + leaf::unexpected_error_info const &, + leaf::complete_diagnostic_info const & di ) { std::ostringstream st; - leaf::diagnostic_output_current_exception(st); + st << di; std::string s = st.str(); BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); BOOST_TEST(s.find(": N/A")!=s.npos); diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp index 2818c39..f9e844d 100644 --- a/test/handle_some_test.cpp +++ b/test/handle_some_test.cpp @@ -46,11 +46,11 @@ int main() c = answer; return { }; }, - [&c]( leaf::error e ) + [&c]( leaf::error_info const & ei ) { BOOST_TEST(c==0); c = 1; - return e; + return ei.get_error(); } ); BOOST_TEST(r); BOOST_TEST(c==42); @@ -576,9 +576,9 @@ int main() LEAF_AUTO(answer, f(error_codes::ok)); return answer; }, - [ ]( leaf::error e ) + [ ]( leaf::error_info const & ei ) { - return e; + return ei.get_error(); } ); BOOST_TEST(r && *r==42); } diff --git a/test/result_state_test.cpp b/test/result_state_test.cpp index d70447e..6b16f94 100644 --- a/test/result_state_test.cpp +++ b/test/result_state_test.cpp @@ -495,7 +495,8 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=std::move(r1); BOOST_TEST(!r2); - exp.handle_error(r2.error(),0,[ ]{ }); + leaf::error const e = r2.error(); + exp.handle_error(leaf::error_info(e),[ ]{ }); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -748,7 +749,8 @@ int main() BOOST_TEST(err::count==1); leaf::result r2; r2=std::move(r1); BOOST_TEST(!r2); - exp.handle_error(r2.error(),0,[ ]{ }); + leaf::error const e = r2.error(); + exp.handle_error(leaf::error_info(e),[ ]{ }); BOOST_TEST(err::count==1); } BOOST_TEST(err::count==0); diff --git a/test/static_store_deduction_test.cpp b/test/static_store_deduction_test.cpp index d6c3257..e255c0f 100644 --- a/test/static_store_deduction_test.cpp +++ b/test/static_store_deduction_test.cpp @@ -40,34 +40,32 @@ void not_called_on_purpose() test< static_store> >( expd([ ]( info<1> ){ }) ); test< static_store> >( expd([ ]( info<1> const ){ }) ); test< static_store> >( expd([ ]( info<1> const & ){ }) ); - test< static_store> >( expd([ ]( info<1>, leaf::error ){ }) ); - test< static_store> >( expd([ ]( info<1>, leaf::error const ){ }) ); - test< static_store> >( expd([ ]( info<1>, leaf::error const & ){ }) ); + test< static_store> >( expd([ ]( info<1>, leaf::error_info const & ){ }) ); test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<2> ){ }) ); test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, info<2> ){ }) ); test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2> ){ }) ); test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( leaf::error, info<1> ){ }, [ ]( info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( leaf::error, info<1> ){ }, [ ]( info<1>, info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( leaf::error, info<1>, info<2> ){ }, [ ]( info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( leaf::error, info<1>, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( leaf::error_info const &, info<1> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( leaf::error_info const &, info<1> ){ }, [ ]( info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( leaf::error_info const &, info<1>, info<2> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( leaf::error_info const &, info<1>, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error ){ }, [ ]( info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error ){ }, [ ]( info<1>, info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error, info<2> ){ }, [ ]( info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error_info const & ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error_info const & ){ }, [ ]( info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error_info const &, info<2> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error_info const &, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( leaf::error, info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( leaf::error, info<1>, info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( leaf::error, info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( leaf::error, info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( leaf::error_info const &, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( leaf::error_info const &, info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( leaf::error_info const &, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( leaf::error_info const &, info<1>, info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<2>, leaf::error ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, leaf::error, info<2> ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2>, leaf::error ){ }) ); - test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<1>, leaf::error, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<2>, leaf::error_info const & ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, leaf::error_info const &, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2>, leaf::error_info const & ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<1>, leaf::error_info const &, info<2> ){ }) ); test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, info<2> ){ }, [ ]( info<1>, info<3> ){ }) ); diff --git a/test/static_store_test.cpp b/test/static_store_test.cpp index ec708a4..6e2c09a 100644 --- a/test/static_store_test.cpp +++ b/test/static_store_test.cpp @@ -82,7 +82,7 @@ int main() BOOST_TEST(!exp.peek>(e1)); { - int r = exp.handle_error( e1, 0, + int r = exp.handle_error( leaf::error_info(e1), [ ](info<1>,info<2>,info<4>) { return 1; @@ -108,7 +108,7 @@ int main() BOOST_TEST(!exp.peek>(e2)); { - int r = exp.handle_error( e2, 0, + int r = exp.handle_error( leaf::error_info(e2), [ ]( info<1>, info<2>, info<4> ) { return 1; @@ -131,7 +131,7 @@ int main() } { - int r = exp0.handle_error( e0, 0, + int r = exp0.handle_error( leaf::error_info(e0), [ ]( info<2> const & i2, info<1> const & i1 ) { BOOST_TEST(i1.value==1); From 38f5e93009e1126815745348cff8164408cb4ab9 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Thu, 10 Jan 2019 21:21:05 -0800 Subject: [PATCH 16/34] fixing broken Jamfile.v2 --- test/Jamfile.v2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 3ea460e..86df7df 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -43,7 +43,7 @@ run defer_nested_new_error_exception_test.cpp ; run defer_nested_new_error_result_test.cpp ; run defer_nested_success_exception_test.cpp ; run defer_nested_success_result_test.cpp ; -run diagnostic_output_test.cpp ; +run diagnostic_info_test.cpp ; run function_traits_test.cpp ; run handle_all_test.cpp ; run handle_some_basic_test.cpp ; From 7d151e54260946b784852d088ac8b872a31b03d7 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Fri, 11 Jan 2019 00:16:22 -0800 Subject: [PATCH 17/34] more diagnostic info refinements --- include/boost/leaf/capture_exception.hpp | 57 ++++++++---- include/boost/leaf/capture_result.hpp | 15 ++-- include/boost/leaf/detail/dynamic_store.hpp | 1 - .../boost/leaf/detail/dynamic_store_impl.hpp | 8 -- include/boost/leaf/detail/print.hpp | 24 ----- .../leaf/detail/print_exception_info.hpp | 4 +- include/boost/leaf/detail/static_store.hpp | 14 +-- include/boost/leaf/error.hpp | 34 +++---- include/boost/leaf/handle.hpp | 21 ++--- include/boost/leaf/try.hpp | 12 +-- test/diagnostic_info_test.cpp | 90 ++++++++++++++++++- 11 files changed, 176 insertions(+), 104 deletions(-) diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp index b412362..73aff8a 100644 --- a/include/boost/leaf/capture_exception.hpp +++ b/include/boost/leaf/capture_exception.hpp @@ -23,16 +23,19 @@ namespace boost { namespace leaf { std::exception_ptr ex_; std::shared_ptr ds_; bool had_error_; + void (*print_captured_types_)(std::ostream &); public: - captured_exception_impl( std::exception_ptr && ex, std::shared_ptr && ds, bool had_error ) noexcept: + captured_exception_impl( std::exception_ptr && ex, std::shared_ptr && ds, bool had_error, void (*print_captured_types)(std::ostream &) ) noexcept: error(ds->get_error()), ex_(std::move(ex)), ds_(std::move(ds)), - had_error_(had_error) + had_error_(had_error), + print_captured_types_(print_captured_types) { assert(ex_); + assert(print_captured_types_!=0); } [[noreturn]] void unload_and_rethrow_original_exception() @@ -52,27 +55,49 @@ namespace boost { namespace leaf { void print( std::ostream & os ) const { - assert(ds_); - ds_->print(os); + print_captured_types_(os); } }; //////////////////////////////////////// - template - class exception_trap + template + struct print_types; + + template + struct print_types + { + static void print( std::ostream & os ) + { + os << '\t' << type() << std::endl; + print_types::print(os); + } + }; + + template <> + struct print_types<> + { + static void print( std::ostream & ) + { + } + }; + + //////////////////////////////////////// + + template + struct exception_trap; + + template class L, class... A, class... E> + struct exception_trap,E...> { F f_; - public: - explicit exception_trap( F && f ) noexcept: - f_(std::move(f)) + f_(std::forward(f)) { } - template - decltype(std::declval()(std::declval()...)) operator()( A && ... a ) const + decltype(std::declval()(std::declval()...)) operator()( A... a ) const { static_store ss; ss.set_reset(true); @@ -86,20 +111,22 @@ namespace boost { namespace leaf { } catch( error const & e ) { - throw captured_exception_impl( std::current_exception(), std::make_shared>(e,std::move(ss)), true ); + throw captured_exception_impl( std::current_exception(), std::make_shared>(e,std::move(ss)), true, &print_types::print ); } catch(...) { - throw captured_exception_impl( std::current_exception(), std::make_shared>(new_error(),std::move(ss)), false ); + throw captured_exception_impl( std::current_exception(), std::make_shared>(new_error(),std::move(ss)), false, &print_types::print ); } } }; + } //leaf_detail template - leaf_detail::exception_trap capture_exception( F && f ) noexcept + leaf_detail::exception_trap::mp_args,E...> capture_exception( F && f ) noexcept { - return leaf_detail::exception_trap(std::move(f)); + using namespace leaf_detail; + return exception_trap::mp_args,E...>(std::move(f)); } } } diff --git a/include/boost/leaf/capture_result.hpp b/include/boost/leaf/capture_result.hpp index c122f3e..353c1b7 100644 --- a/include/boost/leaf/capture_result.hpp +++ b/include/boost/leaf/capture_result.hpp @@ -26,8 +26,11 @@ namespace boost { namespace leaf { namespace leaf_detail { - template - class result_trap + template + struct result_trap; + + template class L, class... A, class... E> + struct result_trap,E...> { F f_; @@ -38,8 +41,7 @@ namespace boost { namespace leaf { { } - template - decltype(std::declval()(std::declval()...)) operator()( A && ... a ) const noexcept + decltype(std::declval()(std::declval()...)) operator()( A ... a ) const noexcept { static_store ss; ss.set_reset(true); @@ -52,9 +54,10 @@ namespace boost { namespace leaf { } template - leaf_detail::result_trap capture_result( F && f ) noexcept + leaf_detail::result_trap::mp_args,E...> capture_result( F && f ) noexcept { - return leaf_detail::result_trap(std::move(f)); + using namespace leaf_detail; + return result_trap::mp_args,E...>(std::move(f)); } } } diff --git a/include/boost/leaf/detail/dynamic_store.hpp b/include/boost/leaf/detail/dynamic_store.hpp index 3b703bd..6b3c952 100644 --- a/include/boost/leaf/detail/dynamic_store.hpp +++ b/include/boost/leaf/detail/dynamic_store.hpp @@ -32,7 +32,6 @@ namespace boost { namespace leaf { virtual error get_error() const noexcept = 0; virtual error unload() noexcept = 0; virtual error unload( error const & ) noexcept = 0; - virtual void print( std::ostream & ) const = 0; }; } diff --git a/include/boost/leaf/detail/dynamic_store_impl.hpp b/include/boost/leaf/detail/dynamic_store_impl.hpp index ca73b41..b6d8b6d 100644 --- a/include/boost/leaf/detail/dynamic_store_impl.hpp +++ b/include/boost/leaf/detail/dynamic_store_impl.hpp @@ -68,14 +68,6 @@ namespace boost { namespace leaf { return e; } - void print( std::ostream & os ) const - { - os << "Types being captured: "; - print_types::print(os); - os << std::endl << "Values actually captured:" << std::endl; - dynamic_store_internal::tuple_for_each::print(os,s_); - } - public: dynamic_store_impl( error const & e, static_store && ss ) noexcept: diff --git a/include/boost/leaf/detail/print.hpp b/include/boost/leaf/detail/print.hpp index 05a1b7b..697ac45 100644 --- a/include/boost/leaf/detail/print.hpp +++ b/include/boost/leaf/detail/print.hpp @@ -50,30 +50,6 @@ namespace boost { namespace leaf { namespace leaf_detail { - template - struct print_types; - - template - struct print_types - { - static void print( std::ostream & os ) - { - os << type(); - } - }; - - template - struct print_types - { - static void print( std::ostream & os ) - { - os << type() << ", "; - print_types::print(os); - } - }; - - //////////////////////////////////////// - template struct is_printable: std::false_type { diff --git a/include/boost/leaf/detail/print_exception_info.hpp b/include/boost/leaf/detail/print_exception_info.hpp index 4823bc0..107639e 100644 --- a/include/boost/leaf/detail/print_exception_info.hpp +++ b/include/boost/leaf/detail/print_exception_info.hpp @@ -19,9 +19,9 @@ namespace boost { namespace leaf { { if( cap ) { - os << "Detected exception_capture" << std::endl; + os << "Detected exception_capture of the following error types:" << std::endl; cap->print(os); - os << std::endl << "Diagnostic Information about the original exception follows" << std::endl; + os << "Diagnostic Information about the original exception follows" << std::endl; } if( ex ) { diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index 431f230..85da59b 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -113,8 +113,8 @@ namespace boost { namespace leaf { }; template <> - class static_store_slot: - public slot, + class static_store_slot: + public slot, enable_any { }; @@ -302,12 +302,12 @@ namespace boost { namespace leaf { }; template <> - struct get_one_argument + struct get_one_argument { template - static complete_diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept + static verbose_diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept { - complete_diagnostic_info const * cdi = ss.template peek(ei.get_error()); + verbose_diagnostic_info const * cdi = ss.template peek(ei.get_error()); assert(cdi!=0); cdi->set_error_info(ei); return *cdi; @@ -320,7 +320,7 @@ namespace boost { namespace leaf { template struct acceptable_last_handler_argument: is_error_type { }; template <> struct acceptable_last_handler_argument: std::true_type { }; template <> struct acceptable_last_handler_argument: std::true_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; template struct ensure_last_handler_matches: std::false_type @@ -404,7 +404,7 @@ namespace boost { namespace leaf { { using namespace static_store_internal; static_assert(ensure_last_handler_matches::mp_args>::value, - "The last handler for handle_all may only take arguments of type error_info const &, complete_diagnostic_info const &, unexpected_error_info const &, or any number of pointer-to-const arguments."); + "The last handler for handle_all may only take arguments of type error_info const &, verbose_diagnostic_info const &, unexpected_error_info const &, or any number of pointer-to-const arguments."); return call_handler( ei, std::forward(f), typename function_traits::mp_args{ } ); } diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 5a01c97..5cdbe64 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -270,10 +270,10 @@ namespace boost { namespace leaf { error_info( error_info const & ) = delete; error_info & operator=( error_info const & ) = delete; - error const & e_; - std::exception const * ex_; - leaf_detail::captured_exception const * cap_; - void (*print_ex_)( std::ostream &, std::exception const *, leaf_detail::captured_exception const * ); + error const e_; + std::exception const * const ex_; + leaf_detail::captured_exception const * const cap_; + void (* const print_ex_)( std::ostream &, std::exception const *, leaf_detail::captured_exception const * ); public: @@ -324,17 +324,17 @@ namespace boost { namespace leaf { } }; - class complete_diagnostic_info + class verbose_diagnostic_info { - complete_diagnostic_info( complete_diagnostic_info const & ) = delete; - complete_diagnostic_info & operator=( complete_diagnostic_info const & ) = delete; + verbose_diagnostic_info( verbose_diagnostic_info const & ) = delete; + verbose_diagnostic_info & operator=( verbose_diagnostic_info const & ) = delete; mutable error_info const * ei_; std::string value_; std::set already_; public: - complete_diagnostic_info( complete_diagnostic_info && x ): + verbose_diagnostic_info( verbose_diagnostic_info && x ): ei_(0), value_(std::move(x.value_)), already_(std::move(x.already_)) @@ -342,7 +342,7 @@ namespace boost { namespace leaf { x.ei_ = 0; } - explicit complete_diagnostic_info( std::stringstream const & s ): + explicit verbose_diagnostic_info( std::stringstream const & s ): ei_(0), value_(s.str()+" {unexpected}") { @@ -364,7 +364,7 @@ namespace boost { namespace leaf { ei_ = &ei; } - friend std::ostream & operator<<( std::ostream & os, complete_diagnostic_info const & x ) + friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x ) { os << *x.ei_; if( !x.value_.empty() ) @@ -373,14 +373,14 @@ namespace boost { namespace leaf { } }; - template <> struct is_error_type: std::true_type { }; + template <> struct is_error_type: std::true_type { }; namespace leaf_detail { template <> - struct diagnostic + struct diagnostic { - static bool print( std::ostream & os, complete_diagnostic_info const & ) + static bool print( std::ostream & os, verbose_diagnostic_info const & ) { return false; } @@ -443,9 +443,9 @@ namespace boost { namespace leaf { } template - void put_complete_diagnostic_info( ev_type const & ev ) noexcept + void put_verbose_diagnostic_info( ev_type const & ev ) noexcept { - if( slot * p = tl_slot_ptr() ) + if( slot * p = tl_slot_ptr() ) { std::stringstream s; if( !diagnostic::print(s,ev.v) ) @@ -459,7 +459,7 @@ namespace boost { namespace leaf { return; } } - (void) p->put( ev_type(ev.e,complete_diagnostic_info(s)) ); + (void) p->put( ev_type(ev.e,verbose_diagnostic_info(s)) ); } } @@ -467,7 +467,7 @@ namespace boost { namespace leaf { void no_expect_slot( ev_type const & ev ) noexcept { put_unexpected(ev); - put_complete_diagnostic_info(ev); + put_verbose_diagnostic_info(ev); } template diff --git a/include/boost/leaf/handle.hpp b/include/boost/leaf/handle.hpp index 7b591b6..5421b73 100644 --- a/include/boost/leaf/handle.hpp +++ b/include/boost/leaf/handle.hpp @@ -21,10 +21,7 @@ namespace boost { namespace leaf { if( auto r = std::forward(try_block)() ) return *r; else - { - error const e = r.error(); - return ss.handle_error(error_info(e), std::forward(handlers)...); - } + return ss.handle_error(error_info(r.error()), std::forward(handlers)...); } namespace leaf_detail @@ -54,7 +51,7 @@ namespace boost { namespace leaf { f_(std::forward(f)) { } - result operator()( A... a ) + result operator()( A... a ) const { f_(a...); return { }; @@ -73,17 +70,13 @@ namespace boost { namespace leaf { ss.set_reset(true); return r; } - else + else if( auto rr = ss.handle_error(error_info(r.error()), handler_wrapper(std::forward(handlers))..., [&r] { return r; } ) ) { - error const e = r.error(); - if( auto rr = ss.handle_error(error_info(e), handler_wrapper(std::forward(handlers))..., [&r] { return r; } ) ) - { - ss.set_reset(true); - return rr; - } - else - return rr; + ss.set_reset(true); + return rr; } + else + return rr; } } } diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp index b7b7c1d..f740fa8 100644 --- a/include/boost/leaf/try.hpp +++ b/include/boost/leaf/try.hpp @@ -41,24 +41,20 @@ namespace boost { namespace leaf { } catch( std::exception const & ex ) { - error const e = get_error(ex); - return ss.handle_error(error_info(e,&ex,&cap,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(get_error(ex),&ex,&cap,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); } catch( ... ) { - error const e = next_error_value(); - return ss.handle_error(error_info(e,0,&cap,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(next_error_value(),0,&cap,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); } } catch( std::exception const & ex ) { - error const e = get_error(ex); - return ss.handle_error(error_info(e,&ex,0,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(get_error(ex),&ex,0,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); } catch( ... ) { - error const e = next_error_value(); - return ss.handle_error(error_info(e,0,0,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(next_error_value(),0,0,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); } } diff --git a/test/diagnostic_info_test.cpp b/test/diagnostic_info_test.cpp index b298003..d716a43 100644 --- a/test/diagnostic_info_test.cpp +++ b/test/diagnostic_info_test.cpp @@ -5,6 +5,7 @@ //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include #include #include "boost/core/lightweight_test.hpp" #include @@ -70,7 +71,6 @@ struct non_printable_info_non_printable_payload int main() { - BOOST_TEST(leaf::leaf_detail::tl_unexpected_enabled_counter()==0); leaf::try_( [ ] { @@ -91,7 +91,45 @@ int main() non_printable_info_non_printable_payload, leaf::e_errno, leaf::unexpected_error_info const &, - leaf::complete_diagnostic_info const & di ) + leaf::error_info const & ei ) + { + std::ostringstream st; + st << ei; + std::string s = st.str(); + BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); + BOOST_TEST(s.find(": N/A")!=s.npos); + BOOST_TEST(s.find(": printed printable_payload")!=s.npos); + BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); + BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); + BOOST_TEST(s.find(") in function")!=s.npos); + BOOST_TEST(s.find("Detected 2 attempts to communicate unexpected error objects, the first one of type ")!=s.npos); + BOOST_TEST(s.find("unexpected_test<2>")==s.npos); + std::cout << s; + } ); + + std::cout << std::endl; + + leaf::try_( + [ ] + { + LEAF_THROW( my_error(), + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + unexpected_test<1>{1}, + unexpected_test<2>{2}, + leaf::e_errno{ENOENT} ); + }, + [ ]( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + leaf::e_errno, + leaf::unexpected_error_info const &, + leaf::verbose_diagnostic_info const & di ) { std::ostringstream st; st << di; @@ -106,5 +144,53 @@ int main() BOOST_TEST(s.find("unexpected_test<2>")!=s.npos); std::cout << s; } ); + + std::cout << std::endl; + + leaf::try_( + leaf::capture_exception< + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + unexpected_test<1>, + unexpected_test<2>, + leaf::e_errno>( + [ ] + { + LEAF_THROW( my_error(), + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + unexpected_test<1>{1}, + unexpected_test<2>{2}, + leaf::e_errno{ENOENT} ); + } ), + [ ]( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + leaf::e_errno, + leaf::unexpected_error_info const &, + leaf::verbose_diagnostic_info const & di ) + { + std::ostringstream st; + st << di; + std::string s = st.str(); + BOOST_TEST(s.find("Detected exception_capture")!=s.npos); + BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); + BOOST_TEST(s.find(": N/A")!=s.npos); + BOOST_TEST(s.find(": printed printable_payload")!=s.npos); + BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); + BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); + BOOST_TEST(s.find(") in function")!=s.npos); + BOOST_TEST(s.find("Detected 2 attempts to communicate unexpected error objects, the first one of type ")!=s.npos); + BOOST_TEST(s.find("unexpected_test<2>")!=s.npos); + std::cout << s; + } ); + return boost::report_errors(); } From 4b61c8c9f6980c4e8404f0d31255f903df68e8fe Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Fri, 11 Jan 2019 00:18:25 -0800 Subject: [PATCH 18/34] error_info::get_error() -> error() --- include/boost/leaf/detail/static_store.hpp | 12 ++++++------ include/boost/leaf/error.hpp | 2 +- test/handle_some_test.cpp | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index 85da59b..b5faf64 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -177,7 +177,7 @@ namespace boost { namespace leaf { static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept { auto & sl = std::get,SlotsTuple>::value>(tup); - return sl.has_value() && sl.value().e==ei.get_error(); + return sl.has_value() && sl.value().e==ei.error(); } }; @@ -199,7 +199,7 @@ namespace boost { namespace leaf { if( sl.has_value() ) { auto const & v = sl.value(); - return v.e==ei.get_error() && check_value_pack(match_type::get(v.v),Value...); + return v.e==ei.error() && check_value_pack(match_type::get(v.v),Value...); } else return false; @@ -253,7 +253,7 @@ namespace boost { namespace leaf { template static T const & get( StaticStore const & ss, error_info const & ei ) noexcept { - T const * arg = ss.template peek(ei.get_error()); + T const * arg = ss.template peek(ei.error()); assert(arg!=0); return *arg; } @@ -265,7 +265,7 @@ namespace boost { namespace leaf { template static T const * get( StaticStore const & ss, error_info const & ei ) noexcept { - return ss.template peek(ei.get_error()); + return ss.template peek(ei.error()); } }; @@ -275,7 +275,7 @@ namespace boost { namespace leaf { template static match get( StaticStore const & ss, error_info const & ei ) noexcept { - E const * arg = ss.template peek(ei.get_error()); + E const * arg = ss.template peek(ei.error()); assert(arg!=0); return match{match_type::get(*arg)}; } @@ -307,7 +307,7 @@ namespace boost { namespace leaf { template static verbose_diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept { - verbose_diagnostic_info const * cdi = ss.template peek(ei.get_error()); + verbose_diagnostic_info const * cdi = ss.template peek(ei.error()); assert(cdi!=0); cdi->set_error_info(ei); return *cdi; diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 5cdbe64..32d6409 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -303,7 +303,7 @@ namespace boost { namespace leaf { assert(print_ex_!=0); } - error const & get_error() const noexcept + error const & error() const noexcept { return e_; } diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp index f9e844d..194744c 100644 --- a/test/handle_some_test.cpp +++ b/test/handle_some_test.cpp @@ -50,7 +50,7 @@ int main() { BOOST_TEST(c==0); c = 1; - return ei.get_error(); + return ei.error(); } ); BOOST_TEST(r); BOOST_TEST(c==42); @@ -578,7 +578,7 @@ int main() }, [ ]( leaf::error_info const & ei ) { - return ei.get_error(); + return ei.error(); } ); BOOST_TEST(r && *r==42); } From 7239b1c4ae2070d8b8958593c94431b8f5125c1b Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Fri, 11 Jan 2019 02:05:39 -0800 Subject: [PATCH 19/34] noexcept, name refinements --- include/boost/leaf/capture_result.hpp | 2 +- include/boost/leaf/detail/dynamic_store.hpp | 2 +- .../boost/leaf/detail/dynamic_store_impl.hpp | 2 +- include/boost/leaf/detail/optional.hpp | 9 ++++ include/boost/leaf/detail/static_store.hpp | 4 +- include/boost/leaf/error.hpp | 54 ++++++++++++------- include/boost/leaf/handle.hpp | 6 +-- include/boost/leaf/preload.hpp | 6 ++- include/boost/leaf/result.hpp | 4 +- include/boost/leaf/throw.hpp | 6 +-- 10 files changed, 60 insertions(+), 35 deletions(-) diff --git a/include/boost/leaf/capture_result.hpp b/include/boost/leaf/capture_result.hpp index 353c1b7..c2e389a 100644 --- a/include/boost/leaf/capture_result.hpp +++ b/include/boost/leaf/capture_result.hpp @@ -41,7 +41,7 @@ namespace boost { namespace leaf { { } - decltype(std::declval()(std::declval()...)) operator()( A ... a ) const noexcept + decltype(std::declval()(std::declval()...)) operator()( A ... a ) const { static_store ss; ss.set_reset(true); diff --git a/include/boost/leaf/detail/dynamic_store.hpp b/include/boost/leaf/detail/dynamic_store.hpp index 6b3c952..8cc7e97 100644 --- a/include/boost/leaf/detail/dynamic_store.hpp +++ b/include/boost/leaf/detail/dynamic_store.hpp @@ -29,7 +29,7 @@ namespace boost { namespace leaf { public: - virtual error get_error() const noexcept = 0; + virtual error const & get_error() const noexcept = 0; virtual error unload() noexcept = 0; virtual error unload( error const & ) noexcept = 0; }; diff --git a/include/boost/leaf/detail/dynamic_store_impl.hpp b/include/boost/leaf/detail/dynamic_store_impl.hpp index b6d8b6d..9654f0d 100644 --- a/include/boost/leaf/detail/dynamic_store_impl.hpp +++ b/include/boost/leaf/detail/dynamic_store_impl.hpp @@ -52,7 +52,7 @@ namespace boost { namespace leaf { error e_; std::tuple...> s_; - error get_error() const noexcept + error const & get_error() const noexcept { return e_; } diff --git a/include/boost/leaf/detail/optional.hpp b/include/boost/leaf/detail/optional.hpp index ef7ffe7..58f80a9 100644 --- a/include/boost/leaf/detail/optional.hpp +++ b/include/boost/leaf/detail/optional.hpp @@ -89,6 +89,15 @@ namespace boost { namespace leaf { } } + template + T & emplace( A && ... a ) + { + reset(); + (void) new(&value_) T(std::forward(a)...); + has_value_=true; + return value_; + } + T & put( T const & v ) { reset(); diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index b5faf64..bdfd3f1 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -162,13 +162,13 @@ namespace boost { namespace leaf { template bool check_exception_pack( error_info const & ei, Ex const * ) noexcept { - return dynamic_cast(ei.get_exception())!=0; + return dynamic_cast(ei.exception())!=0; } template bool check_exception_pack( error_info const & ei, Ex const *, ExRest const * ... ex_rest ) noexcept { - return dynamic_cast(ei.get_exception())!=0 || check_exception_pack(ei, ex_rest...); + return dynamic_cast(ei.exception())!=0 || check_exception_pack(ei, ex_rest...); } template diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 32d6409..2bfecf6 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -147,9 +147,6 @@ namespace boost { namespace leaf { public: - error( error const & ) noexcept = default; - error( error && ) noexcept = default; - friend bool operator==( error const & e1, error const & e2 ) noexcept { return e1.id_==e2.id_; @@ -201,6 +198,11 @@ namespace boost { namespace leaf { error e; E v; + explicit ev_type( error const & e ) noexcept: + e(e) + { + } + ev_type( error const & e, E const & v ): e(e), v(v) @@ -303,14 +305,19 @@ namespace boost { namespace leaf { assert(print_ex_!=0); } - error const & error() const noexcept + leaf::error const & error() const noexcept { return e_; } - std::exception const * get_exception() const noexcept + bool exception_caught() const noexcept { - assert(print_ex_!=0); + return print_ex_!=0; + } + + std::exception const * exception() const noexcept + { + assert(exception_caught()); return ex_; } @@ -334,7 +341,12 @@ namespace boost { namespace leaf { public: - verbose_diagnostic_info( verbose_diagnostic_info && x ): + verbose_diagnostic_info() noexcept: + ei_(0) + { + } + + verbose_diagnostic_info( verbose_diagnostic_info && x ) noexcept: ei_(0), value_(std::move(x.value_)), already_(std::move(x.already_)) @@ -342,10 +354,10 @@ namespace boost { namespace leaf { x.ei_ = 0; } - explicit verbose_diagnostic_info( std::stringstream const & s ): - ei_(0), - value_(s.str()+" {unexpected}") + void reset() noexcept { + value_.clear(); + already_.clear(); } template @@ -359,7 +371,7 @@ namespace boost { namespace leaf { } } - void set_error_info( error_info const & ei ) const + void set_error_info( error_info const & ei ) const noexcept { ei_ = &ei; } @@ -380,7 +392,7 @@ namespace boost { namespace leaf { template <> struct diagnostic { - static bool print( std::ostream & os, verbose_diagnostic_info const & ) + static bool print( std::ostream & os, verbose_diagnostic_info const & ) noexcept { return false; } @@ -415,6 +427,7 @@ namespace boost { namespace leaf { using base::has_value; using base::value; using base::reset; + using base::emplace; }; template @@ -445,21 +458,22 @@ namespace boost { namespace leaf { template void put_verbose_diagnostic_info( ev_type const & ev ) noexcept { - if( slot * p = tl_slot_ptr() ) + if( slot * sl = tl_slot_ptr() ) { std::stringstream s; if( !diagnostic::print(s,ev.v) ) return; - if( p->has_value() ) + if( auto * pv = sl->has_value() ) { - auto & p_ev = p->value(); - if( p_ev.e==ev.e ) + if( pv->e!=ev.e ) { - p_ev.v.add(s); - return; + pv->e = ev.e; + pv->v.reset(); } + pv->v.add(s); } - (void) p->put( ev_type(ev.e,verbose_diagnostic_info(s)) ); + else + sl->emplace(ev.e).v.template add(s); } } @@ -523,7 +537,7 @@ namespace boost { namespace leaf { } template - error make_error( char const * file, int line, char const * function, E && ... e ) + error make_error( char const * file, int line, char const * function, E && ... e ) noexcept { assert(file&&*file); assert(line>0); diff --git a/include/boost/leaf/handle.hpp b/include/boost/leaf/handle.hpp index 5421b73..72a7567 100644 --- a/include/boost/leaf/handle.hpp +++ b/include/boost/leaf/handle.hpp @@ -13,7 +13,7 @@ namespace boost { namespace leaf { template - typename std::remove_reference::return_type>().value())>::type handle_all( TryBlock && try_block, Handlers && ... handlers ) noexcept + typename std::remove_reference::return_type>().value())>::type handle_all( TryBlock && try_block, Handlers && ... handlers ) { using namespace leaf_detail; typename deduce_static_store::type>::type ss; @@ -37,7 +37,7 @@ namespace boost { namespace leaf { f_(std::forward(f)) { } - R operator()( A... a ) + R operator()( A... a ) const { return f_(a...); } @@ -60,7 +60,7 @@ namespace boost { namespace leaf { } template - typename leaf_detail::function_traits::return_type handle_some( TryBlock && try_block, Handlers && ... handlers ) noexcept + typename leaf_detail::function_traits::return_type handle_some( TryBlock && try_block, Handlers && ... handlers ) { using namespace leaf_detail; using R = typename function_traits::return_type; diff --git a/include/boost/leaf/preload.hpp b/include/boost/leaf/preload.hpp index d526a05..22a7e2f 100644 --- a/include/boost/leaf/preload.hpp +++ b/include/boost/leaf/preload.hpp @@ -40,14 +40,16 @@ namespace boost { namespace leaf { { slot * s_; E v_; + public: + explicit preloaded_item( E && v ) noexcept: s_(tl_slot_ptr()), v_(std::forward(v)) { } - void trigger( error e ) noexcept + void trigger( error const & e ) noexcept { if( s_ ) { @@ -132,7 +134,7 @@ namespace boost { namespace leaf { { } - void trigger( error e ) noexcept + void trigger( error const & e ) noexcept { if( s_ ) { diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index d6eea25..dfa12ef 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -131,7 +131,7 @@ namespace boost { namespace leaf { move_from(std::move(x)); } - result() noexcept: + result(): value_(T()), which_(leaf_detail::result_variant::value) { @@ -247,7 +247,7 @@ namespace boost { namespace leaf { { } - result() noexcept = default; + result() = default; result( leaf::error const & e ) noexcept: base(e) diff --git a/include/boost/leaf/throw.hpp b/include/boost/leaf/throw.hpp index 048c4bc..cdefcfe 100644 --- a/include/boost/leaf/throw.hpp +++ b/include/boost/leaf/throw.hpp @@ -18,7 +18,7 @@ namespace boost { namespace leaf { namespace leaf_detail { - inline void enforce_std_exception( std::exception const & ) { } + inline void enforce_std_exception( std::exception const & ) noexcept { } template class exception: @@ -40,7 +40,7 @@ namespace boost { namespace leaf { }; template - exception exception_( char const * file, int line, char const * function, Ex && ex, E && ... e ) + exception exception_( char const * file, int line, char const * function, Ex && ex, E && ... e ) noexcept { assert(file&&*file); assert(line>0); @@ -50,7 +50,7 @@ namespace boost { namespace leaf { } template - leaf_detail::exception exception( Ex && ex, E && ... e ) + leaf_detail::exception exception( Ex && ex, E && ... e ) noexcept { return leaf_detail::exception( std::forward(ex), std::forward(e)... ); } From cab233c83a543d5d3abc7932b25180f80050bf1a Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Fri, 11 Jan 2019 02:14:23 -0800 Subject: [PATCH 20/34] compile errors --- include/boost/leaf/detail/static_store.hpp | 16 ++++++++-------- include/boost/leaf/error.hpp | 4 ++-- test/handle_some_test.cpp | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index bdfd3f1..85da59b 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -162,13 +162,13 @@ namespace boost { namespace leaf { template bool check_exception_pack( error_info const & ei, Ex const * ) noexcept { - return dynamic_cast(ei.exception())!=0; + return dynamic_cast(ei.get_exception())!=0; } template bool check_exception_pack( error_info const & ei, Ex const *, ExRest const * ... ex_rest ) noexcept { - return dynamic_cast(ei.exception())!=0 || check_exception_pack(ei, ex_rest...); + return dynamic_cast(ei.get_exception())!=0 || check_exception_pack(ei, ex_rest...); } template @@ -177,7 +177,7 @@ namespace boost { namespace leaf { static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept { auto & sl = std::get,SlotsTuple>::value>(tup); - return sl.has_value() && sl.value().e==ei.error(); + return sl.has_value() && sl.value().e==ei.get_error(); } }; @@ -199,7 +199,7 @@ namespace boost { namespace leaf { if( sl.has_value() ) { auto const & v = sl.value(); - return v.e==ei.error() && check_value_pack(match_type::get(v.v),Value...); + return v.e==ei.get_error() && check_value_pack(match_type::get(v.v),Value...); } else return false; @@ -253,7 +253,7 @@ namespace boost { namespace leaf { template static T const & get( StaticStore const & ss, error_info const & ei ) noexcept { - T const * arg = ss.template peek(ei.error()); + T const * arg = ss.template peek(ei.get_error()); assert(arg!=0); return *arg; } @@ -265,7 +265,7 @@ namespace boost { namespace leaf { template static T const * get( StaticStore const & ss, error_info const & ei ) noexcept { - return ss.template peek(ei.error()); + return ss.template peek(ei.get_error()); } }; @@ -275,7 +275,7 @@ namespace boost { namespace leaf { template static match get( StaticStore const & ss, error_info const & ei ) noexcept { - E const * arg = ss.template peek(ei.error()); + E const * arg = ss.template peek(ei.get_error()); assert(arg!=0); return match{match_type::get(*arg)}; } @@ -307,7 +307,7 @@ namespace boost { namespace leaf { template static verbose_diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept { - verbose_diagnostic_info const * cdi = ss.template peek(ei.error()); + verbose_diagnostic_info const * cdi = ss.template peek(ei.get_error()); assert(cdi!=0); cdi->set_error_info(ei); return *cdi; diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 2bfecf6..7cbe8a8 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -305,7 +305,7 @@ namespace boost { namespace leaf { assert(print_ex_!=0); } - leaf::error const & error() const noexcept + leaf::error const & get_error() const noexcept { return e_; } @@ -315,7 +315,7 @@ namespace boost { namespace leaf { return print_ex_!=0; } - std::exception const * exception() const noexcept + std::exception const * get_exception() const noexcept { assert(exception_caught()); return ex_; diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp index 194744c..f9e844d 100644 --- a/test/handle_some_test.cpp +++ b/test/handle_some_test.cpp @@ -50,7 +50,7 @@ int main() { BOOST_TEST(c==0); c = 1; - return ei.error(); + return ei.get_error(); } ); BOOST_TEST(r); BOOST_TEST(c==42); @@ -578,7 +578,7 @@ int main() }, [ ]( leaf::error_info const & ei ) { - return ei.error(); + return ei.get_error(); } ); BOOST_TEST(r && *r==42); } From fee386826147fb27d0bf17442319421ecd86375f Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Sun, 13 Jan 2019 09:13:54 -0800 Subject: [PATCH 21/34] documentation, cosmetic changes --- doc/leaf.adoc | 1243 +++++++++-------- doc/synopses/capture_exception.adoc | 14 + doc/synopses/capture_result.adoc | 14 + doc/synopses/common.adoc | 7 - .../diagnostic_output_current_exception.adoc | 11 - doc/synopses/error.adoc | 10 +- doc/synopses/error_capture.adoc | 30 - doc/synopses/exception.adoc | 28 - doc/synopses/exception_capture.adoc | 17 - doc/synopses/exception_to_result.adoc | 14 + doc/synopses/expect.adoc | 40 - doc/synopses/handle.adoc | 18 + doc/synopses/throw.adoc | 17 + doc/synopses/try.adoc | 15 + example/capture_eh.cpp | 34 +- example/capture_result.cpp | 34 +- example/exception_to_result.cpp | 52 +- example/lua_callback_eh.cpp | 64 +- example/lua_callback_result.cpp | 72 +- example/print_file_eh.cpp | 194 +-- example/print_file_result.cpp | 242 ++-- example/print_half.cpp | 26 +- include/boost/leaf/all.hpp | 8 +- include/boost/leaf/capture_exception.hpp | 10 +- include/boost/leaf/capture_result.hpp | 8 +- include/boost/leaf/common.hpp | 8 +- .../boost/leaf/detail/captured_exception.hpp | 8 +- include/boost/leaf/detail/demangle.hpp | 8 +- include/boost/leaf/detail/dynamic_store.hpp | 8 +- .../boost/leaf/detail/dynamic_store_impl.hpp | 10 +- include/boost/leaf/detail/function_traits.hpp | 10 +- include/boost/leaf/detail/optional.hpp | 10 +- include/boost/leaf/detail/print.hpp | 10 +- .../leaf/detail/print_exception_info.hpp | 10 +- include/boost/leaf/detail/static_store.hpp | 121 +- include/boost/leaf/error.hpp | 41 +- include/boost/leaf/exception_to_result.hpp | 8 +- include/boost/leaf/handle.hpp | 24 +- include/boost/leaf/preload.hpp | 14 +- include/boost/leaf/result.hpp | 8 +- include/boost/leaf/throw.hpp | 8 +- include/boost/leaf/try.hpp | 22 +- test/_hpp_all_test.cpp | 8 +- test/_hpp_capture_exception_test.cpp | 8 +- test/_hpp_capture_result_test.cpp | 8 +- test/_hpp_common_test.cpp | 8 +- test/_hpp_error_test.cpp | 8 +- test/_hpp_exception_to_result_test.cpp | 8 +- test/_hpp_handle_test.cpp | 8 +- test/_hpp_preload_test.cpp | 8 +- test/_hpp_result_test.cpp | 8 +- test/_hpp_throw_test.cpp | 8 +- test/_hpp_try_test.cpp | 8 +- test/capture_exception_async_test.cpp | 8 +- test/capture_exception_state_test.cpp | 10 +- test/capture_exception_unload_test.cpp | 12 +- test/capture_result_async_test.cpp | 8 +- test/capture_result_state_test.cpp | 10 +- test/capture_result_unload_test.cpp | 8 +- test/defer_basic_test.cpp | 8 +- test/defer_nested_error_exception_test.cpp | 8 +- test/defer_nested_error_result_test.cpp | 8 +- .../defer_nested_new_error_exception_test.cpp | 8 +- test/defer_nested_new_error_result_test.cpp | 8 +- test/defer_nested_success_exception_test.cpp | 8 +- test/defer_nested_success_result_test.cpp | 8 +- test/diagnostic_info_test.cpp | 12 +- test/function_traits_test.cpp | 8 +- test/handle_all_test.cpp | 122 +- test/handle_some_basic_test.cpp | 10 +- test/handle_some_test.cpp | 322 ++--- test/is_error_type_fail_test.cpp | 10 +- test/is_error_type_test.cpp | 18 +- test/multiple_errors_test.cpp | 8 +- test/optional_test.cpp | 8 +- test/preload_basic_test.cpp | 8 +- test/preload_nested_error_exception_test.cpp | 8 +- test/preload_nested_error_result_test.cpp | 8 +- ...reload_nested_new_error_exception_test.cpp | 8 +- test/preload_nested_new_error_result_test.cpp | 8 +- .../preload_nested_success_exception_test.cpp | 8 +- test/preload_nested_success_result_test.cpp | 8 +- test/print_test.cpp | 8 +- test/result_bad_result_test.cpp | 8 +- test/result_state_test.cpp | 136 +- test/static_store_deduction_test.cpp | 14 +- test/static_store_test.cpp | 8 +- test/try_exception_test.cpp | 8 +- test/try_test.cpp | 44 +- 89 files changed, 1841 insertions(+), 1709 deletions(-) create mode 100644 doc/synopses/capture_exception.adoc create mode 100644 doc/synopses/capture_result.adoc delete mode 100644 doc/synopses/diagnostic_output_current_exception.adoc delete mode 100644 doc/synopses/error_capture.adoc delete mode 100644 doc/synopses/exception.adoc delete mode 100644 doc/synopses/exception_capture.adoc create mode 100644 doc/synopses/exception_to_result.adoc delete mode 100644 doc/synopses/expect.adoc create mode 100644 doc/synopses/handle.adoc create mode 100644 doc/synopses/throw.adoc create mode 100644 doc/synopses/try.adoc diff --git a/doc/leaf.adoc b/doc/leaf.adoc index ea6920c..605fb38 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -2,12 +2,13 @@ :icons: font :prewrap!: :source-highlighter: coderay -:stylesheet: zajo.css +:stylesheet: ../zajo.css = LEAF Low-latency Error Augmentation Framework for C++11 :toclevels: 3 :toc: left :toc-title: +:sourcedir: .. [abstract] == Abstract @@ -19,225 +20,34 @@ LEAF is a {CPP}11 error handling library. Features: * No dynamic memory allocations.footnote:[Except when error objects are transported between threads, see <>.] -* Associate objects of arbitrary types with any failure -- when it is initially reported or at a later time. +* Any error-related object of any type is efficiently delivered to the correct error handler. -* Compatible with `std::error_code`, `errno` and any other error handling API. +* Compatible with `std::error_code`, `errno` and any other `noexcept`-moveable type. * Use with or without exception handling. * Support for multi-thread programming. [.text-right] -https://github.com/zajo/leaf[GitHub] | <> | <> | <> | <> | <> | <> +https://github.com/zajo/leaf[GitHub] | <> <> | <> | <> | <> ==== -NOTE: If you are at least an intermediate level developer and have seen `expected`/`outcome`-like interfaces before, and you are also wondering how the heck you can return a type-erased error object without using heap allocations, you may want to read the <> first. - [[introduction]] -== Two Minute Introduction +== Five Minute Introduction -Let's suppose that to handle errors from a given function `f` we need an error code and a file name. In addition -- depending on the error -- we may need a request ID, also of type string. In terms of LEAF, we can write: +We'll write a short but complete program that reads a text file in a buffer and prints it to `std::cout`, using LEAF to handle errors. For demonstration, we'll implement two versions: one using error codes, and one using exception handling. -[source,c++] ----- -{ - leaf::expect exp; //<1> +[[introduction-result]] +=== Using Error Codes - if( leaf::result r = f() ) - { - //Success! //<2> - } - else - { - bool matched = leaf::handle_error( exp, r, //<3> - - [ ]( error_code ec, e_file_name const & fn, e_request_id const & id ) - { - //<4> - }, - - [ ]( error_code ec, e_file_name const & fn ) - { - //<5> - } - - ); - } -} ----- -[.text-right] -`<>` | `<>` | <> - -<1> The `exp` object provides storage for an `error_code`, an `e_file_name` and an `e_request_id`, which `f()` (next line) may communicate in case it fails. -<2> Successful result values are stored inside of `r` of type `result`, which is a value-or-error variant type. -<3> The call to `f()` has failed; `handle_error` takes `exp`, `r`, and any number of error handling functions to be matched, in order, against the objects currently stored in `exp`, associated with `r`. -<4> Handle the case where we received `error_code`, `e_file_name` and `e_request_id` (associated with the failure reported in `r`). -<5> Handle the case where we only received `error_code` and `e_file_name` (associated with the failure reported in `r`). - -The `error_code` can be a simple `enum`: +First, we need an `enum` to define our error codes: [source,c++] ---- enum error_code { - ec1=1, - ec2, - .... -}; ----- - -Because both the file name and the request ID are strings, we will wrap each of them in a simple `struct`, so that the type system can tell them apart: - -[source,c++] ----- -struct e_file_name { std::string value; }; -struct e_request_id { std::string value; }; ----- - -Reporting an error with LEAF looks like this: - -[source,c++] ----- -leaf::result g() noexcept -{ - if( success ) - return T(....); <1> - else - return leaf::error( ec1, e_request_id{id} ); //<2> -} ----- -[.text-right] -`<>` | `<>` - -<1> Initializing the returned `result` with a `T` object indicates a success. -<2> Report an error, store the passed `error_code` and `e_request_id` in appropriate `expect` object(s) from calling scopes. Presumably, at this point we don't have access to a relevant file name, but that's fine -- we associate with the reported error what relevant information we do have: the error code and the request ID. - -Forwarding errors reported by a lower level function looks like this: - -[source,c++] ----- -leaf::result f( char const * file_name ) noexcept -{ - if( leaf::result r=g() ) - { - .... - return r; //<1> - } - else - return r.error( e_file_name{file_name} ); //<2> -} ----- -[.text-right] -`<>` | `<>` - -<1> Success, return `r`. -<2> Forward the error reported (by `g`) in `r`, storing `e_file_name` in appropriate `expect` object(s) from calling scopes. Note that `g` has no access to the `file_name` passed to `f` and therefore is unable to include it in the reported error; yet the `file_name` is very much relevant to that error. At this point the file name is associated with the error in addition to the relevant `error_code` and `e_request_id` which, presumably, `g` has already communicated. - -== What If I Want to Use Exception Handling? - -That would be a 1-minute introduction. :-) - -Let's assume that to handle exceptions from a given function `f` we need a file name. In addition -- depending on the error -- we may need a request ID, also of type string. In terms of LEAF, we can write: - -[source,c++] ----- -{ - leaf::expect exp; //<1> - - try - { - f(); - } - catch( my_error const & e ) - { - leaf::handle_exception( exp, e, //<2> - - [ ]( e_file_name const & fn, e_request_id const & id ) - { - //<3> - }, - - [ ]( e_file_name const & fn ) - { - //<4> - } - - ); - } -} ----- -[.text-right] -`<>` | <> - -<1> The `exp` object provides storage for an `e_file_name` and an `e_request_id`, which `f()` (next line) may communicate in case it throws. -<2> We caught a `my_error` exception; `handle_error` takes `exp`, `e`, and any number of error handling functions to be matched, in order, against the error objects currently stored in `exp`, associated with `e`. -<3> Handle the case where we received `e_file_name` and `e_request_id` (associated with the failure reported by `e`). -<4> Handle the case where we only received `e_file_name` (associated with the failure reported by `e`). - -Because both the file name and the request ID are strings, we will wrap each of them in a simple `struct`, so that the type system can tell them apart: - -[source,c++] ----- -struct e_file_name { std::string value; }; -struct e_request_id { std::string value; }; ----- - -Such error objects can be passed to LEAF at the point of the `throw`: - -[source,c++] ----- -T g() -{ - if( success ) - return T(....); <1> - else - throw leaf::exception( my_error(), e_request_id{id} ); //<2> -} ----- -[.text-right] -`<>` - -<1> Success, just return `T`. -<2> Throw a `my_error` object, store the passed `e_request_id` in appropriate `expect` object(s) from calling scopes. Presumably, at this point we don't have access to a relevant file name, but that's fine -- we associate with the exception object what relevant information we do have: the request ID. - -Exceptions can be augmented in exception-neutral contexts: - -[source,c++] ----- -void f( char const * file_name ) -{ - auto propagate = leaf::preload( e_file_name{file_name} ); //<1> - - g(); -} ----- -[.text-right] -<> - -<1> In case `g()` (next line) throws, the `file_name` will be associated with the exception object, in addition to the relevant `e_request_id` which, presumably, `g` has already communicated. Note that `g` has no access to the `file_name` passed to `f` and therefore is unable to include it in the reported error; yet the `file_name` is very much relevant to that error. - -TIP: When used with exception handling, LEAF can be viewed as <>. - -[[tutorial]] -== Tutorial - -We'll write a short but complete program, using LEAF to handle errors. We'll implement two versions, one that uses exception handling, and one that does not. To see the source code of the complete programs from this tutorial, follow these links: - -* https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[print_file_result.cpp] (without exception handling) -* https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[print_file_eh.cpp] (with exception handling) - -First, let's see how to use LEAF without exception handling. - -[[tutorial-noexcept]] -=== Using `result` - -We'll write a program that reads a text file in a buffer and prints it to `std::cout`, using LEAF to handle errors. First, we need an `enum` to define our error codes: - -[source,c++] ----- -enum error_code -{ - input_file_open_error=1, + bad_command_line = 1, + input_file_open_error, input_file_size_error, input_file_read_error, input_eof_error, @@ -245,256 +55,216 @@ enum error_code }; ---- -We don't need an enumerated value that indicates success. That's because we will use the handy class template `<>` as the return type in functions which may fail. It is a value-or-error variant type which holds a `T` except if initialized with a `leaf::<>`. - -To enable `leaf::error` to work with our `error_code` `enum`, we need to specialize the <> template: +Very nice, but how does LEAF know that this `enum` represents error codes and not, say, types of cold cuts sold at http://order.bcdeli.com/[Bay Cities Italian Deli]? It doesn't, unless we tell it: [source,c++] ---- namespace boost { namespace leaf { - template<> struct is_error_type: std::true_type { }; + template<> struct is_e_type: std::true_type { }; } } ---- +[.text-right] +<> -[TIP] --- -The `is_error_type` template needs not be specialized for: - -* types that define an accessible data member `value`, - -* `std::error_code`, - -* `boost::system::error_code`. --- - -Here is a function that reads data from a `FILE` into a buffer and reports the various errors which may occur (it returns `result` because in case of success it doesn't return a value): +Now, reading and printing a file may not seem like a complex job, but let's split it into several functions, each communicating failures using `leaf::result`: [source,c++] ---- -leaf::result file_read( FILE & f, void * buf, int size ) -{ - int n = fread( buf, 1, size, &f ); - if( ferror(&f) ) - return leaf::error( input_file_read_error, e_errno{errno} ); //<1> +//Parse the command line, return the file name. +leaf::result parse_command_line( int argc, char const * argv[ ] ); - if( n!=size ) - return leaf::error( input_eof_error ); //<2> +//Open a file for reading. +leaf::result> file_open( char const * file_name ); - return { }; //<3> -} +//Return the size of the file. +leaf::result file_size( FILE & f ); + +//Read size bytes from f into buf. +leaf::result file_read( FILE & f, void * buf, int size ); ---- [.text-right] -`<>` | `<>` +<> -<1> If `ferror` indicates an error, we return `input_file_read_error` and, because there is a relevant `errno` code, we _also_ pass that to the `leaf::<>` constructor (LEAF defines `struct e_errno { int value; }`). -<2> If `fread` reports that it couldn't read all of the data requested, we return `input_eof_error`. In this case there is no relevant `errno` to pass on, because this is not an error as far as `fread` is concerned. -<3> `result` can be initialized with `{ }` to indicate success. +What's this `result` template, you ask? It's a simple variant: it holds a value of type `T` or else it represents an error condition. This way, functions can communicate failures -- and we don't even need an error code to represent success. Which makes sense if you think about it. -NOTE: `leaf::error` may be passed objects of arbitrary movable types, however objects of types that are not specific to error handling -- such as `errno` values, which are of type `int` -- should be wrapped in a `struct`. By convention, such structs use the `e_` prefix and contain a single data member called `value`, e.g. `struct e_errno { int value; }`. + - + -For example, we could define `struct e_input_name { std::string value; }` and `struct e_output_name { std::string value; }` and LEAF will treat them as separate entities even though their `.value` members are of the same type `std::string`. - -Now, let's consider a possible caller of `file_read`, called `print_file`: +Now on to the `main` function: it brings everything together and handles all the errors that may occur. Did I say *all* the errors? I did, so don't be surprised when I tell you that this is done using `leaf::handle_all`. It has the following signature: [source,c++] ---- -leaf::result print_file( char const * file_name ) -{ - leaf::result> f = file_open(file_name); - if( !f ) //<1> - return f.error(); //<2> - - auto propagate = leaf::preload( e_file_name{file_name} ); //<3> - - leaf::result s = file_size(*f.value()); - if( !s ) //<4> - return s.error(); //<5> - - std::string buffer( 1+s.value(), '\0' ); - leaf::result fr = file_read( *f.value, &buffer[0], buffer.size()-1 ); - if( !fr ) - return fr.error(); - - std::cout << buffer; - std::cout.flush(); - if( std::cout.fail() ) - return leaf::error( cout_error ); //<6> - - return { }; //<7> -} +template +<> handle_all( TryBlock && try_block, Handler && ... handler ); ---- -[.text-right] -`<>` | `<>` | <> -<1> If `file_open` returns an error... -<2> ...we forward it to the caller. Notice that we don't return `leaf::error()`, which would indicate a newly detected error; we return `f.error()`, which propagates the error already stored in `f`. -<3> `<>` takes any number of error objects and prepares them to become associated (automatically, at the time the returned object expires) with a `leaf::<>` value created in the future. The effect is that from this point on, any error returned or forwarded by `print_file` will have an associated file name, in addition to everything else passed to `leaf::<>` explicitly (`e_file_name` is defined as `struct e_file_name { std::string value; }`). -<4> If `file_size` returns an error... -<5> ...we forward it to the caller. -<6> If `std::cout` fails to write the buffer, we return `cout_error`. -<7> Success! +`TryBlock` is a function type, almost always a lambda, which is required to return an instance of the `result` template. The first thing `handle_all` does is invoke the `try_block`. If the returned object indicates success, `handle_all` returns the contained value; otherwise it calls a suitable error handling function from the `handler...` list. -Notice the repetitiveness in simply forwarding errors to the caller. LEAF defines two macros, `<>` and `<>`, which can help reduce the clutter: - -* The `LEAF_AUTO` macro takes two arguments, an identifier and a `result`. In case the passed `result` indicates an error, `LEAF_AUTO` returns that error to the caller (therefore control leaves the enclosing function). In case of success, `LEAF_AUTO` defines a variable, of type `T &` (using the provided identifier) that refers to the `T` object stored inside the passed `result`. - -* The `LEAF_CHECK` macro is designed to be used similarly in functions that return `result`, but of course it doesn't define a variable. - -Below is the same `print_file` function simplified using `LEAF_AUTO` and `LEAF_CHECK` (remember that the variables defined by `LEAF_AUTO` are not of type `result`, but of type `T &`; for example `s` used to be of type `result`, but now its type is simply `int &`): - -[source,c++] ----- -leaf::result print_file( char const * file_name ) -{ - LEAF_AUTO( f, file_open(file_name) ); //<1> - - auto propagate = leaf::preload( e_file_name{file_name} ); - - LEAF_AUTO( s, file_size(*f) ); //<2> - - std::string buffer( 1+s, '\0' ); - LEAF_CHECK( file_read(*f,&buffer[0],buffer.size()-1) ); //<3> - - std::cout << buffer; - std::cout.flush(); - if( std::cout.fail() ) - return leaf::error( cout_error ); - - return { }; -} ----- -[.text-right] -<> | <> | <> | `<>` - -<1> Call `file_open`, check for errors, unpack the returned `result>` and define a variable `f` of type `std::shared_ptr &` that refers to its `<>()`. -<2> Call `file_size`, check for errors, unpack the returned `result` and define a variable `s` of type `int &` that refers to its `value()`. -<3> Call `file_read`, check for errors (`file_read` returns `result`). - -Finally, let's look at the `main` function, which handles all errors in this program: +Now, let's see just what kind of a `TryBlock` does `main` pass to `handle_all`: [source,c++] ---- int main( int argc, char const * argv[ ] ) { - char const * fn = parse_command_line(argc,argv); - if( !fn ) - { - std::cout << "Bad command line argument" << std::endl; - return 1; - } + return leaf::handle_all( - //We expect error_code, e_file_name and e_errno objects to be associated - //with errors handled in this function. They will be stored inside of exp. - leaf::expect exp; - - if( auto r = print_file(fn) ) - { - return 0; //Success, we're done! - } - else - { - //Probe exp for the error_code object associated with the error stored in r. - switch( auto ec = *leaf::peek(exp,r) ) + [&]() -> leaf::result <1> { - case input_file_open_error: - { - //handle_error takes a list of functions (in this case only one). It attempts to - //match each function (in order) to objects currently available in exp, which - //are associated with the error value stored in r. If no function can be matched, - //handle_error returns false. Otherwise the matched function is invoked with - //the corresponding available error objects. - bool matched = leaf::handle_error( exp, r, + leaf::result file_name = parse_command_line(argc,argv); + if( !file_name ) + return file_name.error(); <2> - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - if( errn.value==ENOENT ) - std::cerr << "File not found: " << fn.value << std::endl; - else - std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; - } + auto propagate = leaf::preload( leaf::e_file_name{*file_name} ); <3> - ); - assert(matched); - return 2; - } + leaf::result> f = file_open(*file_name); + if( !f ) + return f.error(); <4> - case input_file_size_error: - case input_file_read_error: - case input_eof_error: - { - //In this case handle_error is given 3 functions. It will first check if both - //e_file_name and e_errno, associated with r, are avialable in exp; if not, it will - //next check if just e_errno is available; and if not, the last function (which - //takes no arguments) will always match to print a generic error message. - bool matched = leaf::handle_error( exp, r, + leaf::result s = file_size(**f); + if( !s ) + return s.error(); <4> - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; - }, + std::string buffer( 1 + *s, '\0' ); + leaf::result fr = file_read(**f, &buffer[0], buffer.size()-1); + if( !fr ) + return fr.error(); <4> - [ ] ( e_errno const & errn ) - { - std::cerr << "I/O error, errno=" << errn << std::endl; - }, + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + return leaf::new_error( cout_error, leaf::e_errno{errno} ); <5> - [ ] - { - std::cerr << "I/O error" << std::endl; - } + return 0; + }, - ); - assert(matched); - return 3; - } + .... <6> - case cout_error: - { - //Report failure to write to std::cout, print the relevant errno. - bool matched = leaf::handle_error( exp, r, - - [ ] ( e_errno const & errn ) - { - std::cerr << "Output error, errno=" << errn << std::endl; - } - - ); - assert(matched); - return 4; - } - - //This catch-all case helps diagnose logic errors (presumably, missing case labels - //in the switch statement). - default: - { - std::cerr << "Unknown error code " << ec << ", cryptic information follows." << std::endl; //<7> - leaf::diagnostic_output(std::cerr,exp,r); - return 5; - } - } - } + ); <7> } ---- - [.text-right] -`<>` | <> | <> | <> +<> | <> | <> | <> | <> -To summarize, when using LEAF without exception handling: +<1> Our `TryBlock` returns a `result`. In case of success, it will hold `0`, which will be returned from `main` to the OS. +<2> If `parse_command_line` returns an error, we return that error to `handle_all` (which invoked us). Otherwise, the returned `file_name` stores a value of type `char const *`, which we can access by "dereferencing" it: `*file_name`. +<3> From now on, all errors escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name { std::string value; };` ). It's as if every time one of the following functions reports an error, `preload` says "wait, put this `e_file_name` thing with the error, it's important!" +<4> Call more functions, forward any failures to the caller... +<5> ...but this is slightly different: we didn't get a failure via `result` from another function, this is our own error we've detected! We return a `new_error`, passing the `cout_error` error code and the system `errno` (LEAF defines `struct e_errno { int value; };` ). +<6> List of error handler goes here. We'll see that later. +<7> This concludes the `handle_all` arguments -- as well as our program! -* Functions that may fail return instances of `<>`, a value-or-error variant class template. -* In case a function detects a failure, the returned `result` can be initialized implicitly by returning `leaf::<>`, which may be passed any and all information we have that is relevant to the failure, in the form of objects of arbitrary movable types. -* When a lower level function reports an error, that error is forwarded to the caller, passing any additional relevant information available in the current scope. -* In order for any object passed to `leaf::<>` to be stored rather than discarded, the function that handles the error must contain an instance of the class template `<>` that provides the necessary storage for its type. -* Using `<>`, available objects associated with the `<>` value being handled can be matched to what is required in order to deal with that `error`. +Nice and simple! Writing the `TryBlock`, we concentrate on the "no errors" code path -- if we encounter any error we just return it to `handle_all` for processing. Well, that's if we're being good and using RAII for automatic clean-up -- which we are, `shared_ptr` will automatically close the file for us. -NOTE: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[here]. There is also https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[another] version of the same program that uses exception handling to report errors (see <>). +Now let's look at the juicy second part of the call to `handle_all`, which lists our error handler: +[source,c++] +---- +int main( int argc, char const * argv[ ] ) +{ + return leaf::handle_all( + + [&]() -> leaf::result + { + .... <1> + }, + + [ ](leaf::match, <2> + leaf::match, + leaf::e_file_name const & fn) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + [ ](leaf::match, <3> + leaf::e_errno const & errn, + leaf::e_file_name const & fn) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + [ ](leaf::match, <4> + leaf::e_errno const & errn, + leaf::e_file_name const & fn) + { + std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; + return 3; + }, + + [ ](leaf::match, <5> + leaf::e_errno const & errn) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + [ ](leaf::match) <6> + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + [ ](leaf::error_info const & unmatched) <7> + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } + ); +} +---- +[.text-right] +<> | <> | <> + +<1> This is the `TryBlock` from the previous listing, it does all the work and bails out if it encounters an error. In that case, `handle_all` will consider the error handler that follow, in order, and it will call the first one that can deal with the error: + +<2> This handler will be called if the error includes: + +pass:[•] an object of type `error_code` equal to `input_file_open_error`, and + +pass:[•] an object of type `leaf::e_errno` that has `.value` equal to `ENOENT`, and + +pass:[•] an object of type `leaf::e_file_name`. + +<3> This handler will be called if the error includes: + +pass:[•] an object of type `error_code` equal to `input_file_open_error`, and + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), and + +pass:[•] an object of type `leaf::e_file_name`. + +<4> This handler will be called if the error includes: + +pass:[•] an object of type `error_code` equal to any of `input_file_size_error`, `input_file_read_error`, `input_eof_error`, and + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), and + +pass:[•] an object of type `leaf::e_file_name`. + +<5> This handler will be called if the error includes: + +pass:[•] an object of type `error_code` equal to `cout_error`, and + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), + +<6> This handler will be called if the error includes an object of type `error_code` equal to `bad_command_line`. + +<7> This last handler matches any error: it prints diagnostic information to help debug logic errors in the program, since it failed to match an appropriate error handler to the error condition it encountered. In this program this handler will never be called, but it is required by `handle_all` because, well, it must handle all errors (the alternative is to use `handle_some` instead, which doesn't require a "catch all" last-resort handler; instead, if it fails to find a suitable handler for an error, it returns the error to its caller.) + +To conclude this introduction, let's look at one of the error-reporting functions that our `TryBlock` calls, for example `file_open`: + +[source,c++] +---- +leaf::result> file_open( char const * file_name ) +{ + if( FILE * f = fopen(file_name,"rb") ) + return std::shared_ptr(f,&fclose); + else + return leaf::new_error( input_file_open_error, leaf::e_errno{errno} ); +} +---- +[.text-right] +<> | <> | <> + +If `fopen` succeeds, it returns a `shared_ptr` which will automatically call `fclose` when needed. If it fails, we report an error by calling `new_error`, which takes any number of error objects to send with the error. In this case we pass the error code (`input_file_open_error`), as well as the system `errno` (LEAF defines `struct e_errno { int value; }`). + +NOTE: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[here]. The https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[other] version of the same program uses exception handling to report errors (see <>). ''' -[[tutorial-eh]] +[[introduction-eh]] === Using Exception Handling And now, we'll write the same program that reads a text file in a buffer and prints it to `std::cout`, this time using exceptions to report errors. @@ -518,243 +288,289 @@ struct input_eof_error : virtual input_file_error { }; NOTE: To avoid ambiguities in the dynamic type conversion which occurs when catching a base type, it is generally recommended to use virtual inheritance in exception type hierarchies. -Here is a function that reads data from a file into a buffer and throws exceptions to communicate failures: +We'll split the job into several functions, each communicating failures by throwing exceptions: [source,c++] ---- -void file_read( FILE & f, void * buf, int size ) -{ - int n = fread( buf, 1, size, &f ); +//Parse the command line, return the file name. +char const * parse_command_line( int argc, char const * argv[ ] ); - if( ferror(&f) ) - throw leaf::exception( input_file_read_error(), e_errno{errno} ); //<1> +//Open a file for reading. +std::shared_ptr file_open( char const * file_name ); - if( n!=size ) - throw input_eof_error(); //<2> -} +//Return the size of the file. +int file_size( FILE & f ); + +//Read size bytes from f into buf. +void file_read( FILE & f, void * buf, int size ); ---- -[.text-right] -`<>` -<1> If `ferror` indicates an error, we throw `input_file_read_error` and, because there is a relevant `errno` code, we pass that to the `<>` function template _also_ (LEAF defines `struct e_errno { int value; }`). -<2> If `fread` reports that it couldn't read all of the data requested, we throw `input_eof_error`. In this case there is no relevant `errno` to pass on, because this is not an error as far as `fread` is concerned. - -NOTE: `leaf::error` may be passed objects of arbitrary movable types, however objects of types that are not specific to error handling -- such as `errno` values, which are of type `int` -- should be wrapped in a `struct`. By convention, such structs use the `e_` prefix and contain a single data member called `value`, e.g. `struct e_errno { int value; }`. + - + -Similarly, we could define `struct e_input_name { std::string value; }` and `struct e_output_name { std::string value; }` and LEAF will treat them as separate entities even though their `.value` members are of the same type `std::string`. - -Now, let's consider a possible caller of `file_read`, called `print_file`: +The `main` function brings everything together and handles all the exceptions that are thrown, but instead of using `try`, it will use the function template `leaf::try_`, which has the following signature: [source,c++] ---- -void print_file( char const * file_name ) -{ - std::shared_ptr f = file_open( file_name ); //<1> - - auto propagate1 = leaf::preload( e_file_name{file_name} ); //<2> - - std::string buffer( 1+file_size(*f), '\0' ); //<3> - file_read( *f, &buffer[0], buffer.size()-1 ); - - auto propagate2 = leaf::defer( [ ] { return e_errno{errno}; } ); //<4> - std::cout << buffer; - std::cout.flush(); -} +template +<> try_( TryBlock && try_block, Handler && ... handler ); ---- -[.text-right] -<> | <> -<1> `std::shared_ptr file_open( char const * file_name)` throws on error. -<2> `<>` takes any number of error objects and prepares them to become associated (automatically, at the time the returned object expires) with an exception thrown in the future. The effect is that from this point on, a file name will be associated with any exception escaping `print_file`, in addition to everything else passed earlier to the `leaf::<>` function template explicitly (`e_file_name` is defined as `struct e_file_name { std::string value; }`). -<3> `int file_size( FILE & f )` throws on error. -<4> `<>` is similar to `preload`: it prepares an error object to become associated with an exception thrown in the future, but instead of taking the object itself, `defer` takes a function that returns it. The function is invoked in the returned object's destructor, at which point it becomes associated with the exception being propagated. Assuming `std::cout` is configured to throw on error, the effect of this line is that those exceptions will have the relevant `errno` associated with them. +`TryBlock` is a function type, almost always a lambda; `try_` simply returns the value returned by the `try_block`, catching any exception it throws. In that case `try_` calls a suitable error handling function from the `handler...` list. -Finally, let's consider the `main` function, which is able to handle exceptions thrown by `print_file`: +Let's look at the `TryBlock` our `main` function passes to `try_`: [source,c++] ---- int main( int argc, char const * argv[ ] ) { - //Configure std::cout to throw on error. - std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); + std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); <1> - //We expect e_file_name and e_errno objects to be associated with errors - //handled in this function. They will be stored inside of exp. - leaf::expect exp; + return leaf::try_( - try - { - print_file(parse_command_line(argc,argv)); - return 0; - } - catch( bad_command_line const & ) - { - std::cout << "Bad command line argument" << std::endl; - return 1; - } - catch( input_file_open_error const & e ) - { - //handle_exception takes a list of functions (in this case only one). It attempts to - //match each function (in order) to objects currently available in exp, which - //are associated with the error value stored in e. If no function can be matched, - //handle_exception returns false. Otherwise the matched function is invoked with - //the corresponding available error objects. - leaf::handle_exception( exp, e, + [&] <2> + { + char const * file_name = parse_command_line(argc,argv); <3> + std::shared_ptr f = file_open( file_name ); - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - if( errn.value==ENOENT ) - std::cerr << "File not found: " << fn.value << std::endl; - else - std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; - } + auto propagate1 = leaf::preload( leaf::e_file_name{file_name} ); <4> - ); - return 2; - } - catch( input_error const & e ) - { - //In this case handle_exception is given 3 functions. It will first check if both - //e_file_name and e_errno, associated with e, are avialable in exp; if not, it will - //next check if just e_errno is available; and if not, the last function (which - //takes no arguments) will always match to print a generic error message. - leaf::handle_exception( exp, e, + std::string buffer( 1+file_size(*f), '\0' ); + file_read(*f,&buffer[0],buffer.size()-1); - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Input error, " << fn.value << ", errno=" << errn << std::endl; - }, + auto propagate2 = leaf::defer([ ] { return leaf::e_errno{errno}; } ); <5> + std::cout << buffer; + std::cout.flush(); - [ ] ( e_errno const & errn ) - { - std::cerr << "Input error, errno=" << errn << std::endl; - }, + return 0; + }, - [ ] - { - std::cerr << "Input error" << std::endl; - } + .... <6> - ); - return 3; - } - catch( std::ostream::failure const & e ) - { - //Report failure to write to std::cout, print the relevant errno. - leaf::handle_exception( exp, e, - - [ ] ( e_errno const & errn ) - { - std::cerr << "Output error, errno=" << errn << std::endl; - } - - ); - return 4; - } - catch(...) - { - //This catch-all case helps diagnose logic errors (presumably, missing catch). - std::cerr << "Unknown error, cryptic information follows." << std::endl; - leaf::diagnostic_output_current_exception(std::cerr,exp); - return 5; - } + ); <7> } ---- - [.text-right] -`<>` | <> | <> +<> | <> | <> | <> -To summarize, when using LEAF with exception handling: +<1> Configure std::cout to throw on error. +<2> Except if it throws, our `TryBlock` returns `0`, which will be returned from `main` to the OS. +<3> If any of the functions we call throws, `try_` will find an appropriate handler to invoke. We'll look at that later. +<4> From now on, all exceptions escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name { std::string value; };` ). It's as if every time one of the following functions throws an exception, `preload` says "wait, put this `e_file_name` thing with the exception, it's important!" +<5> `defer` is similar to `preload`, but instead of an error object, it takes a function that returns it. From this point on, when an exception escapes this scope, `defer` will call the passed function in order to include the relevant `e_errno` with the exception (LEAF defines `struct e_errno { int value; };` ). +<6> List of error handler goes here. We'll see that later. +<7> This concludes the `try_` arguments -- as well as our program! -* In case a function detects a failure, it may throw instances of the `leaf::<>` class template, initializing it with any number of error objects, to associate with the exception any information it has that is relevant to the failure. -* Alternatively it may use `<>` to associate error objects with an exception object (of any type) thrown later on, including exceptions thrown by third-party code. -* In order for any error object passed to the `leaf::<>` function template to be stored rather than discarded, the function that catches the exception must contain an instance of the class template `<>` that provides the necessary storage for its type. -* Using `<>`, available error objects associated with the exception being handled can be matched to what is required in order to deal with that exception. +As it is always the case when using exception handling, as long as our `TryBlock` is exception-safe, we can concentrate on the "no errors" code path. Of course, our `TryBlock` is exception-safe, since `shared_ptr` will automatically close the file for us in case an exception is thrown. -NOTE: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[here]. There is also https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[another] version of the same program that does not use exception handling to report errors (see <>). +Now let's look at the second part of the call to `try_`, which lists the error handler: + +[source,c++] +---- +int main( int argc, char const * argv[ ] ) +{ + std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); <1> + + return leaf::try_( + [&] + { + .... <2> + }, + + [ ](leaf::catch_, <3> + leaf::match, + leaf::e_file_name const & fn) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + [ ](leaf::catch_, <4> + leaf::e_errno const & errn, + leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + [ ](leaf::catch_, <5> + leaf::e_errno const & errn, + leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; + return 3; + }, + + [ ](leaf::catch_, <6> + leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + [ ](leaf::catch_) <7> + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + [ ](leaf::error_info const & unmatched) <8> + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} +---- +[.text-right] +<> | <> | <> | <> + +<1> Configure std::cout to throw on error. + +<2> This is the `TryBlock` from the previous listing; if it throws, `try_` will catch the exception, then consider the error handler below, in order, and it will call the first one that can deal with the error: + +<3> This handler will be called if: + +pass:[•] an `input_file_open_error` exception was caught, with + +pass:[•] an object of type `leaf::e_errno` that has `.value` equal to `ENOENT`, and + +pass:[•] an object of type `leaf::e_file_name`. + +<4> This handler will be called if: + +pass:[•] an `input_file_open_error` exception was caught, with + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), and + +pass:[•] an object of type `leaf::e_file_name`. + +<5> This handler will be called if: + +pass:[•] an `input_error` exception was caught (which is a base type), with + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), and + +pass:[•] an object of type `leaf::e_file_name`. + +<6> This handler will be called if: + +pass:[•] an `std::ostream::failure` exception was caught, with + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), + +<7> This handler will be called if a `bad_command_line` exception was caught. + +<8> If `try_` fails to find an appropriate handler, it will re-throw the exception. But this is the `main` function which should handle all exceptions, so this last handler matches any error and prints diagnostic information, to help debug logic errors in the program. + +To conclude this introduction, let's look at one of the error-reporting functions that our `TryBlock` calls, for example `file_open`: + +[source,c++] +---- +std::shared_ptr file_open( char const * file_name ) +{ + if( FILE * f = fopen(file_name,"rb") ) + return std::shared_ptr(f,&fclose); + else + throw leaf::exception( input_file_open_error(), leaf::e_errno{errno} ); +} +---- +[.text-right] +<> | <> + +If `fopen` succeeds, it returns a `shared_ptr` which will automatically call `fclose` when needed. If it fails, we throw the exception object returned by `leaf::exception`, which takes as its first argument an exception object, followed by any number of error objects to send with it. In this case we pass the system `errno` (LEAF defines `struct e_errno { int value; }`). The returned object can be caught as `input_file_open_error`. + +NOTE: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[here]. The https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[other] version of the same program does not use exception handling to report errors (see the <>). [[synopsis]] == Synopsis -=== `expect.hpp` -include::synopses/expect.adoc[] - -''' - === `error.hpp` -include::synopses/error.adoc[] +include::{sourcedir}/synopses/error.adoc[] ''' === `preload.hpp` -include::synopses/preload.adoc[] - -''' - -=== `error_capture.hpp` -include::synopses/error_capture.adoc[] +include::{sourcedir}/synopses/preload.adoc[] ''' === `result.hpp` -include::synopses/result.adoc[] +include::{sourcedir}/synopses/result.adoc[] + +''' + +=== `handle.hpp` +include::{sourcedir}/synopses/handle.adoc[] + +''' + +=== `throw.hpp` +include::{sourcedir}/synopses/throw.adoc[] + +''' + +=== `try.hpp` +include::{sourcedir}/synopses/try.adoc[] ''' === `common.hpp` -include::synopses/common.adoc[] +include::{sourcedir}/synopses/common.adoc[] ''' -=== `exception.hpp` -include::synopses/exception.adoc[] +=== `capture_result.hpp` +include::{sourcedir}/synopses/capture_result.adoc[] ''' -=== `diagnostic_output_current_exception.hpp` -include::synopses/diagnostic_output_current_exception.adoc[] +=== `capture_exception.hpp` +include::{sourcedir}/synopses/capture_exception.adoc[] + +''' + +=== `exception_to_result.hpp` +include::{sourcedir}/synopses/exception_to_result.adoc[] [.text-right] `<>` -=== `exception_capture.hpp` -include::synopses/exception_capture.adoc[] - [[reference]] == Reference -[[wrapping]] -=== Wrapping of Error Types +[[e_types]] +=== E-Types -With LEAF, users can efficiently associate with errors or with exceptions any number of values that pertain to a failure. These values may be of any no-throw movable type for which the <> template is properly specialized. +==== Definition -However, when transporting objects of general types that are not specific to error handling, each value should be enclosed in a C-`struct` that acts as its compile-time identifier and gives it semantic meaning. Examples: +With LEAF, users can efficiently associate with errors or with exceptions any number of values that pertain to a failure. These values may be of any no-throw movable type `E` for which `<>::value` is `true`. The expectation is that this template will be specialized as needed for e.g. all user-defined error enums. + +Throughout this text, types for which `is_e_type` is `true` are called E-types. Objects of those types are called E-objects or E-values. + +The main `is_e_type` template is defined so that `is_e_type::value` is `true` when `E` is: + +* `std::error_code`, +* `boost::system::error_code`, +* `std::exception_ptr`, +* any type for which `std::is_base_of::value` is `true`, +* any type which defines an accessible data member `value`. + +Usually, error values that need to be communicated are of generic types (e.g. `std::string`). Such values should be enclosed in a C-`struct` that acts as their compile-time identifier and gives them semantic meaning. Examples: [source,c++] ---- struct e_input_name { std::string value; }; struct e_output_name { std::string value; }; -struct e_minimum_temperature { int value; }; -struct e_maximum_temperature { int value; }; +struct e_minimum_temperature { float value; }; +struct e_maximum_temperature { float value; }; ---- -Various LEAF functions take a list of error objects to associate with an `<>` value. For example, to indicate an error, a function that returns a `<>` may use something like: +Various LEAF functions take a list of E-objects to associate with an `<>`. For example, if a `copy_file` function that takes the name of the input file and the name of the output file as its arguments detects a failure, it would communicate an error code `ec`, plus the two relevant file names: [source,c++] ---- -return leaf::error( my_error_code, e_input_name{n1}, e_output_name{n2} ); +return leaf::new_error( ec, e_input_name{n1}, e_output_name{n2} ); ---- -Similar types that define an accessible data member `value` can be used with LEAF without having to specialize the `is_error_type` template. By convention such types use the `e_` prefix. +By convention, the enclosing C-`struct` names use the `e_` prefix. -''' +==== Diagnostic Information -=== Diagnostic Information +LEAF is able to automatically generate diagnostic messages that include information about all E-objects available in an error-handling scope. For this purpose, it needs to be able to print objects of user-defined E-types. -LEAF will attempt to print error objects in various `diagnostic_output` overloads it defines. It will first attempt to use `operator<<` overload that takes the actual error object type (e.g. the enclosing `struct`, see <>). If such overload does not exist, the fallback is to attempt to use `operator<<` overload that takes the `.value` (in case the error object type defines it). If that also doesn't work, LEAF is unable to print values of that particular error type (this is permissible, not an error). +First, LEAF attempts to bind an unqualified call to `operator<<`, passing a `std::ostream` and the E-object. If that fails, it will also attempt to bind `operator<<` that takes the `.value` of the E-object. If that also doesn't compile, the E-object will not appear in diagnostic messages, though LEAF will still print its type. -Even with types that define a printable `.value`, the user may still want to overload `operator<<` for the enclosing `struct`, e.g.: +Even with E-types that define a printable `.value`, the user may still want to overload `operator<<` for the enclosing `struct`, e.g.: [source,c++] ---- @@ -769,29 +585,288 @@ struct e_errno }; ---- -The `e_errno` type above is designed to hold `errno` values. The defined `operator<<` overload will automatically include the output from `strerror` when `e_errno` values are printed by `diagnostic_output` overloads (LEAF defines `e_errno` in ``, together with other commonly-used error types). +The `e_errno` type above is designed to hold `errno` values. The defined `operator<<` overload will automatically include the output from `strerror` when `e_errno` values are printed (LEAF defines `e_errno` in ``, together with other commonly-used error types). -TIP: The output from `diagnostic_output` overloads is developer-friendly but not user-friendly. Therefore, `operator<<` overloads for error types should only print technical information in English, and should not attempt to localize strings or to format a message. Formatting a localized user-friendly message should be done at the time individual errors are handled. +TIP: This automatically-generated diagnostic messages are developer-friendly, but not user-friendly. Therefore, `operator<<` overloads for E-types should only print technical information in English, and should not attempt to localize strings or to format a user-friendly message; this should be done in error-handling functions specifically designed for that purpose. ''' -[[expect]] -=== Class Template `expect` +[[handle_some]] +=== `handle_some` -include::synopses/expect.adoc[] +[source,c++] +---- +namespace boost { namespace leaf { -All `expect` objects must use automatic storage duration. They are not copyable and are not movable. + template + decltype(std::declval()()) + handle_some( TryBlock && try_block, Handler && ... handler ); -An `expect` object contains exactly `sizeof...(E)` _slots_, each slot providing storage for a single object of the corresponding type `E`. It is invalid to specify the same type more than once in `E...`; so, each type `E` uniquely identifies an `expect` slot. All slots are initially empty. +} } +---- -Slots of the same type `E` across different `expect` objects (that belong to the calling thread) form a stack. The slot created last for a given type `E` is at the top of that stack. When an object is passed to the `leaf::<>` constructor, it is moved into the corresponding slot on the top of that stack, and is associated with that `leaf::error` value. If no `expect` objects contain a corresponding slot, the object passed to the `leaf::error` constructor is discarded (however, see <>). +When errors are communicated by `<>`, the `handle_some` function template can be used to recognize and handle some errors, forwarding any unrecognized errors to the caller. -An object stored in an `expect` slot can be accessed in several different ways, all requiring the `leaf::error` value it was associated with. While an `expect` object can not store multiple values of the same type, this association guarantees that the object being accessed pertains to that specific `error` value (`error` values act as program-wide unique failure identifiers). +Requirements: :: -When an `expect` object is destroyed, each stored object is moved to the corresponding slot one level below the top of the stack formed by the slots of the same type across different `expect` objects. If that stack is empty, the object is discarded. +* The `try_block` must be a function callable with no arguments that returns an instance of the <> class template. +* All `handler...` arguments must be functions that return the same type as the `try_block`, or types that convert implicitly to it (e.g. `<>`); + +* Each of the `handler...` functions: +** may take arguments of <>, either by value or by `const &`, or as a `const *`; +** may take arguments of the predicate type `<>`, where `E` is an E-type, either by value or by `const &`; +** may take an <> argument by `const &`; +** may take an <> argument by `const &`; +** may take a <> argument by `const &`; +** may not take any other types of arguments. + +Effects: :: + +When `handle_some` is invoked, LEAF reserves space inside of the `handle_some` scope (using automatic storage duration) to store the E-objects it would pass to the appropriate `handler` in case the `try_block` indicates an error. ++ +The list of E-types that `handle_some` needs to store internally is deduced automatically, using the following steps, in order: ++ +-- +* A type list is deduced by concatenating all argument types of all `handler...` functions; +* References and cv-qualifiers are stripped from each type in the list; +* The <>, <>, and <> types are removed from the list; +* Any duplicate types are removed. +-- ++ +Next, `handle_some` calls the `try_block` function, which generally would call other functions as needed. When any function needs to report an error, it passes one or more E-objects to LEAF. For example, a function that returns a `<>` can report an error with a call to <> in a `return` expression: ++ +[source,c++] +---- +if( file-open-fails ) + return leaf::new_error( error_code::file_open_error, e_file_name{fn} ); +---- ++ +As each E-type object is passed to LEAF, it is immediately moved to the `handle_some` (or <>, or <>) scope highest in the call stack that has available storage for that type, and is associated with the specific <> value returned by `new_error`. Objects of E-types for which none of these scopes have available storage are discarded. + +Return Value: :: +If the `<>` object `r` returned by the `try_block` function indicates success, `handle_some` returns `r`. ++ +Otherwise, `handle_some` considers each of the `handler...` functions, in order, until it finds one that matches the reported error. The first matching handler is invoked and the `result` it returns is forwarded to the caller of `handle_some`; if no match is found, `handle_some` returns `r`. ++ +If the value `handle_some` is returning indicates success, all stored E-objects are discarded. Otherwise, each stored E-object is moved to appropriate storage available in `handle_some` (or <>, or <>) scopes higher up the call stack (each moved object retains its association with its specific `error` value). E-objects for which such storage is not available are discarded. + +Handler Matching Procedure: :: ++ +A `handler` matches the reported failure iff `handle_some` is able to produce values to pass as its arguments. As soon as it is determined that an argument value can not be produced, the current handler is dropped and the matching procedure continues with the next handler, if any. ++ +Each argument value `a`~i~ to be passed to the handler currently under consideration is produced as follows: ++ +-- +* If `a`~i~ is of type `error_info const &` or `unexpected_error_info const &` or `verbose_diagnostic_info const &`, `handle_some` is always able to produce it. +* If `a`~i~ is of type `A`~i~ `const &`: +** If an E-object of type `A`~i~ is currently stored in the `handle_some` scope *and* is associated with the specific `<>` value stored in `r`, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. +** If `A`~i~ is of the predicate type `<>`, if an object of type `E` is currently stored in the `handle_some` scope *and* is associated with the specific `error` value stored in `r`, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. The handler is also dropped if the expression `a`~i~`()` evaluates to `false`. +* If `a`~i~ is of type `A`~i~ `const pass:[*]`, `handle_some` is always able to produce it: if an E-object of type `A`~i~ is currently stored in the `handle_some` scope *and* is associated with the specific `<>` value stored in `r`, `a`~i~ is initialized with the address of the stored object, otherwise it is initialized with `0`. +* It is illegal to pass to `handle_some` handlers that take any other argument types. +-- + +TIP: Because `handle_some` can always produce arguments of type `error_info const &`, `unexpected_error_info const &` and `verbose_diagnostic_info const &`, if a handler only takes arguments of these types, it will match any error. ''' +[[handle_all]] +=== `handle_all` + +[source,c++] +---- +namespace boost { namespace leaf { + + template + typename std::remove_reference()().value())>::type + handle_all( TryBlock && try_block, Handler && ... handler ); + +} } +---- + +Requirements: :: + +* The `try_block` must be a function callable with no arguments that returns a `<>`; +* All `handler...` arguments must be functions that return T (or types that convert implicitly to `T`); +* Each of the `handler...` functions: +** may take any number of arguments of <>, either by value or by `const &`; +** may also take, by `const &`, objects of type <> or <>; +** may not take any other types of arguments. +* At least one `handler` function (usually the last) must match any error. + +Effects: :: + +The `handle_all` function, like <>, reserves space in its scope for E-objects, then calls the `try_block` function which returns a `result` object `r`. The differences are: + +* Upon success, `handle_all` returns `r.<>()`, whereas `handle_some` returns r; +* Upon failure, `handle_all` is guaranteed to find a suitable `handler`, returning whatever value it returns (which is usually of type `T`; if not, it must be implicitly convertible to `T`). + +''' + +[[try_]] +=== `try_` + +[source,c++] +---- +namespace boost { namespace leaf { + + template + decltype(std::declval()()) + try_( TryBlock && try_block, Handler && ... handler ); + +} } +---- + +Requirements: :: + +* The `try_block` must be a function, callable with no arguments; +* All `handler...` arguments must be functions that return the same type as the `try_block` (or types that convert implicitly to it); +* Each of the `handler...` functions: +** may take any number of arguments of <>, either by value or by `const &`; +** may also take, by `const &`, objects of type <> or <>; +** may not take any other types of arguments. + +Effects: :: + +The `try_` function, like <>, reserves space in its scope for E-objects, then calls the `try_block` function. + +However, `try_` assumes that any failures are communicated by throwing exceptions. In case the `try_block` throws, + +* Upon success, `handle_all` returns `r.<>()`, whereas `handle_some` returns r; +* Upon failure, `handle_all` is guaranteed to find a suitable `handler`, returning whatever value it returns (which is usually of type `T`; if not, it must be implicitly convertible to `T`). + +''' + +[[match]] +=== `match` + +[source,c++] +---- +namespace boost { namespace leaf { + + template ::type... V> + struct match + { + using type = typename deduce-type::type; + type const & value; + + explicit match( E const & e ) noexcept; + + bool operator()() const noexcept; + }; + +} } +---- + +NOTE: The `match` template is useful only as argument to a handler function passed to <> (or `handle_all` or `try_`). + +Effects: :: + +* If `E` defines an accessible data member `value`: +** The type of the parameter pack `V...` is deduced as `decltype(std::declval().value)`; +** The `match` constructor initializes the `value` reference with `e.value`. + +* Otherwise: +** The type of the parameter pack `V...` is deduced as `E`; +** The `match` constructor initializes the `value` reference with `e`. + +The `match` template is a predicate function type: `operator()` returns `true` iff the expression `value == V~i~` is `true` for at least one of `V...` values. + +.Example 1: +[source,c++] +---- +struct error_code { int value; }; + +error_code e = {42}; + +match m1(e); +assert(!m1()); + +match m2(e); +assert(m2()); + +match m3(e); +assert(m3()); + +match m4(e); +assert(!m4()); +---- + +.Example 2: +[source,c++] +---- +enum error_code { e1=1, e2, e3 }; + +error_code e = e2; + +match m1(e); +assert(!m1()); + +match m2(e); +assert(m2()); + +match m3(e); +assert(m3()); + +match m4(e); +assert(!m4()); +---- + +''' + +[[catch_]] +=== `catch_` + +[source,c++] +---- +namespace boost { namespace leaf { + + template + struct catch_ + { + std::exception const & value; + + explicit catch_( std::exception const & ex ) noexcept; + + bool operator()() const noexcept; + }; + +} } +---- + +NOTE: The `catch_` template is useful only as argument to a handler function passed to <>. + +Effects: :: + +The `catch_` constructor initializes `value` with `ex`. + +The `catch_` template is a predicate function type: `operator()` returns `true` iff the expression `dynamic_cast(&value) != 0` is `true` for at least one of the `Ex...` types. + +.Example: +[source,c++] +---- +struct exception1: std::exception { }; +struct exception2: std::exception { }; +struct exception3: std::exception { }; + +exception2 x; + +catch_ c1(x); +assert(!c1()); + +catch_ c2(x); +assert(c2()); + +catch_ c3(x); +assert(c2()); + +catch_ c4(x); +assert(!c4()); +---- + +''' + + + [[expect::expect]] ==== Constructor @@ -954,12 +1029,12 @@ namespace boost { namespace leaf { Effects: :: Prints diagnostic information about the objects stored in `exp`. The second overload will only print diagnostic information about objects stored in `exp` which are associated with the `leaf::<>` value `e`. -NOTE: The printing of each individual object is done by the rules described <>. +NOTE: The printing of each individual object is done by the rules described <>. ''' [[error]] -=== Class `error` +=== `error` include::synopses/error.adoc[] @@ -983,7 +1058,7 @@ namespace boost { namespace leaf { } } ---- -Requirements: :: `<>::value` must be `true` for each `E`. +Requirements: :: `<>::value` must be `true` for each `E`. Effects: :: Each of the `e...` objects is either moved into the corresponding storage provided by `expect` instances (where it is associated with `*this`), or discarded. See `<>`. @@ -1102,8 +1177,8 @@ IMPORTANT: `error` values are unique across the entire program. ''' -[[is_error_type]] -==== `is_error_type` +[[is_e_type]] +==== `is_e_type` .#include [source,c++] @@ -1111,7 +1186,7 @@ IMPORTANT: `error` values are unique across the entire program. namespace boost { namespace leaf { template - struct is_error_type + struct is_e_type { static constexpr bool value = <>; }; @@ -1119,7 +1194,7 @@ namespace boost { namespace leaf { } } ---- -The `is_error_type` template should be specialized for each user-defined type which the user desires to be used as an error type within LEAF, for example: +The `is_e_type` template should be specialized for each user-defined type which the user desires to be used as an error type within LEAF, for example: [source,c++] ---- @@ -1132,7 +1207,7 @@ enum my_error namespace boost { namespace leaf { - template<> struct is_error_type: std::true_type { }; + template<> struct is_e_type: std::true_type { }; } } ---- @@ -1141,7 +1216,7 @@ This requirement is designed to trigger a diagnostic in case the user unintentio [TIP] -- -The `is_error_type` template needs not be specialized for: +The `is_e_type` template needs not be specialized for: * types that define an accessible data member `value`, @@ -1174,7 +1249,7 @@ namespace boost { namespace leaf { } } ---- -Requirements: :: `<>::value` must be `true` for each `E`. +Requirements: :: `<>::value` must be `true` for each `E`. Effects: :: All `e...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: * If a new `leaf::<>` value was created (in the calling thread) since the object returned by `preload` was created, the stored `e...` objects are propagated and become associated with the _last_ such `leaf::error` value; @@ -1197,7 +1272,7 @@ namespace boost { namespace leaf { } } ---- -Requirements: :: Each of the `f~i~` objects must be a function that does not throw exceptions, takes no arguments and returns an object of a no-throw movable type `E~i~` for which `<>::value` is `true`. +Requirements: :: Each of the `f~i~` objects must be a function that does not throw exceptions, takes no arguments and returns an object of a no-throw movable type `E~i~` for which `<>::value` is `true`. Effects: :: All `f...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: * If a new `leaf::<>` value was created (in the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and the returned object is propagated and becomes associated with the _last_ such `leaf::error` value; @@ -1207,7 +1282,7 @@ Effects: :: All `f...` objects are forwarded and stored into the returned object ''' [[error_capture]] -=== Class `error_capture` +=== `error_capture` include::synopses/error_capture.adoc[] @@ -1386,12 +1461,12 @@ namespace boost { namespace leaf { Effects: :: Prints diagnostic information about the objects stored in `ec`. -NOTE: The printing of each individual object is done by the rules described <>. +NOTE: The printing of each individual object is done by the rules described <>. ''' [[result]] -=== Class Template `result` +=== `result` include::synopses/result.adoc[] @@ -1888,7 +1963,7 @@ namespace boost { namespace leaf { Requirements: :: * `Ex` must derive from `std::exception`. -* For all of E, `<>::value` is `true`. +* For all of E, `<>::value` is `true`. Returns: :: An object of unspecified type which derives publicly from `Ex` *and* from class `<>` such that: * its `Ex` subobject is initialized by `std::forward(ex)`; diff --git a/doc/synopses/capture_exception.adoc b/doc/synopses/capture_exception.adoc new file mode 100644 index 0000000..6338c74 --- /dev/null +++ b/doc/synopses/capture_exception.adoc @@ -0,0 +1,14 @@ +[source,c++] +.#include +---- +namespace boost { namespace leaf { + + template + <> capture_exception( F && f ) noexcept; + +} } +---- + +[.text-right] +<> + diff --git a/doc/synopses/capture_result.adoc b/doc/synopses/capture_result.adoc new file mode 100644 index 0000000..947384c --- /dev/null +++ b/doc/synopses/capture_result.adoc @@ -0,0 +1,14 @@ +[source,c++] +.#include +---- +namespace boost { namespace leaf { + + template + <> capture_result( F && f ) noexcept; + +} } +---- + +[.text-right] +<> + diff --git a/doc/synopses/common.adoc b/doc/synopses/common.adoc index 15ded96..cce5ffd 100644 --- a/doc/synopses/common.adoc +++ b/doc/synopses/common.adoc @@ -42,13 +42,6 @@ namespace boost { namespace leaf { friend std::ostream & operator<<( std::ostream & os, e_unexpected const & x ); }; - struct e_unexpected_diagnostic_output - { - <> - - friend std::ostream & operator<<( std::ostream & os, e_unexpected_diagnostic_output const & x ); - }; - } } ---- diff --git a/doc/synopses/diagnostic_output_current_exception.adoc b/doc/synopses/diagnostic_output_current_exception.adoc deleted file mode 100644 index 067cd2d..0000000 --- a/doc/synopses/diagnostic_output_current_exception.adoc +++ /dev/null @@ -1,11 +0,0 @@ -.#include -[source,c++] ----- - -namespace boost { namespace leaf { - - template - void diagnostic_output_current_exception( std::ostream & os, expect const & exp ); - -} } ----- diff --git a/doc/synopses/error.adoc b/doc/synopses/error.adoc index e0ef34b..dbd50e5 100644 --- a/doc/synopses/error.adoc +++ b/doc/synopses/error.adoc @@ -7,9 +7,6 @@ namespace boost { namespace leaf { { public: - template - explicit error( E && ... e ) noexcept: - template error propagate( E && ... e ) const noexcept; @@ -19,12 +16,15 @@ namespace boost { namespace leaf { friend std::ostream & operator<<( std::ostream & os, error const & e ) }; + template + error new_error( E && ... e ) noexcept; + error next_error_value() noexcept; error last_error_value() noexcept; template - struct is_error_type + struct is_e_type { static constexpr bool value = <>; }; @@ -33,4 +33,4 @@ namespace boost { namespace leaf { ---- [.text-right] -`<>` | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/error_capture.adoc b/doc/synopses/error_capture.adoc deleted file mode 100644 index 713468b..0000000 --- a/doc/synopses/error_capture.adoc +++ /dev/null @@ -1,30 +0,0 @@ -.#include -[source,c++] ----- - -namespace boost { namespace leaf { - - class error_capture - { - public: - - error_capture() noexcept; - - explicit operator bool() const noexcept; - - error unload() noexcept; - }; - - template - <> handle_error( error_capture const & ec, F && ... f ) noexcept; - - template - P const * peek( error_capture const & ec ) noexcept; - - void diagnostic_output( std::ostream & os, error_capture const & ec ); - -} } ----- - -[.text-right] -`<>` | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/exception.adoc b/doc/synopses/exception.adoc deleted file mode 100644 index 3f3a06e..0000000 --- a/doc/synopses/exception.adoc +++ /dev/null @@ -1,28 +0,0 @@ -[source,c++] -.#include ----- -namespace boost { namespace leaf { - - template - <> exception( Ex && ex, E... && e ) noexcept; - - error get_error( std::exception const & ex ) noexcept; - - template - P const * peek( expect const & exp, std::exception const & ex ) noexcept; - - template - void handle_exception( expect & exp, std::exception const & ex, F && ... f ); - - template - void diagnostic_output( std::ostream & os, expect const & exp, std::exception const & ex ); - -} } - -#define LEAF_EXCEPTION(...) <> - -#define LEAF_THROW(...) throw LEAF_EXCEPTION(__VA_ARGS__) ----- - -[.text-right] -<> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/exception_capture.adoc b/doc/synopses/exception_capture.adoc deleted file mode 100644 index 48b92de..0000000 --- a/doc/synopses/exception_capture.adoc +++ /dev/null @@ -1,17 +0,0 @@ -[source,c++] -.#include ----- -namespace boost { namespace leaf { - - template - <> capture_exception( F && f ) noexcept; - - template - decltype(std::declval().get()) get( Future && f ); - -} } ----- - -[.text-right] -<> | <> - diff --git a/doc/synopses/exception_to_result.adoc b/doc/synopses/exception_to_result.adoc new file mode 100644 index 0000000..227a70c --- /dev/null +++ b/doc/synopses/exception_to_result.adoc @@ -0,0 +1,14 @@ +[source,c++] +.#include +---- +namespace boost { namespace leaf { + + template + leaf::result exception_to_result( F && f ) noexcept; + +} } +---- + +[.text-right] +<> + diff --git a/doc/synopses/expect.adoc b/doc/synopses/expect.adoc deleted file mode 100644 index cee0162..0000000 --- a/doc/synopses/expect.adoc +++ /dev/null @@ -1,40 +0,0 @@ -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - class expect - { - public: - - expect() noexcept; - ~expect() noexcept; - }; - - template - error_capture capture( expect & exp, error const & e ); - - template - struct uhnandled_error - { - static constexpr R value( error const & ) noexcept; - }; - - template - <> handle_error( expect const & exp, error const & e, F && ... f ) noexcept; - - template - P const * peek( expect const & exp, error const & e ) noexcept; - - template - void diagnostic_output( std::ostream & os, expect const & exp ); - - template - void diagnostic_output( std::ostream & os, expect const & exp, error const & e ); - -} } ----- - -[.text-right] -<> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/handle.adoc b/doc/synopses/handle.adoc new file mode 100644 index 0000000..8d03a8f --- /dev/null +++ b/doc/synopses/handle.adoc @@ -0,0 +1,18 @@ +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + typename std::remove_reference()().value())>::type + handle_all( TryBlock && try_block, Handler && ... handler ); + + template + decltype(std::declval()()) + handle_some( TryBlock && try_block, Handler && ... handler ); + +} } +---- + +[.text-right] +`<>` | <> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/throw.adoc b/doc/synopses/throw.adoc new file mode 100644 index 0000000..29be587 --- /dev/null +++ b/doc/synopses/throw.adoc @@ -0,0 +1,17 @@ +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + <> exception( Ex && ex, E && ... e ) noexcept; + +} } + +#define LEAF_EXCEPTION(...) <> + +#define LEAF_THROW(...) throw <> +---- + +[.text-right] +<> | <> | <> diff --git a/doc/synopses/try.adoc b/doc/synopses/try.adoc new file mode 100644 index 0000000..fccabfa --- /dev/null +++ b/doc/synopses/try.adoc @@ -0,0 +1,15 @@ +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + error get_error( std::exception const & ex ) noexcept; + + template + decltype(std::declval()()) try_( TryBlock && try_block, Handler && ... handler ); + +} } +---- + +[.text-right] +<> diff --git a/example/capture_eh.cpp b/example/capture_eh.cpp index fcb52ba..0af5169 100644 --- a/example/capture_eh.cpp +++ b/example/capture_eh.cpp @@ -1,11 +1,11 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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 is a simple program that demonstrates the use of LEAF to transport e-objects between threads, -//without using exception handling. See capture_eh.cpp for the exception-handling variant. +// This is a simple program that demonstrates the use of LEAF to transport e-objects between threads, +// without using exception handling. See capture_eh.cpp for the exception-handling variant. #include #include @@ -19,15 +19,15 @@ namespace leaf = boost::leaf; -//Define several e-types. +// Define several e-types. struct e_thread_id { std::thread::id value; }; struct e_failure_info1 { std::string value; }; struct e_failure_info2 { int value; }; -//A type that represents a successfully returned result from a task. +// A type that represents a successfully returned result from a task. struct task_result { }; - //This is our task function. It produces objects of type task_result, but it may fail... + // This is our task function. It produces objects of type task_result, but it may fail... task_result task() { bool succeed = (rand()%4) !=0; //...at random. @@ -44,12 +44,12 @@ int main() { int const task_count = 42; - //Container to collect the generated std::future objects. + // Container to collect the generated std::future objects. std::vector> fut; - //Launch the tasks, but rather than launching the task function directly, we launch the - //wrapper function returned by leaf::capture_result. It captures the specified error object - //types and automatically transports them in the leaf::result it returns. + // Launch the tasks, but rather than launching the task function directly, we launch the + // wrapper function returned by leaf::capture_result. It captures the specified error object + // types and automatically transports them in the leaf::result it returns. std::generate_n( std::inserter(fut,fut.end()), task_count, [ ] { @@ -58,7 +58,7 @@ int main() leaf::capture_exception(task) ); } ); - //Wait on the futures, get the task results, handle errors. + // Wait on the futures, get the task results, handle errors. for( auto & f : fut ) { f.wait(); @@ -68,7 +68,7 @@ int main() { task_result r = f.get(); - //Success! Use r to access task_result. + // Success! Use r to access task_result. std::cout << "Success!" << std::endl; (void) r; @@ -79,12 +79,12 @@ int main() std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; }, - [ ]( leaf::error_info const & ei ) + [ ]( leaf::error_info const & unmatched ) { std::cerr << "Unknown failure detected" << std::endl << "Cryptic diagnostic information follows" << std::endl << - ei; + unmatched; } ); } } diff --git a/example/capture_result.cpp b/example/capture_result.cpp index e1ec86e..4b96c33 100644 --- a/example/capture_result.cpp +++ b/example/capture_result.cpp @@ -1,11 +1,11 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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 is a simple program that demonstrates the use of LEAF to transport e-objects between threads, -//without using exception handling. See capture_eh.cpp for the exception-handling variant. +// This is a simple program that demonstrates the use of LEAF to transport e-objects between threads, +// without using exception handling. See capture_eh.cpp for the exception-handling variant. #include #include @@ -19,15 +19,15 @@ namespace leaf = boost::leaf; -//Define several e-types. +// Define several e-types. struct e_thread_id { std::thread::id value; }; struct e_failure_info1 { std::string value; }; struct e_failure_info2 { int value; }; -//A type that represents a successfully returned result from a task. +// A type that represents a successfully returned result from a task. struct task_result { }; - //This is our task function. It produces objects of type task_result, but it may fail... + // This is our task function. It produces objects of type task_result, but it may fail... leaf::result task() { bool succeed = (rand()%4) !=0; //...at random. @@ -44,12 +44,12 @@ int main() { int const task_count = 42; - //Container to collect the generated std::future objects. + // Container to collect the generated std::future objects. std::vector>> fut; - //Launch the tasks, but rather than launching the task function directly, we launch the - //wrapper function returned by leaf::capture_result. It captures the specified error object - //types and automatically transports them in the leaf::result it returns. + // Launch the tasks, but rather than launching the task function directly, we launch the + // wrapper function returned by leaf::capture_result. It captures the specified error object + // types and automatically transports them in the leaf::result it returns. std::generate_n( std::inserter(fut,fut.end()), task_count, [ ] { @@ -58,7 +58,7 @@ int main() leaf::capture_result(task) ); } ); - //Wait on the futures, get the task results, handle errors. + // Wait on the futures, get the task results, handle errors. for( auto & f : fut ) { f.wait(); @@ -68,7 +68,7 @@ int main() { LEAF_AUTO(r,f.get()); - //Success! Use r to access task_result. + // Success! Use r to access task_result. std::cout << "Success!" << std::endl; (void) r; return { }; @@ -79,12 +79,12 @@ int main() std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; }, - [ ]( leaf::error_info const & ei ) + [ ]( leaf::error_info const & unmatched ) { std::cerr << "Unknown failure detected" << std::endl << "Cryptic diagnostic information follows" << std::endl << - ei; + unmatched; } ); } } diff --git a/example/exception_to_result.cpp b/example/exception_to_result.cpp index 9aa0866..83f8a3b 100644 --- a/example/exception_to_result.cpp +++ b/example/exception_to_result.cpp @@ -1,16 +1,16 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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. +// 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<>. +// 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 @@ -24,7 +24,7 @@ class error_b: public virtual error_base { }; class error_c: public virtual error_base { }; -//Lower-level library function which throws exceptions. +// Lower-level library function which throws exceptions. int compute_answer_throws() { switch( rand()%4 ) @@ -37,11 +37,11 @@ int compute_answer_throws() } -//Calls compute_answer_throws, switches to result for error handling. +// Calls compute_answer_throws, switches to result for error handling. leaf::result 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. + // 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( [ ] { @@ -50,7 +50,7 @@ leaf::result compute_answer() noexcept } -//Print the answer if the call to compute_answer is successful. +// Print the answer if the call to compute_answer is successful. leaf::result print_answer() noexcept { LEAF_AUTO( r, compute_answer()); @@ -61,8 +61,8 @@ leaf::result print_answer() noexcept 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... + // 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::handle_all( @@ -83,27 +83,27 @@ int main() }, //...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. + // 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 + // 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. + // would not compile. if( ep ) leaf::try_( [&] { std::rethrow_exception(*ep); }, - [ ]( leaf::error_info const & ei ) + [ ]( leaf::error_info const & unmatched ) { - std::cerr << ei << std::endl; + std::cerr << unmatched << std::endl; } ); } ); } diff --git a/example/lua_callback_eh.cpp b/example/lua_callback_eh.cpp index 2e18ee9..bc73e93 100644 --- a/example/lua_callback_eh.cpp +++ b/example/lua_callback_eh.cpp @@ -1,12 +1,12 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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 is a simple program that shows how to propagate leaf::error objects out -//of a C-callback, and converting them to leaf::result as soon as control -//reaches C++. +// This is a simple program that shows how to propagate leaf::error objects out +// of a C-callback, and converting them to leaf::result as soon as control +// reaches C++. extern "C" { #include "lua.h" @@ -26,30 +26,30 @@ enum do_work_error_code ec2 }; namespace boost { namespace leaf { - template<> struct is_error_type: std::true_type { }; + template<> struct is_e_type: std::true_type { }; } } struct e_lua_pcall_error { int value; }; struct e_lua_error_message { std::string value; }; -//This is a C callback function with a specific signature, made accessible to programs -//written in Lua. +// This is a C callback function with a specific signature, made accessible to programs +// written in Lua. -//If it succeeds, it returns an int answer, by pushing it onto the Lua stack. But "sometimes" -//it fails, in which case it calls luaL_error. This causes the Lua interpreter to abort and pop -//back into the C++ code which called it (see call_lua below). +// If it succeeds, it returns an int answer, by pushing it onto the Lua stack. But "sometimes" +// it fails, in which case it calls luaL_error. This causes the Lua interpreter to abort and pop +// back into the C++ code which called it (see call_lua below). int do_work( lua_State * L ) { bool success=rand()%2; if( success ) { - lua_pushnumber(L,42); //Return 42 to the calling Lua program. + lua_pushnumber(L,42); // Return 42 to the calling Lua program. return 1; } else { - //Remarkably, the Lua interpreter is exception-safe. So, just throw. + // Remarkably, the Lua interpreter is exception-safe. So, just throw. throw leaf::exception(lua_failure(),ec1); } } @@ -57,16 +57,16 @@ int do_work( lua_State * L ) std::shared_ptr init_lua_state() { - //Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. + // Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. std::shared_ptr L(lua_open(),&lua_close); - //Register the do_work function (above) as a C callback, under the global - //Lua name "do_work". With this, calls from Lua programs to do_work - //will land in the do_work C function we've registered. + // Register the do_work function (above) as a C callback, under the global + // Lua name "do_work". With this, calls from Lua programs to do_work + // will land in the do_work C function we've registered. lua_register( &*L, "do_work", &do_work ); - //Pass some Lua code as a C string literal to Lua. This creates a global Lua - //function called "call_do_work", which we will later ask Lua to execute. + // Pass some Lua code as a C string literal to Lua. This creates a global Lua + // function called "call_do_work", which we will later ask Lua to execute. luaL_dostring( &*L, "\ \n function call_do_work()\ \n return do_work()\ @@ -76,27 +76,27 @@ std::shared_ptr init_lua_state() } -//Here we will ask Lua to execute the function call_do_work, which is written -//in Lua, and returns the value from do_work, which is written in C++ and -//registered with the Lua interpreter as a C callback. +// Here we will ask Lua to execute the function call_do_work, which is written +// in Lua, and returns the value from do_work, which is written in C++ and +// registered with the Lua interpreter as a C callback. -//If do_work succeeds, we return the resulting int answer. If it fails, we'll -//communicate that failure to our caller. +// If do_work succeeds, we return the resulting int answer. If it fails, we'll +// communicate that failure to our caller. int call_lua( lua_State * L ) { - //Ask the Lua interpreter to call the global Lua function call_do_work. + // Ask the Lua interpreter to call the global Lua function call_do_work. lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); if( int err=lua_pcall(L,0,1,0) ) { - //Something went wrong with the call, so we'll throw lua_failure. - //This is definitely not a do_work failure, because it throws on error. + // Something went wrong with the call, so we'll throw lua_failure. + // This is definitely not a do_work failure, because it throws on error. auto propagate = leaf::preload( e_lua_error_message{lua_tostring(L,1)} ); lua_pop(L,1); throw leaf::exception( lua_failure(), e_lua_pcall_error{err} ); } else { - //Success! Just return the int answer. + // Success! Just return the int answer. int answer=lua_tonumber(L,-1); lua_pop(L,1); return answer; @@ -117,13 +117,13 @@ int main() noexcept { handle_exception( exp, e, - //Handle do_work failures: + // Handle do_work failures: [ ]( do_work_error_code const & e ) { std::cout << "Got do_work_error_code = " << e << "!\n"; }, - //Handle all other lua_pcall failures: + // Handle all other lua_pcall failures: [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) { std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index 98758be..050df98 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -1,12 +1,12 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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 is a simple program that shows how to propagate leaf::error objects out -//of a C-callback, and converting them to leaf::result as soon as control -//reaches C++. +// This is a simple program that shows how to propagate leaf::error objects out +// of a C-callback, and converting them to leaf::result as soon as control +// reaches C++. extern "C" { #include "lua.h" @@ -24,31 +24,31 @@ enum do_work_error_code ec2 }; namespace boost { namespace leaf { - template<> struct is_error_type: std::true_type { }; + template<> struct is_e_type: std::true_type { }; } } struct e_lua_pcall_error { int value; }; struct e_lua_error_message { std::string value; }; -//This is a C callback function with a specific signature, made accessible to programs -//written in Lua. +// This is a C callback function with a specific signature, made accessible to programs +// written in Lua. -//If it succeeds, it returns an int answer, by pushing it onto the Lua stack. But "sometimes" -//it fails, in which case it calls luaL_error. This causes the Lua interpreter to abort and pop -//back into the C++ code which called it (see call_lua below). +// If it succeeds, it returns an int answer, by pushing it onto the Lua stack. But "sometimes" +// it fails, in which case it calls luaL_error. This causes the Lua interpreter to abort and pop +// back into the C++ code which called it (see call_lua below). int do_work( lua_State * L ) noexcept { bool success=rand()%2; if( success ) { - lua_pushnumber(L,42); //Return 42 to the calling Lua program. + lua_pushnumber(L,42); // Return 42 to the calling Lua program. return 1; } else { - //Associate an do_work_error_code object with the *next* leaf::error object we will - //definitely return from the call_lua function... + // Associate an do_work_error_code object with the *next* leaf::error object we will + // definitely return from the call_lua function... leaf::next_error_value().propagate(ec1); //...once control reaches it, after we tell the Lua interpreter to abort the program. @@ -59,16 +59,16 @@ int do_work( lua_State * L ) noexcept std::shared_ptr init_lua_state() noexcept { - //Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. + // Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. std::shared_ptr L(lua_open(),&lua_close); - //Register the do_work function (above) as a C callback, under the global - //Lua name "do_work". With this, calls from Lua programs to do_work - //will land in the do_work C function we've registered. + // Register the do_work function (above) as a C callback, under the global + // Lua name "do_work". With this, calls from Lua programs to do_work + // will land in the do_work C function we've registered. lua_register( &*L, "do_work", &do_work ); - //Pass some Lua code as a C string literal to Lua. This creates a global Lua - //function called "call_do_work", which we will later ask Lua to execute. + // Pass some Lua code as a C string literal to Lua. This creates a global Lua + // function called "call_do_work", which we will later ask Lua to execute. luaL_dostring( &*L, "\ \n function call_do_work()\ \n return do_work()\ @@ -78,30 +78,30 @@ std::shared_ptr init_lua_state() noexcept } -//Here we will ask Lua to execute the function call_do_work, which is written -//in Lua, and returns the value from do_work, which is written in C++ and -//registered with the Lua interpreter as a C callback. +// Here we will ask Lua to execute the function call_do_work, which is written +// in Lua, and returns the value from do_work, which is written in C++ and +// registered with the Lua interpreter as a C callback. -//If do_work succeeds, we return the resulting int answer in leaf::result. -//If it fails, we'll communicate that failure to our caller. +// If do_work succeeds, we return the resulting int answer in leaf::result. +// If it fails, we'll communicate that failure to our caller. leaf::result call_lua( lua_State * L ) { - //Ask the Lua interpreter to call the global Lua function call_do_work. + // Ask the Lua interpreter to call the global Lua function call_do_work. lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); if( int err=lua_pcall(L,0,1,0) ) { - //Something went wrong with the call, so we'll return a leaf::error. - //If this is a do_work failure, the do_work_error_code object prepared in - //do_work will become associated with this leaf::error value. If not, - //we will still need to communicate that the lua_pcall failed with an - //error code and an error message. + // Something went wrong with the call, so we'll return a leaf::error. + // If this is a do_work failure, the do_work_error_code object prepared in + // do_work will become associated with this leaf::error value. If not, + // we will still need to communicate that the lua_pcall failed with an + // error code and an error message. auto propagate = leaf::preload( e_lua_error_message{lua_tostring(L,1)} ); lua_pop(L,1); return leaf::new_error( e_lua_pcall_error{err} ); } else { - //Success! Just return the int answer. + // Success! Just return the int answer. int answer=lua_tonumber(L,-1); lua_pop(L,1); return answer; @@ -121,13 +121,13 @@ int main() noexcept { bool matched = exp.handle_error( r, - //Handle e_do_work failures: + // Handle e_do_work failures: [ ]( do_work_error_code e ) { std::cout << "Got do_work_error_code = " << e << "!\n"; }, - //Handle all other lua_pcall failures: + // Handle all other lua_pcall failures: [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) { std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; diff --git a/example/print_file_eh.cpp b/example/print_file_eh.cpp index 9e4c6d1..6ebfb77 100644 --- a/example/print_file_eh.cpp +++ b/example/print_file_eh.cpp @@ -1,12 +1,12 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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 the basic use of LEAF to augment error conditions with -//additional information when using exceptions to report failures. See print_file_result.cpp -//for the variant that doesn't use exceptions. +// This example demonstrates the basic use of LEAF to augment error conditions with +// additional information when using exceptions to report failures. See print_file_result.cpp +// for the variant that doesn't use exceptions. #include #include @@ -18,13 +18,7 @@ namespace leaf = boost::leaf; -//We could define our own e-types, but for this example the ones defined in -// are a perfect match. -using leaf::e_file_name; -using leaf::e_errno; - - -//Exception type hierarchy. +// Exception type hierarchy. struct print_file_error : virtual std::exception { }; struct command_line_error : virtual print_file_error { }; struct bad_command_line : virtual command_line_error { }; @@ -36,19 +30,110 @@ struct input_file_read_error : virtual input_file_error { }; struct input_eof_error : virtual input_file_error { }; +// We will handle all failures in our main function, but first, here are the declarations of the functions it calls, each +// communicating failures using leaf::result: + +// Parse the command line, return the file name. +char const * parse_command_line( int argc, char const * argv[ ] ); + +// Open a file for reading. +std::shared_ptr file_open( char const * file_name ); + +// Return the size of the file. +int file_size( FILE & f ); + +// Read size bytes from f into buf. +void file_read( FILE & f, void * buf, int size ); + + +int main( int argc, char const * argv[ ] ) +{ + // Configure std::cout to throw on error. + std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); + + return leaf::try_( + [&] + { + char const * file_name = parse_command_line(argc,argv); + std::shared_ptr f = file_open( file_name ); + + auto propagate1 = leaf::preload( leaf::e_file_name{file_name} ); + + std::string buffer( 1+file_size(*f), '\0' ); + file_read(*f,&buffer[0],buffer.size()-1); + + auto propagate2 = leaf::defer([ ] { return leaf::e_errno{errno}; } ); + std::cout << buffer; + std::cout.flush(); + + return 0; + }, + + [ ]( leaf::catch_, leaf::match, leaf::e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + [ ]( leaf::catch_, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + [ ]( leaf::catch_, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; + return 3; + }, + + [ ]( leaf::catch_, leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + [ ]( leaf::catch_ ) + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} + + +// Parse the command line, return the file name. +char const * parse_command_line( int argc, char const * argv[ ] ) +{ + if( argc!=2 ) + throw bad_command_line(); + return argv[1]; +} + + +// Open a file for reading. std::shared_ptr file_open( char const * file_name ) { if( FILE * f = fopen(file_name,"rb") ) return std::shared_ptr(f,&fclose); else - throw leaf::exception( input_file_open_error(), e_file_name{file_name}, e_errno{errno} ); + throw leaf::exception( input_file_open_error(), leaf::e_errno{errno} ); } +// Return the size of the file. int file_size( FILE & f ) { - //All exceptions escaping this function will automatically propagate errno. - auto propagate = leaf::defer( [ ] { return e_errno{errno}; } ); + // All exceptions escaping this function will automatically propagate errno. + auto propagate = leaf::defer( [ ] { return leaf::e_errno{errno}; } ); if( fseek(&f,0,SEEK_END) ) throw input_file_size_error(); @@ -64,85 +149,14 @@ int file_size( FILE & f ) } +// Read size bytes from f into buf. void file_read( FILE & f, void * buf, int size ) { int n = fread(buf,1,size,&f); if( ferror(&f) ) - throw leaf::exception( input_file_read_error(), e_errno{errno} ); + throw leaf::exception( input_file_read_error(), leaf::e_errno{errno} ); if( n!=size ) throw input_eof_error(); } - - -char const * parse_command_line( int argc, char const * argv[ ] ) -{ - if( argc!=2 ) - throw bad_command_line(); - return argv[1]; -} - - -int main( int argc, char const * argv[ ] ) -{ - //Configure std::cout to throw on error. - std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); - - return leaf::try_( - [&] - { - char const * file_name = parse_command_line(argc,argv); - std::shared_ptr f = file_open( file_name ); - - auto propagate1 = leaf::preload( e_file_name{file_name} ); - - std::string buffer( 1+file_size(*f), '\0' ); - file_read(*f,&buffer[0],buffer.size()-1); - - auto propagate2 = leaf::defer([ ] { return e_errno{errno}; } ); - std::cout << buffer; - std::cout.flush(); - - return 0; - }, - - [ ]( leaf::catch_ ) - { - std::cout << "Bad command line argument" << std::endl; - return 1; - }, - - [ ]( leaf::catch_, leaf::match, e_file_name const & fn ) - { - std::cerr << "File not found: " << fn.value << std::endl; - return 2; - }, - - [ ]( leaf::catch_, e_errno const & errn, e_file_name const & fn ) - { - std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; - return 3; - }, - - [ ]( leaf::catch_, e_errno const & errn, e_file_name const & fn ) - { - std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; - return 4; - }, - - [ ]( leaf::catch_, e_errno const & errn ) - { - std::cerr << "Output error, errno=" << errn << std::endl; - return 5; - }, - - [ ]( leaf::error_info const & ei ) - { - std::cerr << - "Unknown failure detected" << std::endl << - "Cryptic diagnostic information follows" << std::endl << - ei; - return 6; - } ); -} diff --git a/example/print_file_result.cpp b/example/print_file_result.cpp index 1187795..9df97b7 100644 --- a/example/print_file_result.cpp +++ b/example/print_file_result.cpp @@ -1,12 +1,11 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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 the basic use of LEAF to augment error conditions with -//additional information when using result to report failures. See print_file_eh.cpp -//for the variant that uses exception handling. +// This is a short but complete program that reads a text file in a buffer and prints it to std::cout, +// using LEAF to handle errors. It does not use exception handling. #include #include @@ -16,14 +15,8 @@ namespace leaf = boost::leaf; - -//We could define our own e-types, but for this example the ones -//defined in are a perfect match. -using leaf::e_file_name; -using leaf::e_errno; - - -enum error_codes +// First, we need an enum to define our error codes: +enum error_code { bad_command_line = 1, input_file_open_error, @@ -32,23 +25,156 @@ enum error_codes input_eof_error, cout_error }; + +// To enable leaf::error to work with our error_code enum, we need to specialize the is_e_type template: namespace boost { namespace leaf { - template<> struct is_error_type: std::true_type { }; + template<> struct is_e_type: std::true_type { }; } } +// We will handle all failures in our main function, but first, here are the declarations of the functions it calls, each +// communicating failures using leaf::result: + +// Parse the command line, return the file name. +leaf::result parse_command_line( int argc, char const * argv[ ] ); + +// Open a file for reading. +leaf::result> file_open( char const * file_name ); + +// Return the size of the file. +leaf::result file_size( FILE & f ); + +// Read size bytes from f into buf. +leaf::result file_read( FILE & f, void * buf, int size ); + + +// The main function, which handles all errors. +int main( int argc, char const * argv[ ] ) +{ + return leaf::handle_all( + + [&]() -> leaf::result + { + leaf::result file_name = parse_command_line(argc,argv); + if( !file_name ) + return file_name.error(); + + auto propagate = leaf::preload( leaf::e_file_name{*file_name} ); + + leaf::result> f = file_open(*file_name); + if( !f ) + return f.error(); + + leaf::result s = file_size(**f); + if( !s ) + return s.error(); + + std::string buffer( 1 + *s, '\0' ); + leaf::result fr = file_read(**f, &buffer[0], buffer.size()-1); + if( !fr ) + return fr.error(); + + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + return leaf::new_error( cout_error, leaf::e_errno{errno} ); + + return 0; + }, + + // Each of the lambdas below is an error handler. LEAF will consider them, in order, and call the first one that matches + // the available error information. + + // This handler will be called if the error includes: + // - an object of type error_code equal to input_file_open_error, and + // - an object of type leaf::e_errno that has .value equal to ENOENT, and + // - an object of type leaf::e_file_name. + [ ]( leaf::match, leaf::match, leaf::e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to input_file_open_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + [ ]( leaf::match, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to any of input_file_size_error, input_file_read_error, input_eof_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + [ ]( leaf::match, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; + return 3; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to cout_error, and + // - an object of type leaf::e_errno (regardless of its .value), + [ ]( leaf::match, leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + // This handler will be called if the error includes an object of type error_code equal to bad_command_line. + [ ]( leaf::match ) + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + // This last handler matches any error: it prints diagnostic information to help debug logic errors in the program, since it + // failed to match an appropriate error handler to the error condition it encountered. In this program this handler will + // never be called, but it is required by handle_all because, well, it must handle all errors (the alternative is to use + // handle_some instead, which doesn't require a "catch all" last-resort handler; instead, if it fails to find a suitable + // handler for an error, it returns the error to its caller.) + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } + ); +} + + +// Implementations of the functions called by main: + + +// Parse the command line, return the file name. +leaf::result parse_command_line( int argc, char const * argv[ ] ) +{ + if( argc==2 ) + return argv[1]; + else + return leaf::new_error(bad_command_line); +} + + +// Open a file for reading. leaf::result> file_open( char const * file_name ) { if( FILE * f = fopen(file_name,"rb") ) return std::shared_ptr(f,&fclose); else - return leaf::new_error( input_file_open_error, e_file_name{file_name}, e_errno{errno} ); + return leaf::new_error( input_file_open_error, leaf::e_errno{errno} ); } +// Return the size of the file. leaf::result file_size( FILE & f ) { - auto propagate = leaf::defer([ ] { return e_errno{errno}; } ); + auto propagate = leaf::defer([ ] { return leaf::e_errno{errno}; } ); if( fseek(&f,0,SEEK_END) ) return leaf::new_error( input_file_size_error ); @@ -64,91 +190,15 @@ leaf::result file_size( FILE & f ) } +// Read size bytes from f into buf. leaf::result file_read( FILE & f, void * buf, int size ) { int n = fread(buf,1,size,&f); if( ferror(&f) ) - return leaf::new_error( input_file_read_error, e_errno{errno} ); + return leaf::new_error( input_file_read_error, leaf::e_errno{errno} ); if( n!=size ) return leaf::new_error( input_eof_error ); return { }; } - - -leaf::result parse_command_line( int argc, char const * argv[ ] ) -{ - if( argc==2 ) - return argv[1]; - else - return leaf::new_error(bad_command_line); -} - - -int main( int argc, char const * argv[ ] ) -{ - return leaf::handle_all( - - [&]() -> leaf::result - { - LEAF_AUTO(file_name, parse_command_line(argc,argv)); - - LEAF_AUTO(f, file_open(file_name)); - - auto propagate1 = leaf::preload( e_file_name{file_name} ); - - LEAF_AUTO(s, file_size(*f)); - - std::string buffer( 1+s, '\0' ); - LEAF_CHECK(file_read(*f,&buffer[0],buffer.size()-1)); - - auto propagate2 = leaf::defer([ ] { return e_errno{errno}; } ); - std::cout << buffer; - std::cout.flush(); - if( std::cout.fail() ) - return leaf::new_error( cout_error ); - - return 0; - }, - - [ ]( leaf::match ) - { - std::cout << "Bad command line argument" << std::endl; - return 1; - }, - - [ ]( leaf::match, leaf::match, e_file_name const & fn ) - { - std::cerr << "File not found: " << fn.value << std::endl; - return 2; - }, - - [ ]( leaf::match, e_errno const & errn, e_file_name const & fn ) - { - std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; - return 3; - }, - - [ ]( leaf::match, e_errno const & errn, e_file_name const & fn ) - { - std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; - return 4; - }, - - [ ]( leaf::match, e_errno const & errn ) - { - std::cerr << "Output error, errno=" << errn << std::endl; - return 5; - }, - - [ ]( leaf::error_info const & ei ) - { - std::cerr << - "Unknown failure detected" << std::endl << - "Cryptic diagnostic information follows" << std::endl << - ei; - return 6; - } - ); -} diff --git a/example/print_half.cpp b/example/print_half.cpp index 0ce11e6..0cf62cf 100644 --- a/example/print_half.cpp +++ b/example/print_half.cpp @@ -1,11 +1,11 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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 is an adaptation of the following Boost Outcome example: -//https://github.com/ned14/outcome/blob/master/doc/src/snippets/using_result.cpp +// This program is an adaptation of the following Boost Outcome example: +// https://github.com/ned14/outcome/blob/master/doc/src/snippets/using_result.cpp #include #include @@ -23,7 +23,7 @@ enum class ConversionErrc }; namespace boost { namespace leaf { - template <> struct is_error_type: public std::true_type { }; + template <> struct is_e_type: public std::true_type { }; } } leaf::result convert(const std::string& str) noexcept @@ -40,7 +40,7 @@ leaf::result convert(const std::string& str) noexcept return atoi(str.c_str()); } -//Do not static_store BigInt to actually work -- it's a stub. +// Do not static_store BigInt to actually work -- it's a stub. struct BigInt { static leaf::result fromString(const std::string& s) { return BigInt{s}; } @@ -49,7 +49,7 @@ struct BigInt friend std::ostream& operator<<(std::ostream& o, const BigInt&) { return o << "big int half"; } }; -//This function handles ConversionErrc::TooLong errors, forwards any other error to the caller. +// This function handles ConversionErrc::TooLong errors, forwards any other error to the caller. leaf::result print_half(const std::string& text) { return leaf::handle_some( @@ -89,14 +89,14 @@ int main( int argc, char const * argv[ ] ) return 2; }, - [ ]( leaf::error_info const & ei ) + [ ]( leaf::error_info const & unmatched ) { - //This will never execute in this program, but it would detect logic errors where an unknown error reaches main. - //In this case, we print diagnostic information. + // This will never execute in this program, but it would detect logic errors where an unknown error reaches main. + // In this case, we print diagnostic information. std::cerr << "Unknown failure detected" << std::endl << "Cryptic diagnostic information follows" << std::endl << - ei; + unmatched; return 3; } ); } diff --git a/include/boost/leaf/all.hpp b/include/boost/leaf/all.hpp index 064d807..e8e6222 100644 --- a/include/boost/leaf/all.hpp +++ b/include/boost/leaf/all.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_3382F652FCDD11E8AAD1CCB3642D5A5F #define BOOST_LEAF_3382F652FCDD11E8AAD1CCB3642D5A5F -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp index 73aff8a..8d19d7d 100644 --- a/include/boost/leaf/capture_exception.hpp +++ b/include/boost/leaf/capture_exception.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_BC24FB98B2DE11E884419CF5AD35F1A2 #define BOOST_LEAF_BC24FB98B2DE11E884419CF5AD35F1A2 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -120,7 +120,7 @@ namespace boost { namespace leaf { } }; - } //leaf_detail + } // leaf_detail template leaf_detail::exception_trap::mp_args,E...> capture_exception( F && f ) noexcept diff --git a/include/boost/leaf/capture_result.hpp b/include/boost/leaf/capture_result.hpp index c2e389a..2a0b7f5 100644 --- a/include/boost/leaf/capture_result.hpp +++ b/include/boost/leaf/capture_result.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_2416C558123711E9B9D9691F8C7F4AFC #define BOOST_LEAF_2416C558123711E9B9D9691F8C7F4AFC -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/include/boost/leaf/common.hpp b/include/boost/leaf/common.hpp index c7c504b..8dfe6e5 100644 --- a/include/boost/leaf/common.hpp +++ b/include/boost/leaf/common.hpp @@ -1,11 +1,11 @@ #ifndef EBA7EF10B6F311E8AAD493990D39171A #define EBA7EF10B6F311E8AAD493990D39171A -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/include/boost/leaf/detail/captured_exception.hpp b/include/boost/leaf/detail/captured_exception.hpp index 8468bf5..c37f0da 100644 --- a/include/boost/leaf/detail/captured_exception.hpp +++ b/include/boost/leaf/detail/captured_exception.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_E32F3CCC139011E995085E318C7F4AFC #define BOOST_LEAF_E32F3CCC139011E995085E318C7F4AFC -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/include/boost/leaf/detail/demangle.hpp b/include/boost/leaf/detail/demangle.hpp index d07eac6..42033a4 100644 --- a/include/boost/leaf/detail/demangle.hpp +++ b/include/boost/leaf/detail/demangle.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_E7999136FCD911E8B7F308A7642D5A5F #define BOOST_LEAF_E7999136FCD911E8B7F308A7642D5A5F -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) // core::demangle // diff --git a/include/boost/leaf/detail/dynamic_store.hpp b/include/boost/leaf/detail/dynamic_store.hpp index 8cc7e97..700c8bf 100644 --- a/include/boost/leaf/detail/dynamic_store.hpp +++ b/include/boost/leaf/detail/dynamic_store.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_6CCC5F56124B11E9B6C4CB8C8C7F4AFC #define BOOST_LEAF_6CCC5F56124B11E9B6C4CB8C8C7F4AFC -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include diff --git a/include/boost/leaf/detail/dynamic_store_impl.hpp b/include/boost/leaf/detail/dynamic_store_impl.hpp index 9654f0d..292b78b 100644 --- a/include/boost/leaf/detail/dynamic_store_impl.hpp +++ b/include/boost/leaf/detail/dynamic_store_impl.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_C86E4C4ED0F011E8BB777EB8A659E189 #define BOOST_LEAF_C86E4C4ED0F011E8BB777EB8A659E189 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -76,7 +76,7 @@ namespace boost { namespace leaf { { } }; - } //leaf_detail + } // leaf_detail } } diff --git a/include/boost/leaf/detail/function_traits.hpp b/include/boost/leaf/detail/function_traits.hpp index 7dca769..b633d01 100644 --- a/include/boost/leaf/detail/function_traits.hpp +++ b/include/boost/leaf/detail/function_traits.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_14440B9CF07011E88377FD4B0ABE8030 #define BOOST_LEAF_14440B9CF07011E88377FD4B0ABE8030 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -65,7 +65,7 @@ namespace boost { namespace leaf { template struct function_traits : function_traits { }; template struct function_traits : function_traits { }; template struct function_traits : function_traits { }; - } //namespace leaf_detail + } // namespace leaf_detail } } diff --git a/include/boost/leaf/detail/optional.hpp b/include/boost/leaf/detail/optional.hpp index 58f80a9..701245f 100644 --- a/include/boost/leaf/detail/optional.hpp +++ b/include/boost/leaf/detail/optional.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_47258FCCB6B411E8A1F35AA00C39171A #define BOOST_LEAF_47258FCCB6B411E8A1F35AA00C39171A -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -151,7 +151,7 @@ namespace boost { namespace leaf { } }; - } //leaf_detail + } // leaf_detail } } diff --git a/include/boost/leaf/detail/print.hpp b/include/boost/leaf/detail/print.hpp index 697ac45..e5872c9 100644 --- a/include/boost/leaf/detail/print.hpp +++ b/include/boost/leaf/detail/print.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_3BAB50A2B87E11E89EEB30600C39171A #define BOOST_LEAF_3BAB50A2B87E11E89EEB30600C39171A -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -115,7 +115,7 @@ namespace boost { namespace leaf { return false; } }; - } //leaf_detail + } // leaf_detail } } diff --git a/include/boost/leaf/detail/print_exception_info.hpp b/include/boost/leaf/detail/print_exception_info.hpp index 107639e..0abdf67 100644 --- a/include/boost/leaf/detail/print_exception_info.hpp +++ b/include/boost/leaf/detail/print_exception_info.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_E823AAD6151311E9A430DDBB67511AEB #define BOOST_LEAF_E823AAD6151311E9A430DDBB67511AEB -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -33,7 +33,7 @@ namespace boost { namespace leaf { else os << "Unknown exception type (not a std::exception)" << std::endl; } - } //namespace leaf_detail + } // namespace leaf_detail } } diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index 85da59b..2f0d0fc 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_AFBBD676B2FF11E8984C7976AE35F1A2 #define BOOST_LEAF_AFBBD676B2FF11E8984C7976AE35F1A2 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -40,17 +40,63 @@ namespace boost { namespace leaf { return e; } }; + + template + bool check_value_pack( T const & x, T const & v ) noexcept + { + return x==v; + } + + template + bool check_value_pack( T const & x, T const & v1, VRest const & ... v_rest ) noexcept + { + return x==v1 || check_value_pack(x,v_rest...); + } + + template + bool check_exception_pack( std::exception const * ex, Ex const * ) noexcept + { + return dynamic_cast(ex)!=0; + } + + template + bool check_exception_pack( std::exception const * ex, Ex const *, ExRest const * ... ex_rest ) noexcept + { + return dynamic_cast(ex)!=0 || check_exception_pack(ex, ex_rest...); + } } - template ::type... Value> + template ::type... V> struct match { - typename leaf_detail::match_type::type value; + using type = typename leaf_detail::match_type::type; + type const & value; + + explicit match( E const & e ): + value(leaf_detail::match_type::get(e)) + { + } + + bool operator()() const noexcept + { + return leaf_detail::check_value_pack(value,V...); + } }; template struct catch_ { + std::exception const & value; + + explicit catch_( std::exception const & ex ): + value(ex) + { + } + + bool operator()() const noexcept + { + return leaf_detail::check_exception_pack(&value,static_cast(0)...); + } }; namespace leaf_detail @@ -147,30 +193,6 @@ namespace boost { namespace leaf { //////////////////////////////////////// - template - bool check_value_pack( T const & x, T const & v ) noexcept - { - return x==v; - } - - template - bool check_value_pack( T const & x, T const & v1, VRest const & ... v_rest ) noexcept - { - return x==v1 || check_value_pack(x,v_rest...); - } - - template - bool check_exception_pack( error_info const & ei, Ex const * ) noexcept - { - return dynamic_cast(ei.get_exception())!=0; - } - - template - bool check_exception_pack( error_info const & ei, Ex const *, ExRest const * ... ex_rest ) noexcept - { - return dynamic_cast(ei.get_exception())!=0 || check_exception_pack(ei, ex_rest...); - } - template struct check_one_argument { @@ -190,8 +212,8 @@ namespace boost { namespace leaf { } }; - template ::type... Value> - struct check_one_argument> + template ::type... V> + struct check_one_argument> { static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept { @@ -199,7 +221,7 @@ namespace boost { namespace leaf { if( sl.has_value() ) { auto const & v = sl.value(); - return v.e==ei.get_error() && check_value_pack(match_type::get(v.v),Value...); + return v.e==ei.get_error() && match(v.v)(); } else return false; @@ -211,7 +233,10 @@ namespace boost { namespace leaf { { static bool check( SlotsTuple const &, error_info const & ei ) noexcept { - return check_exception_pack(ei,static_cast(0)...); + if( std::exception const * ex = ei.get_exception() ) + return catch_(*ex)(); + else + return false; } }; @@ -269,15 +294,15 @@ namespace boost { namespace leaf { } }; - template ::type... Value> - struct get_one_argument> + template ::type... V> + struct get_one_argument> { template - static match get( StaticStore const & ss, error_info const & ei ) noexcept + static match get( StaticStore const & ss, error_info const & ei ) noexcept { E const * arg = ss.template peek(ei.get_error()); assert(arg!=0); - return match{match_type::get(*arg)}; + return match(*arg); } }; @@ -285,9 +310,11 @@ namespace boost { namespace leaf { struct get_one_argument> { template - static constexpr catch_ get( StaticStore const &, error_info const & ) noexcept + static catch_ get( StaticStore const &, error_info const & ei ) noexcept { - return { }; + std::exception const * ex = ei.get_exception(); + assert(ex!=0); + return catch_(*ex); } }; @@ -317,7 +344,7 @@ namespace boost { namespace leaf { //////////////////////////////////////// template struct acceptable_last_handler_argument: std::false_type { }; - template struct acceptable_last_handler_argument: is_error_type { }; + template struct acceptable_last_handler_argument: is_e_type { }; template <> struct acceptable_last_handler_argument: std::true_type { }; template <> struct acceptable_last_handler_argument: std::true_type { }; template <> struct acceptable_last_handler_argument: std::true_type { }; @@ -419,13 +446,13 @@ namespace boost { namespace leaf { } }; - //Static store deduction + // Static store deduction template struct translate_expect_deduction { typedef T type; }; template struct translate_expect_deduction { typedef T type; }; template struct translate_expect_deduction { typedef T type; }; template struct translate_expect_deduction { typedef T type; }; - template ::type... Value> struct translate_expect_deduction> { typedef E type; }; + template ::type... V> struct translate_expect_deduction> { typedef E type; }; template struct translate_expect_deduction> { typedef void type; }; template @@ -443,15 +470,15 @@ namespace boost { namespace leaf { template <> struct does_not_participate_in_expect_deduction: std::true_type { }; template <> struct does_not_participate_in_expect_deduction: std::true_type { }; - template - struct handlers_args_set + template + struct handler_args_set { using type = leaf_detail_mp11::mp_remove_if< leaf_detail_mp11::mp_unique< translate_list< leaf_detail_mp11::mp_append< - typename function_traits::mp_args... + typename function_traits::mp_args... > > >, @@ -467,7 +494,7 @@ namespace boost { namespace leaf { { typedef static_store type; }; - } //leaf_detail + } // leaf_detail } } diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 7cbe8a8..6913d79 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_BA049396D0D411E8B45DF7D4A759E189 #define BOOST_LEAF_BA049396D0D411E8B45DF7D4A759E189 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -64,20 +64,25 @@ namespace boost { namespace leaf { { static constexpr bool value=std::is_member_object_pointer::value; }; + + template + struct is_error_type_default + { + static constexpr bool value = has_data_member_value::value || std::is_base_of::value; + }; + + template <> struct is_error_type_default: std::true_type { }; + template <> struct is_error_type_default: std::true_type { }; + template <> struct is_error_type_default: std::true_type { }; + template <> struct is_error_type_default: std::true_type { }; + template <> struct is_error_type_default: std::true_type { }; } template - struct is_error_type + struct is_e_type: leaf_detail::is_error_type_default { - static constexpr bool value = leaf_detail::has_data_member_value::value || std::is_base_of::value; }; - template <> struct is_error_type: std::true_type { }; - template <> struct is_error_type: std::true_type { }; - template <> struct is_error_type: std::true_type { }; - template <> struct is_error_type: std::true_type { }; - template <> struct is_error_type: std::true_type { }; - //////////////////////////////////////// class error; @@ -385,10 +390,12 @@ namespace boost { namespace leaf { } }; - template <> struct is_error_type: std::true_type { }; - namespace leaf_detail { + template <> struct is_error_type_default: std::true_type + { + }; + template <> struct diagnostic { @@ -412,7 +419,7 @@ namespace boost { namespace leaf { slot & operator=( slot const & ) = delete; typedef optional> base; slot * prev_; - static_assert(is_error_type::value,"Not an error type"); + static_assert(is_e_type::value,"Not an error type"); bool slot_print( std::ostream &, error const & ) const; @@ -542,7 +549,7 @@ namespace boost { namespace leaf { assert(file&&*file); assert(line>0); assert(function&&*function); - e_source_location sl { file, line, function }; //Temp object MSVC workaround + e_source_location sl { file, line, function }; // Temp object MSVC workaround return error( std::move(sl), std::forward(e)... ); } @@ -552,7 +559,7 @@ namespace boost { namespace leaf { err, cap }; - } //leaf_detail + } // leaf_detail template error error::propagate( E && ... e ) const noexcept diff --git a/include/boost/leaf/exception_to_result.hpp b/include/boost/leaf/exception_to_result.hpp index e6120b6..86ba897 100644 --- a/include/boost/leaf/exception_to_result.hpp +++ b/include/boost/leaf/exception_to_result.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_017BF91412EB11E9926CDCED8B7F4AFC #define BOOST_LEAF_017BF91412EB11E9926CDCED8B7F4AFC -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/include/boost/leaf/handle.hpp b/include/boost/leaf/handle.hpp index 72a7567..6722d70 100644 --- a/include/boost/leaf/handle.hpp +++ b/include/boost/leaf/handle.hpp @@ -1,27 +1,27 @@ #ifndef BOOST_LEAF_73685B76115611E9950D61678B7F4AFC #define BOOST_LEAF_73685B76115611E9950D61678B7F4AFC -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include namespace boost { namespace leaf { - template - typename std::remove_reference::return_type>().value())>::type handle_all( TryBlock && try_block, Handlers && ... handlers ) + template + typename std::remove_reference()().value())>::type handle_all( TryBlock && try_block, Handler && ... handler ) { using namespace leaf_detail; - typename deduce_static_store::type>::type ss; + typename deduce_static_store::type>::type ss; ss.set_reset(true); if( auto r = std::forward(try_block)() ) return *r; else - return ss.handle_error(error_info(r.error()), std::forward(handlers)...); + return ss.handle_error(error_info(r.error()), std::forward(handler)...); } namespace leaf_detail @@ -59,18 +59,18 @@ namespace boost { namespace leaf { }; } - template - typename leaf_detail::function_traits::return_type handle_some( TryBlock && try_block, Handlers && ... handlers ) + template + decltype(std::declval()()) handle_some( TryBlock && try_block, Handler && ... handler ) { using namespace leaf_detail; using R = typename function_traits::return_type; - typename deduce_static_store::type>::type ss; + typename deduce_static_store::type>::type ss; if( auto r = std::forward(try_block)() ) { ss.set_reset(true); return r; } - else if( auto rr = ss.handle_error(error_info(r.error()), handler_wrapper(std::forward(handlers))..., [&r] { return r; } ) ) + else if( auto rr = ss.handle_error(error_info(r.error()), handler_wrapper(std::forward(handler))..., [&r] { return r; } ) ) { ss.set_reset(true); return rr; diff --git a/include/boost/leaf/preload.hpp b/include/boost/leaf/preload.hpp index 22a7e2f..0e57776 100644 --- a/include/boost/leaf/preload.hpp +++ b/include/boost/leaf/preload.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_25AF99F6DC6F11E8803DE9BC9723C688 #define BOOST_LEAF_25AF99F6DC6F11E8803DE9BC9723C688 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -29,7 +29,7 @@ namespace boost { namespace leaf { { static void trigger( Tuple const &, error const & ) noexcept { } }; - } //leaf_detail + } // leaf_detail //////////////////////////////////////// @@ -107,7 +107,7 @@ namespace boost { namespace leaf { leaf_detail::tuple_for_each_preload::trigger(p_,e); } }; - } //leaf_detail + } // leaf_detail template leaf_detail::preloaded preload( E && ... e ) noexcept @@ -191,7 +191,7 @@ namespace boost { namespace leaf { leaf_detail::tuple_for_each_preload::trigger(d_,e); } }; - } //leaf_detail + } // leaf_detail template leaf_detail::deferred defer( F && ... f ) noexcept diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index dfa12ef..e9eb086 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_2CD8E6B8CA8D11E8BD3B80D66CE5B91B #define BOOST_LEAF_2CD8E6B8CA8D11E8BD3B80D66CE5B91B -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/include/boost/leaf/throw.hpp b/include/boost/leaf/throw.hpp index cdefcfe..faf76f0 100644 --- a/include/boost/leaf/throw.hpp +++ b/include/boost/leaf/throw.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_75F38740D98D11E881DDB244C82C3C47 #define BOOST_LEAF_75F38740D98D11E881DDB244C82C3C47 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp index f740fa8..fa44e7e 100644 --- a/include/boost/leaf/try.hpp +++ b/include/boost/leaf/try.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_87F274C4D4BA11E89928D55AC82C3C47 #define BOOST_LEAF_87F274C4D4BA11E89928D55AC82C3C47 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -21,11 +21,11 @@ namespace boost { namespace leaf { return next_error_value(); } - template - typename leaf_detail::function_traits::return_type try_( TryBlock && try_block, Handlers && ... handlers ) + template + decltype(std::declval()()) try_( TryBlock && try_block, Handler && ... handler ) { using namespace leaf_detail; - typename deduce_static_store::type>::type ss; + typename deduce_static_store::type>::type ss; ss.set_reset(true); try { @@ -41,20 +41,20 @@ namespace boost { namespace leaf { } catch( std::exception const & ex ) { - return ss.handle_error(error_info(get_error(ex),&ex,&cap,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(get_error(ex),&ex,&cap,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); } catch( ... ) { - return ss.handle_error(error_info(next_error_value(),0,&cap,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(next_error_value(),0,&cap,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); } } catch( std::exception const & ex ) { - return ss.handle_error(error_info(get_error(ex),&ex,0,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(get_error(ex),&ex,0,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); } catch( ... ) { - return ss.handle_error(error_info(next_error_value(),0,0,&print_exception_info), std::forward(handlers)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(next_error_value(),0,0,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); } } diff --git a/test/_hpp_all_test.cpp b/test/_hpp_all_test.cpp index 341dfa8..aa4c265 100644 --- a/test/_hpp_all_test.cpp +++ b/test/_hpp_all_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/_hpp_capture_exception_test.cpp b/test/_hpp_capture_exception_test.cpp index 8c9742d..bd0e4f7 100644 --- a/test/_hpp_capture_exception_test.cpp +++ b/test/_hpp_capture_exception_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/_hpp_capture_result_test.cpp b/test/_hpp_capture_result_test.cpp index f89e198..f660b58 100644 --- a/test/_hpp_capture_result_test.cpp +++ b/test/_hpp_capture_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/_hpp_common_test.cpp b/test/_hpp_common_test.cpp index 62811a1..23dc3aa 100644 --- a/test/_hpp_common_test.cpp +++ b/test/_hpp_common_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/_hpp_error_test.cpp b/test/_hpp_error_test.cpp index ec3c95c..19a79fa 100644 --- a/test/_hpp_error_test.cpp +++ b/test/_hpp_error_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/_hpp_exception_to_result_test.cpp b/test/_hpp_exception_to_result_test.cpp index 7041667..0f8ba33 100644 --- a/test/_hpp_exception_to_result_test.cpp +++ b/test/_hpp_exception_to_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/_hpp_handle_test.cpp b/test/_hpp_handle_test.cpp index d1bb229..a704338 100644 --- a/test/_hpp_handle_test.cpp +++ b/test/_hpp_handle_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/_hpp_preload_test.cpp b/test/_hpp_preload_test.cpp index a865538..005d35f 100644 --- a/test/_hpp_preload_test.cpp +++ b/test/_hpp_preload_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/_hpp_result_test.cpp b/test/_hpp_result_test.cpp index 85cfc16..88362f8 100644 --- a/test/_hpp_result_test.cpp +++ b/test/_hpp_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/_hpp_throw_test.cpp b/test/_hpp_throw_test.cpp index c982dab..f92173a 100644 --- a/test/_hpp_throw_test.cpp +++ b/test/_hpp_throw_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/_hpp_try_test.cpp b/test/_hpp_try_test.cpp index 4f8eaaf..772af23 100644 --- a/test/_hpp_try_test.cpp +++ b/test/_hpp_try_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/capture_exception_async_test.cpp b/test/capture_exception_async_test.cpp index 6b7f40e..c92e285 100644 --- a/test/capture_exception_async_test.cpp +++ b/test/capture_exception_async_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/capture_exception_state_test.cpp b/test/capture_exception_state_test.cpp index 8420c3d..189c1a8 100644 --- a/test/capture_exception_state_test.cpp +++ b/test/capture_exception_state_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -33,7 +33,7 @@ struct info }; namespace boost { namespace leaf { - template struct is_error_type>: public std::true_type { }; + template struct is_e_type>: public std::true_type { }; } } int main() diff --git a/test/capture_exception_unload_test.cpp b/test/capture_exception_unload_test.cpp index 175a139..04d6ff9 100644 --- a/test/capture_exception_unload_test.cpp +++ b/test/capture_exception_unload_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -157,14 +157,14 @@ int main() test( [ ] { - throw leaf::exception( std::exception(), info<1>{1}, info<3>{3} ); //Derives from leaf::error + throw leaf::exception( std::exception(), info<1>{1}, info<3>{3} ); // Derives from leaf::error } ); test( [ ] { auto propagate = leaf::preload( info<1>{1}, info<3>{3} ); - throw std::exception(); //Does not derive from leaf::error + throw std::exception(); // Does not derive from leaf::error } ); return boost::report_errors(); diff --git a/test/capture_result_async_test.cpp b/test/capture_result_async_test.cpp index 324162b..85fe2d5 100644 --- a/test/capture_result_async_test.cpp +++ b/test/capture_result_async_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/capture_result_state_test.cpp b/test/capture_result_state_test.cpp index 211e466..726dbb8 100644 --- a/test/capture_result_state_test.cpp +++ b/test/capture_result_state_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -33,7 +33,7 @@ struct info }; namespace boost { namespace leaf { - template struct is_error_type>: public std::true_type { }; + template struct is_e_type>: public std::true_type { }; } } int main() diff --git a/test/capture_result_unload_test.cpp b/test/capture_result_unload_test.cpp index 0324e91..41922e8 100644 --- a/test/capture_result_unload_test.cpp +++ b/test/capture_result_unload_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/defer_basic_test.cpp b/test/defer_basic_test.cpp index 24a7113..72253a1 100644 --- a/test/defer_basic_test.cpp +++ b/test/defer_basic_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/defer_nested_error_exception_test.cpp b/test/defer_nested_error_exception_test.cpp index 43cbbd2..3fbc53f 100644 --- a/test/defer_nested_error_exception_test.cpp +++ b/test/defer_nested_error_exception_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/defer_nested_error_result_test.cpp b/test/defer_nested_error_result_test.cpp index af76317..f38f5ea 100644 --- a/test/defer_nested_error_result_test.cpp +++ b/test/defer_nested_error_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/defer_nested_new_error_exception_test.cpp b/test/defer_nested_new_error_exception_test.cpp index e780996..1e09e3d 100644 --- a/test/defer_nested_new_error_exception_test.cpp +++ b/test/defer_nested_new_error_exception_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/defer_nested_new_error_result_test.cpp b/test/defer_nested_new_error_result_test.cpp index 41e77c7..0e80e26 100644 --- a/test/defer_nested_new_error_result_test.cpp +++ b/test/defer_nested_new_error_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/defer_nested_success_exception_test.cpp b/test/defer_nested_success_exception_test.cpp index a4a8a5c..db36b87 100644 --- a/test/defer_nested_success_exception_test.cpp +++ b/test/defer_nested_success_exception_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/defer_nested_success_result_test.cpp b/test/defer_nested_success_result_test.cpp index 877aab4..ed35d04 100644 --- a/test/defer_nested_success_result_test.cpp +++ b/test/defer_nested_success_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/diagnostic_info_test.cpp b/test/diagnostic_info_test.cpp index d716a43..529fee0 100644 --- a/test/diagnostic_info_test.cpp +++ b/test/diagnostic_info_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -91,10 +91,10 @@ int main() non_printable_info_non_printable_payload, leaf::e_errno, leaf::unexpected_error_info const &, - leaf::error_info const & ei ) + leaf::error_info const & unmatched ) { std::ostringstream st; - st << ei; + st << unmatched; std::string s = st.str(); BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); BOOST_TEST(s.find(": N/A")!=s.npos); diff --git a/test/function_traits_test.cpp b/test/function_traits_test.cpp index 4b736e9..07f1991 100644 --- a/test/function_traits_test.cpp +++ b/test/function_traits_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/handle_all_test.cpp b/test/handle_all_test.cpp index 5744fab..17b0767 100644 --- a/test/handle_all_test.cpp +++ b/test/handle_all_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include "boost/core/lightweight_test.hpp" @@ -11,7 +11,7 @@ namespace leaf = boost::leaf; template struct info { int value; }; -enum class error_codes +enum class error_code { ok, error1, @@ -19,15 +19,15 @@ enum class error_codes error3 }; namespace boost { namespace leaf { - template <> struct is_error_type: std::true_type { }; + template <> struct is_e_type: std::true_type { }; } } -struct error_codes_ { error_codes value; }; +struct error_codes_ { error_code value; }; template -leaf::result f( error_codes ec ) +leaf::result f( error_code ec ) { - if( ec==error_codes::ok ) + if( ec==error_code::ok ) return R(42); else return leaf::new_error(ec,error_codes_{ec},info<1>{1},info<2>{2},info<3>{3}); @@ -35,13 +35,13 @@ leaf::result f( error_codes ec ) int main() { - //void, handle_all (success) + // void, handle_all (success) { int c=0; leaf::handle_all( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::ok)); + LEAF_AUTO(answer, f(error_code::ok)); c = answer; return { }; }, @@ -53,19 +53,19 @@ int main() BOOST_TEST(c==42); } - //void, handle_all (failure) + // void, handle_all (failure) { int c=0; leaf::handle_all( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( error_codes ec, info<1> const & x, info<2> y ) + [&c]( error_code ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(ec==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -79,24 +79,24 @@ int main() BOOST_TEST(c==1); } - //void, handle_all (failure), match enum (single enum value) + // void, handle_all (failure), match enum (single enum value) { int c=0; leaf::handle_all( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -110,24 +110,24 @@ int main() BOOST_TEST(c==2); } - //void, handle_all (failure), match enum (multiple enum values) + // void, handle_all (failure), match enum (multiple enum values) { int c=0; leaf::handle_all( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -141,24 +141,24 @@ int main() BOOST_TEST(c==2); } - //void, handle_all (failure), match value (single value) + // void, handle_all (failure), match value (single value) { int c=0; leaf::handle_all( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -172,24 +172,24 @@ int main() BOOST_TEST(c==2); } - //void, handle_all (failure), match value (multiple values) + // void, handle_all (failure), match value (multiple values) { int c=0; leaf::handle_all( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -205,12 +205,12 @@ int main() ////////////////////////////////////// - //int, handle_all (success) + // int, handle_all (success) { int r = leaf::handle_all( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::ok)); + LEAF_AUTO(answer, f(error_code::ok)); return answer; }, [ ] @@ -220,17 +220,17 @@ int main() BOOST_TEST(r==42); } - //int, handle_all (failure) + // int, handle_all (failure) { int r = leaf::handle_all( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( error_codes ec, info<1> const & x, info<2> y ) + [ ]( error_code ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(ec==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 1; @@ -242,21 +242,21 @@ int main() BOOST_TEST(r==1); } - //int, handle_all (failure), match enum (single enum value) + // int, handle_all (failure), match enum (single enum value) { int r = leaf::handle_all( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -268,21 +268,21 @@ int main() BOOST_TEST(r==2); } - //int, handle_all (failure), match enum (multiple enum values) + // int, handle_all (failure), match enum (multiple enum values) { int r = leaf::handle_all( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -294,21 +294,21 @@ int main() BOOST_TEST(r==2); } - //int, handle_all (failure), match value (single value) + // int, handle_all (failure), match value (single value) { int r = leaf::handle_all( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -320,21 +320,21 @@ int main() BOOST_TEST(r==2); } - //int, handle_all (failure), match value (multiple values) + // int, handle_all (failure), match value (multiple values) { int r = leaf::handle_all( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; diff --git a/test/handle_some_basic_test.cpp b/test/handle_some_basic_test.cpp index 3236de8..7f40047 100644 --- a/test/handle_some_basic_test.cpp +++ b/test/handle_some_basic_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include "boost/core/lightweight_test.hpp" @@ -17,7 +17,7 @@ enum class error_code }; namespace boost { namespace leaf { - template <> struct is_error_type: public std::true_type { }; + template <> struct is_e_type: public std::true_type { }; } } leaf::result compute_answer( int what_to_do ) noexcept diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp index f9e844d..fb7f0fd 100644 --- a/test/handle_some_test.cpp +++ b/test/handle_some_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include "boost/core/lightweight_test.hpp" @@ -11,7 +11,7 @@ namespace leaf = boost::leaf; template struct info { int value; }; -enum class error_codes +enum class error_code { ok, error1, @@ -19,15 +19,15 @@ enum class error_codes error3 }; namespace boost { namespace leaf { - template <> struct is_error_type: std::true_type { }; + template <> struct is_e_type: std::true_type { }; } } -struct error_codes_ { error_codes value; }; +struct error_codes_ { error_code value; }; template -leaf::result f( error_codes ec ) +leaf::result f( error_code ec ) { - if( ec==error_codes::ok ) + if( ec==error_code::ok ) return R(42); else return leaf::new_error(ec,error_codes_{ec},info<1>{1},info<2>{2},info<3>{3}); @@ -36,39 +36,39 @@ leaf::result f( error_codes ec ) int main() { - //void, handle_some (success) + // void, handle_some (success) { int c=0; leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::ok)); + LEAF_AUTO(answer, f(error_code::ok)); c = answer; return { }; }, - [&c]( leaf::error_info const & ei ) + [&c]( leaf::error_info const & unmatched ) { BOOST_TEST(c==0); c = 1; - return ei.get_error(); + return unmatched.get_error(); } ); BOOST_TEST(r); BOOST_TEST(c==42); } - //void, handle_some (failure, matched) + // void, handle_some (failure, matched) { int c=0; leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( error_codes ec, info<1> const & x, info<2> y ) + [&c]( error_code ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(ec==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -78,24 +78,24 @@ int main() BOOST_TEST(r); } - //void, handle_some (failure, matched), match enum (single enum value) + // void, handle_some (failure, matched), match enum (single enum value) { int c=0; leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -105,24 +105,24 @@ int main() BOOST_TEST(r); } - //void, handle_some (failure, matched), match enum (multiple enum values) + // void, handle_some (failure, matched), match enum (multiple enum values) { int c=0; leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -132,24 +132,24 @@ int main() BOOST_TEST(r); } - //void, handle_some (failure, matched), match value (single value) + // void, handle_some (failure, matched), match value (single value) { int c=0; leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -159,24 +159,24 @@ int main() BOOST_TEST(r); } - //void, handle_some (failure, matched), match value (multiple values) + // void, handle_some (failure, matched), match value (multiple values) { int c=0; leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -186,7 +186,7 @@ int main() BOOST_TEST(r); } - //void, handle_some (failure, initially not matched) + // void, handle_some (failure, initially not matched) { int c=0; leaf::handle_all( @@ -195,7 +195,7 @@ int main() leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, @@ -208,9 +208,9 @@ int main() BOOST_TEST(c==0); return r; }, - [&c]( error_codes ec, info<1> const & x, info<2> y ) + [&c]( error_code ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(ec==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -224,7 +224,7 @@ int main() BOOST_TEST(c==2); } - //void, handle_some (failure, initially not matched), match enum (single enum value) + // void, handle_some (failure, initially not matched), match enum (single enum value) { int c=0; leaf::handle_all( @@ -233,11 +233,11 @@ int main() leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; @@ -246,9 +246,9 @@ int main() BOOST_TEST(c==0); return r; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -262,7 +262,7 @@ int main() BOOST_TEST(c==2); } - //void, handle_some (failure, initially not matched), match enum (multiple enum values) + // void, handle_some (failure, initially not matched), match enum (multiple enum values) { int c=0; leaf::handle_all( @@ -271,11 +271,11 @@ int main() leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; @@ -284,9 +284,9 @@ int main() BOOST_TEST(c==0); return r; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -300,7 +300,7 @@ int main() BOOST_TEST(c==2); } - //void, handle_some (failure, initially not matched), match value (single value) + // void, handle_some (failure, initially not matched), match value (single value) { int c=0; leaf::handle_all( @@ -309,11 +309,11 @@ int main() leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; @@ -322,9 +322,9 @@ int main() BOOST_TEST(c==0); return r; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -338,7 +338,7 @@ int main() BOOST_TEST(c==2); } - //void, handle_some (failure, initially not matched), match value (multiple values) + // void, handle_some (failure, initially not matched), match value (multiple values) { int c=0; leaf::handle_all( @@ -347,11 +347,11 @@ int main() leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 1; @@ -360,9 +360,9 @@ int main() BOOST_TEST(c==0); return r; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -376,7 +376,7 @@ int main() BOOST_TEST(c==2); } - //void, handle_some (failure, initially matched) + // void, handle_some (failure, initially matched) { int c=0; leaf::handle_all( @@ -385,13 +385,13 @@ int main() leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( error_codes ec, info<1> const & x, info<2> y ) + [&c]( error_code ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(ec==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -414,7 +414,7 @@ int main() BOOST_TEST(c==1); } - //void, handle_some (failure, initially matched), match enum (single enum value) + // void, handle_some (failure, initially matched), match enum (single enum value) { int c=0; leaf::handle_all( @@ -423,13 +423,13 @@ int main() leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -439,7 +439,7 @@ int main() BOOST_TEST(c==1); return r; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 2; @@ -452,7 +452,7 @@ int main() BOOST_TEST(c==1); } - //void, handle_some (failure, initially matched), match enum (multiple enum values) + // void, handle_some (failure, initially matched), match enum (multiple enum values) { int c=0; leaf::handle_all( @@ -461,13 +461,13 @@ int main() leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -477,7 +477,7 @@ int main() BOOST_TEST(c==1); return r; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 2; @@ -490,7 +490,7 @@ int main() BOOST_TEST(c==1); } - //void, handle_some (failure, initially matched), match value (single value) + // void, handle_some (failure, initially matched), match value (single value) { int c=0; leaf::handle_all( @@ -499,13 +499,13 @@ int main() leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -515,7 +515,7 @@ int main() BOOST_TEST(c==1); return r; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 2; @@ -528,7 +528,7 @@ int main() BOOST_TEST(c==1); } - //void, handle_some (failure, initially matched), match value (multiple values) + // void, handle_some (failure, initially matched), match value (multiple values) { int c=0; leaf::handle_all( @@ -537,13 +537,13 @@ int main() leaf::result r = leaf::handle_some( [&c]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); c = answer; return { }; }, - [&c]( leaf::match ec, info<1> const & x, info<2> y ) + [&c]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); BOOST_TEST(c==0); @@ -553,7 +553,7 @@ int main() BOOST_TEST(c==1); return r; }, - [&c]( leaf::match ) + [&c]( leaf::match ) { BOOST_TEST(c==0); c = 2; @@ -568,32 +568,32 @@ int main() ////////////////////////////////////// - //int, handle_some (success) + // int, handle_some (success) { leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::ok)); + LEAF_AUTO(answer, f(error_code::ok)); return answer; }, - [ ]( leaf::error_info const & ei ) + [ ]( leaf::error_info const & unmatched ) { - return ei.get_error(); + return unmatched.get_error(); } ); BOOST_TEST(r && *r==42); } - //int, handle_some (failure, matched) + // int, handle_some (failure, matched) { leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( error_codes ec, info<1> const & x, info<2> y ) + [ ]( error_code ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(ec==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 1; @@ -601,21 +601,21 @@ int main() BOOST_TEST(r && *r==1); } - //int, handle_some (failure, matched), match enum (single enum value) + // int, handle_some (failure, matched), match enum (single enum value) { leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -623,21 +623,21 @@ int main() BOOST_TEST(r && *r==2); } - //int, handle_some (failure, matched), match enum (multiple enum values) + // int, handle_some (failure, matched), match enum (multiple enum values) { leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -645,21 +645,21 @@ int main() BOOST_TEST(r && *r==2); } - //int, handle_some (failure, matched), match value (single value) + // int, handle_some (failure, matched), match value (single value) { leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -667,21 +667,21 @@ int main() BOOST_TEST(r && *r==2); } - //int, handle_some (failure, matched), match value (multiple values) + // int, handle_some (failure, matched), match value (multiple values) { leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -689,7 +689,7 @@ int main() BOOST_TEST(r && *r==2); } - //int, handle_some (failure, initially not matched) + // int, handle_some (failure, initially not matched) { int r = leaf::handle_all( [ ] @@ -697,7 +697,7 @@ int main() leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, [ ]( info<4> ) @@ -707,9 +707,9 @@ int main() BOOST_TEST(!r); return r; }, - [ ]( error_codes ec, info<1> const & x, info<2> y ) + [ ]( error_code ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(ec==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -721,7 +721,7 @@ int main() BOOST_TEST(r==2); } - //int, handle_some (failure, initially not matched), match enum (single enum value) + // int, handle_some (failure, initially not matched), match enum (single enum value) { int r = leaf::handle_all( [ ] @@ -729,19 +729,19 @@ int main() leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; } ); BOOST_TEST(!r); return r; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -753,7 +753,7 @@ int main() BOOST_TEST(r==2); } - //int, handle_some (failure, initially not matched), match enum (multiple enum values) + // int, handle_some (failure, initially not matched), match enum (multiple enum values) { int r = leaf::handle_all( [ ] @@ -761,19 +761,19 @@ int main() leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; } ); BOOST_TEST(!r); return r; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -785,7 +785,7 @@ int main() BOOST_TEST(r==2); } - //int, handle_some (failure, initially not matched), match value (single value) + // int, handle_some (failure, initially not matched), match value (single value) { int r = leaf::handle_all( [ ] @@ -793,19 +793,19 @@ int main() leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; } ); BOOST_TEST(!r); return r; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -817,7 +817,7 @@ int main() BOOST_TEST(r==2); } - //int, handle_some (failure, initially not matched), match value (multiple values) + // int, handle_some (failure, initially not matched), match value (multiple values) { int r = leaf::handle_all( [ ] @@ -825,19 +825,19 @@ int main() leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 1; } ); BOOST_TEST(!r); return r; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 2; @@ -849,7 +849,7 @@ int main() BOOST_TEST(r==2); } - //int, handle_some (failure, initially matched) + // int, handle_some (failure, initially matched) { int r = leaf::handle_all( [ ] @@ -857,12 +857,12 @@ int main() leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( error_codes ec, info<1> const & x, info<2> y ) + [ ]( error_code ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec==error_codes::error1); + BOOST_TEST(ec==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 1; @@ -881,7 +881,7 @@ int main() BOOST_TEST(r==1); } - //int, handle_some (failure, initially matched), match enum (single enum value) + // int, handle_some (failure, initially matched), match enum (single enum value) { int r = leaf::handle_all( [ ] @@ -889,12 +889,12 @@ int main() leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 1; @@ -902,7 +902,7 @@ int main() BOOST_TEST(r); return r; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 2; }, @@ -913,7 +913,7 @@ int main() BOOST_TEST(r==1); } - //int, handle_some (failure, initially matched), match enum (multiple enum values) + // int, handle_some (failure, initially matched), match enum (multiple enum values) { int r = leaf::handle_all( [ ] @@ -921,12 +921,12 @@ int main() leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 1; @@ -934,7 +934,7 @@ int main() BOOST_TEST(r); return r; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 2; }, @@ -945,7 +945,7 @@ int main() BOOST_TEST(r==1); } - //int, handle_some (failure, initially matched), match value (single value) + // int, handle_some (failure, initially matched), match value (single value) { int r = leaf::handle_all( [ ] @@ -953,12 +953,12 @@ int main() leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 1; @@ -966,7 +966,7 @@ int main() BOOST_TEST(r); return r; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 2; }, @@ -977,7 +977,7 @@ int main() BOOST_TEST(r==1); } - //int, handle_some (failure, initially matched), match value (multiple values) + // int, handle_some (failure, initially matched), match value (multiple values) { int r = leaf::handle_all( [ ] @@ -985,12 +985,12 @@ int main() leaf::result r = leaf::handle_some( [ ]() -> leaf::result { - LEAF_AUTO(answer, f(error_codes::error1)); + LEAF_AUTO(answer, f(error_code::error1)); return answer; }, - [ ]( leaf::match ec, info<1> const & x, info<2> y ) + [ ]( leaf::match ec, info<1> const & x, info<2> y ) { - BOOST_TEST(ec.value==error_codes::error1); + BOOST_TEST(ec.value==error_code::error1); BOOST_TEST(x.value==1); BOOST_TEST(y.value==2); return 1; @@ -998,7 +998,7 @@ int main() BOOST_TEST(r); return r; }, - [ ]( leaf::match ) + [ ]( leaf::match ) { return 2; }, diff --git a/test/is_error_type_fail_test.cpp b/test/is_error_type_fail_test.cpp index 6cc9bf7..f4de1a3 100644 --- a/test/is_error_type_fail_test.cpp +++ b/test/is_error_type_fail_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include @@ -12,6 +12,6 @@ struct no_member_value { }; leaf::error f() { - //Note: the line below should trigger a compile error (via static_assert). + // Note: the line below should trigger a compile error (via static_assert). return leaf::new_error( no_member_value{ } ); } diff --git a/test/is_error_type_test.cpp b/test/is_error_type_test.cpp index b44ab1b..a779cdc 100644 --- a/test/is_error_type_test.cpp +++ b/test/is_error_type_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include @@ -12,16 +12,16 @@ struct t2 { }; struct t3 { }; namespace boost { namespace leaf { - template <> struct is_error_type { static constexpr bool value = true; }; + template <> struct is_e_type { static constexpr bool value = true; }; } } namespace leaf = boost::leaf; int main() { - static_assert(leaf::is_error_type::value,"t0"); - static_assert(!leaf::is_error_type::value,"t1"); - static_assert(!leaf::is_error_type::value,"t2"); - static_assert(leaf::is_error_type::value,"t3"); + static_assert(leaf::is_e_type::value,"t0"); + static_assert(!leaf::is_e_type::value,"t1"); + static_assert(!leaf::is_e_type::value,"t2"); + static_assert(leaf::is_e_type::value,"t3"); return 0; } diff --git a/test/multiple_errors_test.cpp b/test/multiple_errors_test.cpp index c7d74d9..30eb294 100644 --- a/test/multiple_errors_test.cpp +++ b/test/multiple_errors_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include "boost/core/lightweight_test.hpp" diff --git a/test/optional_test.cpp b/test/optional_test.cpp index a3b7d0d..fda9c9b 100644 --- a/test/optional_test.cpp +++ b/test/optional_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include "boost/core/lightweight_test.hpp" diff --git a/test/preload_basic_test.cpp b/test/preload_basic_test.cpp index 8f4e160..200c80c 100644 --- a/test/preload_basic_test.cpp +++ b/test/preload_basic_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/preload_nested_error_exception_test.cpp b/test/preload_nested_error_exception_test.cpp index e2db1d1..435848d 100644 --- a/test/preload_nested_error_exception_test.cpp +++ b/test/preload_nested_error_exception_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/preload_nested_error_result_test.cpp b/test/preload_nested_error_result_test.cpp index a7f8304..97dea1e 100644 --- a/test/preload_nested_error_result_test.cpp +++ b/test/preload_nested_error_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/preload_nested_new_error_exception_test.cpp b/test/preload_nested_new_error_exception_test.cpp index 806bc8d..0b871e0 100644 --- a/test/preload_nested_new_error_exception_test.cpp +++ b/test/preload_nested_new_error_exception_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/preload_nested_new_error_result_test.cpp b/test/preload_nested_new_error_result_test.cpp index 6ca24ad..7ece7e0 100644 --- a/test/preload_nested_new_error_result_test.cpp +++ b/test/preload_nested_new_error_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/preload_nested_success_exception_test.cpp b/test/preload_nested_success_exception_test.cpp index 4545040..4ff1f19 100644 --- a/test/preload_nested_success_exception_test.cpp +++ b/test/preload_nested_success_exception_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/preload_nested_success_result_test.cpp b/test/preload_nested_success_result_test.cpp index ee5dce6..5a64739 100644 --- a/test/preload_nested_success_result_test.cpp +++ b/test/preload_nested_success_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/print_test.cpp b/test/print_test.cpp index 3bd2f9b..867b8d0 100644 --- a/test/print_test.cpp +++ b/test/print_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include "boost/core/lightweight_test.hpp" diff --git a/test/result_bad_result_test.cpp b/test/result_bad_result_test.cpp index 7520f48..2b63db8 100644 --- a/test/result_bad_result_test.cpp +++ b/test/result_bad_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/result_state_test.cpp b/test/result_state_test.cpp index 6b16f94..7b366e1 100644 --- a/test/result_state_test.cpp +++ b/test/result_state_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -91,7 +91,7 @@ int main() using leaf::leaf_detail::static_store; using leaf::capture_result; - { //value default -> copy + { // value default -> copy leaf::result r1; BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -104,7 +104,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value move -> copy + { // value move -> copy leaf::result r1 = val(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -117,7 +117,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value copy -> copy + { // value copy -> copy val v; leaf::result r1 = v; BOOST_TEST(r1); @@ -132,7 +132,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value default -> move + { // value default -> move leaf::result r1; BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -144,7 +144,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value move -> move + { // value move -> move leaf::result r1 = val(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -156,7 +156,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value copy -> move + { // value copy -> move val v; leaf::result r1 = v; BOOST_TEST(r1); @@ -170,7 +170,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value default -> assign-copy + { // value default -> assign-copy leaf::result r1; BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -183,7 +183,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value move -> assign-copy + { // value move -> assign-copy leaf::result r1 = val(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -196,7 +196,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value copy -> assign-copy + { // value copy -> assign-copy val v; leaf::result r1 = v; BOOST_TEST(r1); @@ -211,7 +211,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value default -> assign-move + { // value default -> assign-move leaf::result r1; BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -223,7 +223,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value move -> assign-move + { // value move -> assign-move leaf::result r1 = val(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -235,7 +235,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value copy -> assign-move + { // value copy -> assign-move val v; leaf::result r1 = v; BOOST_TEST(r1); @@ -249,7 +249,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value default -> capture -> copy + { // value default -> capture -> copy leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -262,7 +262,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value move -> capture -> copy + { // value move -> capture -> copy leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -275,7 +275,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value copy -> capture -> copy + { // value copy -> capture -> copy leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -289,7 +289,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value default -> capture -> move + { // value default -> capture -> move leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -301,7 +301,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value move -> capture -> move + { // value move -> capture -> move leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -313,7 +313,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value copy -> capture -> move + { // value copy -> capture -> move leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -326,7 +326,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value default -> capture -> assign-copy + { // value default -> capture -> assign-copy leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -339,7 +339,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value move -> capture -> assign-copy + { // value move -> capture -> assign-copy leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -352,7 +352,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value copy -> capture -> assign-copy + { // value copy -> capture -> assign-copy leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -366,7 +366,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value default -> capture -> assign-move + { // value default -> capture -> assign-move leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -378,7 +378,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value move -> capture -> assign-move + { // value move -> capture -> assign-move leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -390,7 +390,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //value copy -> capture -> assign-move + { // value copy -> capture -> assign-move leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); BOOST_TEST(r1); BOOST_TEST(err::count==0); @@ -406,7 +406,7 @@ int main() // ^^ value ^^ // vv error vv - { //error move -> copy + { // error move -> copy static_store exp; exp.set_reset(true); leaf::result r1 = leaf::new_error( e_err { } ); @@ -419,7 +419,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error copy -> copy + { // error copy -> copy static_store exp; exp.set_reset(true); leaf::error e = leaf::new_error( e_err{ } ); @@ -434,7 +434,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error move -> move + { // error move -> move static_store exp; exp.set_reset(true); leaf::result r1 = leaf::new_error( e_err { } ); @@ -447,7 +447,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error copy -> move + { // error copy -> move static_store exp; leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; @@ -461,7 +461,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error move -> assign copy + { // error move -> assign copy static_store exp; leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); @@ -473,7 +473,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error copy -> assign copy + { // error copy -> assign copy static_store exp; leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; @@ -487,7 +487,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error move -> assign move + { // error move -> assign move static_store exp; leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); @@ -502,7 +502,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error copy -> assign move + { // error copy -> assign move static_store exp; leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; @@ -516,7 +516,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error move -> capture -> copy + { // error move -> capture -> copy leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -527,7 +527,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error copy -> capture -> copy + { // error copy -> capture -> copy leaf::result r1 = capture_result( [ ] { leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -539,7 +539,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error move -> capture -> move + { // error move -> capture -> move leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -550,7 +550,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error copy -> capture -> move + { // error copy -> capture -> move leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -562,7 +562,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error move -> capture -> assign-copy + { // error move -> capture -> assign-copy leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -573,7 +573,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error copy -> capture -> assign-copy + { // error copy -> capture -> assign-copy leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -585,7 +585,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error move -> capture -> assign-move + { // error move -> capture -> assign-move leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -596,7 +596,7 @@ int main() } BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); - { //error copy -> capture -> assign-move + { // error copy -> capture -> assign-move leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -614,56 +614,56 @@ int main() // vv result vv - { //void default -> copy + { // void default -> copy leaf::result r1; BOOST_TEST(r1); leaf::result r2 = r1; BOOST_TEST(r2); } - { //void default -> move + { // void default -> move leaf::result r1; BOOST_TEST(r1); leaf::result r2 = std::move(r1); BOOST_TEST(r2); } - { //void default -> assign-copy + { // void default -> assign-copy leaf::result r1; BOOST_TEST(r1); leaf::result r2; r2=r1; BOOST_TEST(r2); } - { //void default -> assign-move + { // void default -> assign-move leaf::result r1; BOOST_TEST(r1); leaf::result r2; r2=std::move(r1); BOOST_TEST(r2); } - { //void default -> capture -> copy + { // void default -> capture -> copy leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); BOOST_TEST(r1); leaf::result r2 = r1; BOOST_TEST(r2); } - { //void default -> capture -> move + { // void default -> capture -> move leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); BOOST_TEST(r1); leaf::result r2 = std::move(r1); BOOST_TEST(r2); } - { //void default -> capture -> assign-copy + { // void default -> capture -> assign-copy leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); BOOST_TEST(r1); leaf::result r2; r2=r1; BOOST_TEST(r2); } - { //void default -> capture -> assign-move + { // void default -> capture -> assign-move leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); BOOST_TEST(r1); leaf::result r2; r2=std::move(r1); @@ -673,7 +673,7 @@ int main() // ^^ void default ^^ // vv void error vv - { //void error move -> copy + { // void error move -> copy static_store exp; exp.set_reset(true); leaf::result r1 = leaf::new_error( e_err { } ); @@ -684,7 +684,7 @@ int main() BOOST_TEST(r1.error()==r2.error()); } BOOST_TEST(err::count==0); - { //void error copy -> copy + { // void error copy -> copy static_store exp; exp.set_reset(true); leaf::error e = leaf::new_error( e_err{ } ); @@ -697,7 +697,7 @@ int main() } BOOST_TEST(err::count==0); - { //void error move -> move + { // void error move -> move static_store exp; exp.set_reset(true); leaf::result r1 = leaf::new_error( e_err { } ); @@ -708,7 +708,7 @@ int main() BOOST_TEST(r1.error()==r2.error()); } BOOST_TEST(err::count==0); - { //void error copy -> move + { // void error copy -> move static_store exp; leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; @@ -720,7 +720,7 @@ int main() } BOOST_TEST(err::count==0); - { //void error move -> assign copy + { // void error move -> assign copy static_store exp; leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); @@ -730,7 +730,7 @@ int main() BOOST_TEST(r1.error()==r2.error()); } BOOST_TEST(err::count==0); - { //void error copy -> assign copy + { // void error copy -> assign copy static_store exp; leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; @@ -742,7 +742,7 @@ int main() } BOOST_TEST(err::count==0); - { //void error move -> assign move + { // void error move -> assign move static_store exp; leaf::result r1 = leaf::new_error( e_err { } ); BOOST_TEST(!r1); @@ -754,7 +754,7 @@ int main() BOOST_TEST(err::count==1); } BOOST_TEST(err::count==0); - { //void error copy -> assign move + { // void error copy -> assign move static_store exp; leaf::error e = leaf::new_error( e_err{ } ); leaf::result r1 = e; @@ -766,7 +766,7 @@ int main() } BOOST_TEST(err::count==0); - { //void error move -> capture -> copy + { // void error move -> capture -> copy leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -775,7 +775,7 @@ int main() BOOST_TEST(r1.error()==r2.error()); } BOOST_TEST(err::count==0); - { //void error copy -> capture -> copy + { // void error copy -> capture -> copy leaf::result r1 = capture_result( [ ] { leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -785,7 +785,7 @@ int main() } BOOST_TEST(err::count==0); - { //void error move -> capture -> move + { // void error move -> capture -> move leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -794,7 +794,7 @@ int main() BOOST_TEST(r1.error()==r2.error()); } BOOST_TEST(err::count==0); - { //void error copy -> capture -> move + { // void error copy -> capture -> move leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -804,7 +804,7 @@ int main() } BOOST_TEST(err::count==0); - { //void error move -> capture -> assign-copy + { // void error move -> capture -> assign-copy leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -813,7 +813,7 @@ int main() BOOST_TEST(r1.error()==r2.error()); } BOOST_TEST(err::count==0); - { //void error copy -> capture -> assign-copy + { // void error copy -> capture -> assign-copy leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -823,7 +823,7 @@ int main() } BOOST_TEST(err::count==0); - { //void error move -> capture -> assign-move + { // void error move -> capture -> assign-move leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -832,7 +832,7 @@ int main() BOOST_TEST(r1.error()==r2.error()); } BOOST_TEST(err::count==0); - { //void error copy -> capture -> assign-move + { // void error copy -> capture -> assign-move leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); diff --git a/test/static_store_deduction_test.cpp b/test/static_store_deduction_test.cpp index e255c0f..4f4e202 100644 --- a/test/static_store_deduction_test.cpp +++ b/test/static_store_deduction_test.cpp @@ -1,15 +1,15 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include namespace leaf = boost::leaf; -template -typename leaf::leaf_detail::deduce_static_store::type>::type * expd( Handlers && ... ) +template +typename leaf::leaf_detail::deduce_static_store::type>::type * expd( Handler && ... ) { return 0; } @@ -30,7 +30,7 @@ enum class my_error_code error3 }; namespace boost { namespace leaf { - template <> struct is_error_type: std::true_type { }; + template <> struct is_e_type: std::true_type { }; } } void not_called_on_purpose() diff --git a/test/static_store_test.cpp b/test/static_store_test.cpp index 6e2c09a..a8b325f 100644 --- a/test/static_store_test.cpp +++ b/test/static_store_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include "boost/core/lightweight_test.hpp" diff --git a/test/try_exception_test.cpp b/test/try_exception_test.cpp index 47629b1..c80635c 100644 --- a/test/try_exception_test.cpp +++ b/test/try_exception_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include diff --git a/test/try_test.cpp b/test/try_test.cpp index 735fe91..0e861ed 100644 --- a/test/try_test.cpp +++ b/test/try_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// 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) +// 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) #include #include @@ -30,7 +30,7 @@ R f() int main() { - //void, handle_all (success) + // void, handle_all (success) { int c=0; leaf::try_( @@ -46,7 +46,7 @@ int main() BOOST_TEST(c==42); } - //void, handle_all (failure), match_enum (single enum value) + // void, handle_all (failure), match_enum (single enum value) { int c=0; leaf::try_( @@ -74,7 +74,7 @@ int main() BOOST_TEST(c==2); } - //void, handle_all (failure), match_enum (multiple enum values) + // void, handle_all (failure), match_enum (multiple enum values) { int c=0; leaf::try_( @@ -102,7 +102,7 @@ int main() BOOST_TEST(c==2); } - //void, handle_all (failure), match_value (single value) + // void, handle_all (failure), match_value (single value) { int c=0; leaf::try_( @@ -130,7 +130,7 @@ int main() BOOST_TEST(c==2); } - //void, handle_all (failure), match_value (multiple values) + // void, handle_all (failure), match_value (multiple values) { int c=0; leaf::try_( @@ -160,7 +160,7 @@ int main() ////////////////////////////////////// - //void, handle_some (failure, initially not matched), match_enum (single enum value) + // void, handle_some (failure, initially not matched), match_enum (single enum value) { int c=0; leaf::try_( @@ -193,7 +193,7 @@ int main() BOOST_TEST(c==2); } - //void, handle_some (failure, initially not matched), match_enum (multiple enum values) + // void, handle_some (failure, initially not matched), match_enum (multiple enum values) { int c=0; leaf::try_( @@ -226,7 +226,7 @@ int main() BOOST_TEST(c==2); } - //void, handle_some (failure, initially matched), match_enum (single enum value) + // void, handle_some (failure, initially matched), match_enum (single enum value) { int c=0; leaf::try_( @@ -258,7 +258,7 @@ int main() BOOST_TEST(c==1); } - //void, handle_some (failure, initially matched), match_enum (multiple enum values) + // void, handle_some (failure, initially matched), match_enum (multiple enum values) { int c=0; leaf::try_( @@ -292,7 +292,7 @@ int main() ////////////////////////////////////// - //int, handle_all (success) + // int, handle_all (success) { int r = leaf::try_( [ ] @@ -306,7 +306,7 @@ int main() BOOST_TEST(r==42); } - //int, handle_all (failure), match_enum (single enum value) + // int, handle_all (failure), match_enum (single enum value) { int r = leaf::try_( [ ] @@ -330,7 +330,7 @@ int main() BOOST_TEST(r==2); } - //int, handle_all (failure), match_enum (multiple enum values) + // int, handle_all (failure), match_enum (multiple enum values) { int r = leaf::try_( [ ] @@ -356,7 +356,7 @@ int main() ////////////////////////////////////// - //int, handle_some (failure, matched), match_enum (single enum value) + // int, handle_some (failure, matched), match_enum (single enum value) { int r = leaf::try_( [ ] @@ -376,7 +376,7 @@ int main() BOOST_TEST(r==2); } - //int, handle_some (failure, matched), match_enum (multiple enum values) + // int, handle_some (failure, matched), match_enum (multiple enum values) { int r = leaf::try_( [ ] @@ -396,7 +396,7 @@ int main() BOOST_TEST(r==2); } - //int, handle_some (failure, initially not matched), match_enum (single enum value) + // int, handle_some (failure, initially not matched), match_enum (single enum value) { int r = leaf::try_( [ ] @@ -426,7 +426,7 @@ int main() BOOST_TEST(r==2); } - //int, handle_some (failure, initially not matched), match_enum (multiple enum values) + // int, handle_some (failure, initially not matched), match_enum (multiple enum values) { int r = leaf::try_( [ ] @@ -456,7 +456,7 @@ int main() BOOST_TEST(r==2); } - //int, handle_some (failure, initially matched), match_enum (single enum value) + // int, handle_some (failure, initially matched), match_enum (single enum value) { int r = leaf::try_( [ ] @@ -486,7 +486,7 @@ int main() BOOST_TEST(r==1); } - //int, handle_some (failure, initially matched), match_enum (multiple enum values) + // int, handle_some (failure, initially matched), match_enum (multiple enum values) { int r = leaf::try_( [ ] From b869dc2fa3b220d039317fd4bec1d3a485d08ba8 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Sun, 13 Jan 2019 13:01:45 -0800 Subject: [PATCH 22/34] diagnostic info changes --- doc/leaf.adoc | 96 ++++++- include/boost/leaf/detail/static_store.hpp | 23 +- include/boost/leaf/error.hpp | 294 ++++++++++++--------- test/diagnostic_info_test.cpp | 88 +++--- 4 files changed, 319 insertions(+), 182 deletions(-) diff --git a/doc/leaf.adoc b/doc/leaf.adoc index 605fb38..134f636 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -591,6 +591,90 @@ TIP: This automatically-generated diagnostic messages are developer-friendly, bu ''' +[[error_info]] +=== `error_info` + +[source,c++] +---- +namespace boost { namespace leaf { + + class error_info + { + //Constructors unspecified + + public: + + leaf::error const & get_error() const noexcept; + + bool exception_caught() const noexcept; + + std::exception const * get_exception() const noexcept; + + friend std::ostream & operator<<( std::ostream & os, error_info const & x ); + }; + +} } +---- + +Handlers passed to <>, <> or <> may take an argument of type `error_info const &` to receive information about the error. + +The `get_error` member function returns a program-wide unique identifier of the <>. + +The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` otherwise. + +The `get_exception` member function returns a pointer to the `std::exception` subobject of the exception caught by `try_`, or `0` if that exception could not be converted to `std::exception`. It is illegal to call `get_exception` unless `exception_caught()` is `true`. + +The `operator<<` overload prints diagnostic information about all E-objects, associated with the <> value returned by `get_error()`, currently stored in any active <>, <> or <> scope. + +''' + +[[verbose_diagnostic_info]] +=== `verbose_diagnostic_info` + +[source,c++] +---- +namespace boost { namespace leaf { + + class verbose_diagnostic_info + { + //Constructors unspecified + + public: + + friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x ); + }; + +} } +---- + +Handlers passed to <>, <> or <> may take an argument of type `verbose_diagnostic_info const &` to be able to print information about the error. + +The `get_error` member function returns a program-wide unique identifier of the <>. + +The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` otherwise. + +The `get_exception` member function returns a pointer to the `std::exception` subobject of the exception caught by `try_`, or `0` if that exception could not be converted to `std::exception`. It is illegal to call `get_exception` unless `exception_caught()` is `true`. + +The `operator<<` overload prints diagnostic information about all E-objects, associated with the <> value returned by `get_error()`, currently stored in any active <>, <> or <> scope. + +''' + +[[unexpected_error_info]] +=== `unexpected_error_info` + +[source,c++] +---- +namespace boost { namespace leaf { + + class unexpected_error_info; + +} } +---- + +The members of the `unexpected_error_info` type are not specified. If a handler passed to <>, <> or <> takes an argument of type `unexpected_error_info const &`, the diagnostic information output when printing an <> object will include information about any E-objects communicated to LEAF, for which there was no storage reserved in any active <> or <> or <> scope. + +''' + [[handle_some]] === `handle_some` @@ -652,17 +736,17 @@ If the value `handle_some` is returning indicates success, all stored E-objects Handler Matching Procedure: :: + -A `handler` matches the reported failure iff `handle_some` is able to produce values to pass as its arguments. As soon as it is determined that an argument value can not be produced, the current handler is dropped and the matching procedure continues with the next handler, if any. +A `handler` matches the reported failure iff `handle_some` is able to produce values to pass as its arguments. As soon as it is determined that an argument value can not be produced, the current `handler` is dropped and the matching procedure continues with the next `handler`, if any. + -Each argument value `a`~i~ to be passed to the handler currently under consideration is produced as follows: +If `e` is the specific <> value stored in the `result` returned by the `try_block`, each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced as follows: + -- * If `a`~i~ is of type `error_info const &` or `unexpected_error_info const &` or `verbose_diagnostic_info const &`, `handle_some` is always able to produce it. * If `a`~i~ is of type `A`~i~ `const &`: -** If an E-object of type `A`~i~ is currently stored in the `handle_some` scope *and* is associated with the specific `<>` value stored in `r`, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. -** If `A`~i~ is of the predicate type `<>`, if an object of type `E` is currently stored in the `handle_some` scope *and* is associated with the specific `error` value stored in `r`, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. The handler is also dropped if the expression `a`~i~`()` evaluates to `false`. -* If `a`~i~ is of type `A`~i~ `const pass:[*]`, `handle_some` is always able to produce it: if an E-object of type `A`~i~ is currently stored in the `handle_some` scope *and* is associated with the specific `<>` value stored in `r`, `a`~i~ is initialized with the address of the stored object, otherwise it is initialized with `0`. -* It is illegal to pass to `handle_some` handlers that take any other argument types. +** If an E-object of type `A`~i~, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. +** If `A`~i~ is of the predicate type `<>`, if an object of type `E`, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. The handler is also dropped if the expression `a`~i~`()` evaluates to `false`. +* If `a`~i~ is of type `A`~i~ `const pass:[*]`, `handle_some` is always able to produce it: if an E-object of type `A`~i~, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with the address of the stored object, otherwise it is initialized with `0`. +* It is illegal to pass to `handle_some` a `handler` that takes any other argument types. -- TIP: Because `handle_some` can always produce arguments of type `error_info const &`, `unexpected_error_info const &` and `verbose_diagnostic_info const &`, if a handler only takes arguments of these types, it will match any error. diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index 2f0d0fc..8f3af84 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -328,16 +328,29 @@ namespace boost { namespace leaf { } }; + template <> + struct get_one_argument + { + template + static unexpected_error_info const & get( StaticStore const & ss, error_info const & ei ) noexcept + { + unexpected_error_info const * uei = ss.template peek(ei.get_error()); + assert(uei!=0); + uei->set_error_info(ei); + return *uei; + } + }; + template <> struct get_one_argument { template static verbose_diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept { - verbose_diagnostic_info const * cdi = ss.template peek(ei.get_error()); - assert(cdi!=0); - cdi->set_error_info(ei); - return *cdi; + verbose_diagnostic_info const * vdi = ss.template peek(ei.get_error()); + assert(vdi!=0); + vdi->set_error_info(ei); + return *vdi; } }; @@ -431,7 +444,7 @@ namespace boost { namespace leaf { { using namespace static_store_internal; static_assert(ensure_last_handler_matches::mp_args>::value, - "The last handler for handle_all may only take arguments of type error_info const &, verbose_diagnostic_info const &, unexpected_error_info const &, or any number of pointer-to-const arguments."); + "The last handler for handle_all must match any error."); return call_handler( ei, std::forward(f), typename function_traits::mp_args{ } ); } diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 6913d79..3f51f2c 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -22,69 +22,6 @@ namespace boost { namespace system { class error_code; } } namespace boost { namespace leaf { - struct e_source_location - { - char const * const file; - int const line; - char const * const function; - - friend std::ostream & operator<<( std::ostream & os, e_source_location const & x ) - { - return os << leaf::type() << ": " << x.file << '(' << x.line << ") in function " << x.function; - } - }; - - struct unexpected_error_info - { - char const * (*first_type)() noexcept; - int count; - - friend std::ostream & operator<<( std::ostream & os, unexpected_error_info const & x ) - { - assert(x.first_type!=0); - assert(x.count>0); - if( x.count==1 ) - os << "Detected 1 attempt to communicate an unexpected error object of type "; - else - os << "Detected " << x.count << " attempts to communicate unexpected error objects, the first one of type "; - return os << x.first_type(); - } - }; - - namespace leaf_detail - { - template - struct has_data_member_value - { - static constexpr bool value=false; - }; - - template - struct has_data_member_value().value, void())> - { - static constexpr bool value=std::is_member_object_pointer::value; - }; - - template - struct is_error_type_default - { - static constexpr bool value = has_data_member_value::value || std::is_base_of::value; - }; - - template <> struct is_error_type_default: std::true_type { }; - template <> struct is_error_type_default: std::true_type { }; - template <> struct is_error_type_default: std::true_type { }; - template <> struct is_error_type_default: std::true_type { }; - template <> struct is_error_type_default: std::true_type { }; - } - - template - struct is_e_type: leaf_detail::is_error_type_default - { - }; - - //////////////////////////////////////// - class error; error next_error_value() noexcept; @@ -197,36 +134,6 @@ namespace boost { namespace leaf { namespace leaf_detail { - template - struct ev_type - { - error e; - E v; - - explicit ev_type( error const & e ) noexcept: - e(e) - { - } - - ev_type( error const & e, E const & v ): - e(e), - v(v) - { - } - - ev_type( error const & e, E && v ) noexcept: - e(e), - v(std::forward(v)) - { - } - }; - - inline int & tl_unexpected_enabled_counter() noexcept - { - static thread_local int c; - return c; - } - class slot_base { slot_base( slot_base const & ) = delete; @@ -336,28 +243,146 @@ namespace boost { namespace leaf { } }; - class verbose_diagnostic_info + //////////////////////////////////////// + + namespace leaf_detail + { + template + struct has_data_member_value + { + static constexpr bool value=false; + }; + + template + struct has_data_member_value().value, void())> + { + static constexpr bool value=std::is_member_object_pointer::value; + }; + + template + struct is_error_type_default + { + static constexpr bool value = has_data_member_value::value || std::is_base_of::value; + }; + + template <> struct is_error_type_default: std::true_type { }; + template <> struct is_error_type_default: std::true_type { }; + template <> struct is_error_type_default: std::true_type { }; + } + + template + struct is_e_type: leaf_detail::is_error_type_default + { + }; + + //////////////////////////////////////// + + struct e_source_location + { + char const * const file; + int const line; + char const * const function; + + friend std::ostream & operator<<( std::ostream & os, e_source_location const & x ) + { + return os << leaf::type() << ": " << x.file << '(' << x.line << ") in function " << x.function; + } + }; + + namespace leaf_detail + { + template <> struct is_error_type_default: std::true_type { }; + } + + //////////////////////////////////////// + + namespace leaf_detail + { + class monitor_base + { + monitor_base( monitor_base const & ) = delete; + monitor_base & operator=( monitor_base const & ) = delete; + + mutable error_info const * ei_; + + protected: + + monitor_base() noexcept: + ei_(0) + { + } + + monitor_base( monitor_base && x ) noexcept: + ei_(0) + { + x.ei_ = 0; + } + + void set_error_info( error_info const & ei ) const noexcept + { + ei_ = &ei; + } + + error_info const & get_error_info() const noexcept + { + assert(ei_!=0); + return *ei_; + } + }; + } + + class unexpected_error_info: leaf_detail::monitor_base + { + public: + + char const * (*first_type)(); + int count; + + explicit unexpected_error_info( char const * (*first_type)() ) noexcept: + first_type(first_type), + count(1) + { + } + + using monitor_base::set_error_info; + + friend std::ostream & operator<<( std::ostream & os, unexpected_error_info const & x ) + { + assert(x.first_type!=0); + assert(x.count>0); + os << x.get_error_info() << "Detected "; + if( x.count==1 ) + os << "1 attempt to communicate an E-object"; + else + os << x.count << " attempts to communicate unexpected E-objects, the first one"; + return os << " of type " << x.first_type() << std::endl; + } + }; + + namespace leaf_detail + { + template <> struct is_error_type_default: std::true_type { }; + + template <> + struct diagnostic + { + static bool print( std::ostream & os, unexpected_error_info const & ) noexcept + { + return false; + } + }; + } + + class verbose_diagnostic_info: leaf_detail::monitor_base { - verbose_diagnostic_info( verbose_diagnostic_info const & ) = delete; - verbose_diagnostic_info & operator=( verbose_diagnostic_info const & ) = delete; - mutable error_info const * ei_; std::string value_; std::set already_; public: - verbose_diagnostic_info() noexcept: - ei_(0) - { - } + verbose_diagnostic_info() noexcept = default; - verbose_diagnostic_info( verbose_diagnostic_info && x ) noexcept: - ei_(0), - value_(std::move(x.value_)), - already_(std::move(x.already_)) - { - x.ei_ = 0; - } + using monitor_base::set_error_info; void reset() noexcept { @@ -370,31 +395,21 @@ namespace boost { namespace leaf { { if( already_.insert(&type).second ) { - value_ += '\n'; value_ += s.str(); - value_ += " {unexpected}"; + value_ += " {unexpected}\n"; } } - void set_error_info( error_info const & ei ) const noexcept - { - ei_ = &ei; - } - friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x ) { - os << *x.ei_; - if( !x.value_.empty() ) - os << x.value_ << std::endl; + os << x.get_error_info() << x.value_; return os; } }; namespace leaf_detail { - template <> struct is_error_type_default: std::true_type - { - }; + template <> struct is_error_type_default: std::true_type { }; template <> struct diagnostic @@ -408,6 +423,41 @@ namespace boost { namespace leaf { //////////////////////////////////////// + namespace leaf_detail + { + template + struct ev_type + { + error e; + E v; + + explicit ev_type( error const & e ) noexcept: + e(e) + { + } + + ev_type( error const & e, E const & v ): + e(e), + v(v) + { + } + + ev_type( error const & e, E && v ) noexcept: + e(e), + v(std::forward(v)) + { + } + }; + + inline int & tl_unexpected_enabled_counter() noexcept + { + static thread_local int c; + return c; + } + } + + //////////////////////////////////////// + namespace leaf_detail { template @@ -458,7 +508,7 @@ namespace boost { namespace leaf { return; } } - (void) p->put( ev_type(ev.e,unexpected_error_info{&type,1}) ); + (void) p->put( ev_type(ev.e,unexpected_error_info(&type)) ); } } diff --git a/test/diagnostic_info_test.cpp b/test/diagnostic_info_test.cpp index 529fee0..c6499ca 100644 --- a/test/diagnostic_info_test.cpp +++ b/test/diagnostic_info_test.cpp @@ -90,7 +90,6 @@ int main() non_printable_info_printable_payload, non_printable_info_non_printable_payload, leaf::e_errno, - leaf::unexpected_error_info const &, leaf::error_info const & unmatched ) { std::ostringstream st; @@ -102,7 +101,44 @@ int main() BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); BOOST_TEST(s.find(") in function")!=s.npos); - BOOST_TEST(s.find("Detected 2 attempts to communicate unexpected error objects, the first one of type ")!=s.npos); + BOOST_TEST(s.find("unexpected")==s.npos); + std::cout << s; + } ); + + std::cout << std::endl; + + leaf::try_( + [ ] + { + LEAF_THROW( my_error(), + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + unexpected_test<1>{1}, + unexpected_test<2>{2}, + leaf::e_errno{ENOENT} ); + }, + [ ]( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + leaf::e_errno, + leaf::unexpected_error_info const & unmatched ) + { + std::ostringstream st; + st << unmatched; + std::string s = st.str(); + BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); + BOOST_TEST(s.find(": N/A")!=s.npos); + BOOST_TEST(s.find(": printed printable_payload")!=s.npos); + BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); + BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); + BOOST_TEST(s.find(") in function")!=s.npos); + BOOST_TEST(s.find("Detected 2 attempts")!=s.npos); + BOOST_TEST(s.find("unexpected_test<1>")!=s.npos); BOOST_TEST(s.find("unexpected_test<2>")==s.npos); std::cout << s; } ); @@ -128,7 +164,6 @@ int main() non_printable_info_printable_payload, non_printable_info_non_printable_payload, leaf::e_errno, - leaf::unexpected_error_info const &, leaf::verbose_diagnostic_info const & di ) { std::ostringstream st; @@ -140,57 +175,12 @@ int main() BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); BOOST_TEST(s.find(") in function")!=s.npos); - BOOST_TEST(s.find("Detected 2 attempts to communicate unexpected error objects, the first one of type ")!=s.npos); + BOOST_TEST(s.find("unexpected_test<1>")!=s.npos); BOOST_TEST(s.find("unexpected_test<2>")!=s.npos); std::cout << s; } ); std::cout << std::endl; - leaf::try_( - leaf::capture_exception< - printable_info_printable_payload, - printable_info_non_printable_payload, - non_printable_info_printable_payload, - non_printable_info_non_printable_payload, - unexpected_test<1>, - unexpected_test<2>, - leaf::e_errno>( - [ ] - { - LEAF_THROW( my_error(), - printable_info_printable_payload(), - printable_info_non_printable_payload(), - non_printable_info_printable_payload(), - non_printable_info_non_printable_payload(), - unexpected_test<1>{1}, - unexpected_test<2>{2}, - leaf::e_errno{ENOENT} ); - } ), - [ ]( - leaf::e_source_location, - printable_info_printable_payload, - printable_info_non_printable_payload, - non_printable_info_printable_payload, - non_printable_info_non_printable_payload, - leaf::e_errno, - leaf::unexpected_error_info const &, - leaf::verbose_diagnostic_info const & di ) - { - std::ostringstream st; - st << di; - std::string s = st.str(); - BOOST_TEST(s.find("Detected exception_capture")!=s.npos); - BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); - BOOST_TEST(s.find(": N/A")!=s.npos); - BOOST_TEST(s.find(": printed printable_payload")!=s.npos); - BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); - BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); - BOOST_TEST(s.find(") in function")!=s.npos); - BOOST_TEST(s.find("Detected 2 attempts to communicate unexpected error objects, the first one of type ")!=s.npos); - BOOST_TEST(s.find("unexpected_test<2>")!=s.npos); - std::cout << s; - } ); - return boost::report_errors(); } From cee35291711b6d7c51974c30886872a1fcd5e2e6 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Sun, 13 Jan 2019 13:18:57 -0800 Subject: [PATCH 23/34] travis --- include/boost/leaf/error.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 3f51f2c..cc752ca 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -380,7 +380,9 @@ namespace boost { namespace leaf { public: - verbose_diagnostic_info() noexcept = default; + verbose_diagnostic_info() noexcept + { + } using monitor_base::set_error_info; From 3a4aee0b59fb0944a949ad266d520dacc64be52a Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Sun, 13 Jan 2019 13:47:50 -0800 Subject: [PATCH 24/34] diagnostic info tweaks --- doc/leaf.adoc | 63 ++++++++++++---------- include/boost/leaf/detail/static_store.hpp | 12 ++--- include/boost/leaf/error.hpp | 42 +++++++-------- test/diagnostic_info_test.cpp | 2 +- 4 files changed, 64 insertions(+), 55 deletions(-) diff --git a/doc/leaf.adoc b/doc/leaf.adoc index 134f636..1d3490b 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -618,7 +618,7 @@ namespace boost { namespace leaf { Handlers passed to <>, <> or <> may take an argument of type `error_info const &` to receive information about the error. -The `get_error` member function returns a program-wide unique identifier of the <>. +The `get_error` member function returns the program-wide unique identifier of the <>. The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` otherwise. @@ -628,6 +628,33 @@ The `operator<<` overload prints diagnostic information about all E-objects, ass ''' +[[diagnostic_info]] +=== `diagnostic_info` + +[source,c++] +---- +namespace boost { namespace leaf { + + class diagnostic_info + { + //Constructors unspecified + + public: + + friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x ); + }; + +} } +---- + +Handlers passed to <>, <> or <> may take an argument of type `diagnostic_info const &` if they need to print diagnostic information about the error. + +The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. + +The additional information includes the total count, as well as the type of the first such E-object. + +''' + [[verbose_diagnostic_info]] === `verbose_diagnostic_info` @@ -647,31 +674,13 @@ namespace boost { namespace leaf { } } ---- -Handlers passed to <>, <> or <> may take an argument of type `verbose_diagnostic_info const &` to be able to print information about the error. +Handlers passed to <>, <> or <> may take an argument of type `verbose_diagnostic_info const &` if they need to print diagnostic information about the error. -The `get_error` member function returns a program-wide unique identifier of the <>. +The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. -The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` otherwise. +The additional information includes the types and the values of all such E-objects. -The `get_exception` member function returns a pointer to the `std::exception` subobject of the exception caught by `try_`, or `0` if that exception could not be converted to `std::exception`. It is illegal to call `get_exception` unless `exception_caught()` is `true`. - -The `operator<<` overload prints diagnostic information about all E-objects, associated with the <> value returned by `get_error()`, currently stored in any active <>, <> or <> scope. - -''' - -[[unexpected_error_info]] -=== `unexpected_error_info` - -[source,c++] ----- -namespace boost { namespace leaf { - - class unexpected_error_info; - -} } ----- - -The members of the `unexpected_error_info` type are not specified. If a handler passed to <>, <> or <> takes an argument of type `unexpected_error_info const &`, the diagnostic information output when printing an <> object will include information about any E-objects communicated to LEAF, for which there was no storage reserved in any active <> or <> or <> scope. +WARNING: Using `verbose_diagnostic_info` may allocate memory dynamically. ''' @@ -700,7 +709,7 @@ Requirements: :: ** may take arguments of <>, either by value or by `const &`, or as a `const *`; ** may take arguments of the predicate type `<>`, where `E` is an E-type, either by value or by `const &`; ** may take an <> argument by `const &`; -** may take an <> argument by `const &`; +** may take an <> argument by `const &`; ** may take a <> argument by `const &`; ** may not take any other types of arguments. @@ -713,7 +722,7 @@ The list of E-types that `handle_some` needs to store internally is deduced auto -- * A type list is deduced by concatenating all argument types of all `handler...` functions; * References and cv-qualifiers are stripped from each type in the list; -* The <>, <>, and <> types are removed from the list; +* The <>, <>, and <> types are removed from the list; * Any duplicate types are removed. -- + @@ -741,7 +750,7 @@ A `handler` matches the reported failure iff `handle_some` is able to produce va If `e` is the specific <> value stored in the `result` returned by the `try_block`, each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced as follows: + -- -* If `a`~i~ is of type `error_info const &` or `unexpected_error_info const &` or `verbose_diagnostic_info const &`, `handle_some` is always able to produce it. +* If `a`~i~ is of type `error_info const &` or `diagnostic_info const &` or `verbose_diagnostic_info const &`, `handle_some` is always able to produce it. * If `a`~i~ is of type `A`~i~ `const &`: ** If an E-object of type `A`~i~, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. ** If `A`~i~ is of the predicate type `<>`, if an object of type `E`, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. The handler is also dropped if the expression `a`~i~`()` evaluates to `false`. @@ -749,7 +758,7 @@ If `e` is the specific <> value stored in the `result` returned by th * It is illegal to pass to `handle_some` a `handler` that takes any other argument types. -- -TIP: Because `handle_some` can always produce arguments of type `error_info const &`, `unexpected_error_info const &` and `verbose_diagnostic_info const &`, if a handler only takes arguments of these types, it will match any error. +TIP: Because `handle_some` can always produce arguments of type `error_info const &`, `diagnostic_info const &` and `verbose_diagnostic_info const &`, if a handler only takes arguments of these types, it will match any error. ''' diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index 8f3af84..4f492a9 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -152,8 +152,8 @@ namespace boost { namespace leaf { }; template <> - class static_store_slot: - public slot, + class static_store_slot: + public slot, enable_any { }; @@ -329,12 +329,12 @@ namespace boost { namespace leaf { }; template <> - struct get_one_argument + struct get_one_argument { template - static unexpected_error_info const & get( StaticStore const & ss, error_info const & ei ) noexcept + static diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept { - unexpected_error_info const * uei = ss.template peek(ei.get_error()); + diagnostic_info const * uei = ss.template peek(ei.get_error()); assert(uei!=0); uei->set_error_info(ei); return *uei; @@ -359,7 +359,7 @@ namespace boost { namespace leaf { template struct acceptable_last_handler_argument: std::false_type { }; template struct acceptable_last_handler_argument: is_e_type { }; template <> struct acceptable_last_handler_argument: std::true_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; + template <> struct acceptable_last_handler_argument: std::true_type { }; template <> struct acceptable_last_handler_argument: std::true_type { }; template diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index cc752ca..ce95ff6 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -331,14 +331,14 @@ namespace boost { namespace leaf { }; } - class unexpected_error_info: leaf_detail::monitor_base + class diagnostic_info: leaf_detail::monitor_base { public: char const * (*first_type)(); int count; - explicit unexpected_error_info( char const * (*first_type)() ) noexcept: + explicit diagnostic_info( char const * (*first_type)() ) noexcept: first_type(first_type), count(1) { @@ -346,11 +346,11 @@ namespace boost { namespace leaf { using monitor_base::set_error_info; - friend std::ostream & operator<<( std::ostream & os, unexpected_error_info const & x ) + friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x ) { assert(x.first_type!=0); assert(x.count>0); - os << x.get_error_info() << "Detected "; + os << x.get_error_info() << "diagnostic_info: Detected "; if( x.count==1 ) os << "1 attempt to communicate an E-object"; else @@ -361,12 +361,12 @@ namespace boost { namespace leaf { namespace leaf_detail { - template <> struct is_error_type_default: std::true_type { }; + template <> struct is_error_type_default: std::true_type { }; template <> - struct diagnostic + struct diagnostic { - static bool print( std::ostream & os, unexpected_error_info const & ) noexcept + static bool print( std::ostream & os, diagnostic_info const & ) noexcept { return false; } @@ -375,7 +375,7 @@ namespace boost { namespace leaf { class verbose_diagnostic_info: leaf_detail::monitor_base { - std::string value_; + std::stringstream s_; std::set already_; public: @@ -388,23 +388,26 @@ namespace boost { namespace leaf { void reset() noexcept { - value_.clear(); + std::stringstream().swap(s_); already_.clear(); } template - void add( std::stringstream const & s ) + void add( E const & e ) { - if( already_.insert(&type).second ) + if( already_.find(&type)==already_.end() && leaf_detail::diagnostic::print(s_,e) ) { - value_ += s.str(); - value_ += " {unexpected}\n"; + s_ << std::endl; + already_.insert(&type); } } friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x ) { - os << x.get_error_info() << x.value_; + os << + x.get_error_info() << + "verbose_diagnostic_info:" << std::endl << + x.s_.str(); return os; } }; @@ -499,7 +502,7 @@ namespace boost { namespace leaf { template void put_unexpected( ev_type const & ev ) noexcept { - if( slot * p = tl_slot_ptr() ) + if( slot * p = tl_slot_ptr() ) { if( p->has_value() ) { @@ -510,7 +513,7 @@ namespace boost { namespace leaf { return; } } - (void) p->put( ev_type(ev.e,unexpected_error_info(&type)) ); + (void) p->put( ev_type(ev.e,diagnostic_info(&type)) ); } } @@ -519,9 +522,6 @@ namespace boost { namespace leaf { { if( slot * sl = tl_slot_ptr() ) { - std::stringstream s; - if( !diagnostic::print(s,ev.v) ) - return; if( auto * pv = sl->has_value() ) { if( pv->e!=ev.e ) @@ -529,10 +529,10 @@ namespace boost { namespace leaf { pv->e = ev.e; pv->v.reset(); } - pv->v.add(s); + pv->v.add(ev.v); } else - sl->emplace(ev.e).v.template add(s); + sl->emplace(ev.e).v.add(ev.v); } } diff --git a/test/diagnostic_info_test.cpp b/test/diagnostic_info_test.cpp index c6499ca..d9aeb57 100644 --- a/test/diagnostic_info_test.cpp +++ b/test/diagnostic_info_test.cpp @@ -126,7 +126,7 @@ int main() non_printable_info_printable_payload, non_printable_info_non_printable_payload, leaf::e_errno, - leaf::unexpected_error_info const & unmatched ) + leaf::diagnostic_info const & unmatched ) { std::ostringstream st; st << unmatched; From 6362b5e2508d0655fce5647b6a3e19b8be7122f6 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Sun, 13 Jan 2019 14:20:41 -0800 Subject: [PATCH 25/34] travis --- include/boost/leaf/error.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index ce95ff6..339d3c2 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -388,7 +388,7 @@ namespace boost { namespace leaf { void reset() noexcept { - std::stringstream().swap(s_); + s_ = std::stringstream(); already_.clear(); } From bef90c5095bebb54f37814fcf653be01880bd3c9 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Sun, 13 Jan 2019 15:14:07 -0800 Subject: [PATCH 26/34] travis --- include/boost/leaf/error.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 339d3c2..a487cdf 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -388,7 +388,7 @@ namespace boost { namespace leaf { void reset() noexcept { - s_ = std::stringstream(); + s_.str(std::string()); already_.clear(); } From bb9ba95e17b2aff1d94b5115688c0b79adbb5ed7 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Sun, 13 Jan 2019 16:34:40 -0800 Subject: [PATCH 27/34] travis --- doc/leaf.adoc | 1062 ++++++++-------------- doc/synopses/capture_result.adoc | 4 - doc/synopses/error.adoc | 18 +- doc/synopses/handle.adoc | 2 +- doc/synopses/result.adoc | 14 +- example/lua_callback_result.cpp | 2 +- include/boost/leaf/capture_exception.hpp | 2 +- include/boost/leaf/error.hpp | 29 +- include/boost/leaf/preload.hpp | 12 +- include/boost/leaf/try.hpp | 6 +- 10 files changed, 426 insertions(+), 725 deletions(-) diff --git a/doc/leaf.adoc b/doc/leaf.adoc index 1d3490b..0327aa3 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -511,6 +511,9 @@ include::{sourcedir}/synopses/common.adoc[] === `capture_result.hpp` include::{sourcedir}/synopses/capture_result.adoc[] +[.text-right] +<> + ''' === `capture_exception.hpp` @@ -591,6 +594,312 @@ TIP: This automatically-generated diagnostic messages are developer-friendly, bu ''' +[[error]] +=== `error` + +include::{sourcedir}/synopses/error.adoc[] + +Objects of class `error` are values that identify an error across the entire program. They can be copied, moved, assigned to, and compared to other `error` objects. They occupy as much memory, and are as efficient as `unsigned int`. + +''' + +[[new_error]] +==== `new_error` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + error new_error( E && ... e ) noexcept; + +} } +---- + +Requirements: :: `<>::value` must be `true` for each `E`. + +Effects: :: Each of the `e...` objects is either moved into the corresponding storage inside an active `handle_some`, `handle_all` or `try_` scope (where it is uniquely associated with `*this`), or discarded. See <>. + +Returns: :: A new `error` value, which is unique across the entire program. + +''' + +[[next_error]] +==== `next_error` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + error next_error() noexcept; + +} } +---- + +Returns: :: The `error` value which will be returned the next time <> is invoked from the calling thread. ++ +This function can be used to associate E-objects with the next `error` value to be reported. Use with caution, only when restricted to reporting errors via specific third-party types, incompatible with LEAF -- for example when reporting an error from a C callback. As soon as control exits this critical path, you should create a <> (which will be equal to the `error` object returned by the earlier call to `next_error`). + +TIP: `error` values are unique across the entire program. + +''' + +[[last_error]] +==== `last_error` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + error last_error() noexcept; + +} } +---- + +Returns: :: The `error` value returned the last time <> was invoked from the calling thread. + +TIP: `error` values are unique across the entire program. + +''' + +[[propagate]] +==== `propagate` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + error error::propagate( E && ... e ) const noexcept; + +} } +---- + +Effects: :: Each of the `e...` objects is either moved into the corresponding storage inside an active `handle_some`, `handle_all` or `try_` scope (where it is uniquely associated with `*this`), or discarded. See <>. + +Returns: :: `*this`. + +''' + +[[operator_eq-error]] +==== `operator==` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + bool operator==( error const & e1, error const & e2 ) noexcept; + +} } +---- + +Returns: :: `true` if the two values `e1` and `e2` are equal, `false` otherwise. + +''' + +[[operator_neq-error]] +==== `operator!=` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + bool operator!=( error const & e1, error const & e2 ) noexcept; + +} } +---- + +Returns: :: `!(e1==e2)`. + +''' + +[[operator_shl-error]] +==== `operator<<` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + std::ostream & operator<<( std::ostream & os, error const & e ) + +} } +---- + +Effects: :: Prints an `unsigned int` value that uniquely identifies the value `e`. + +''' + +[[result]] +=== `result` + +include::{sourcedir}/synopses/result.adoc[] + +''' + +[[result::result]] +==== Constructors + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + result::result() noexcept; + + template + result::result( T const & v ); + + template + result::result( T && v ) noexcept; + + template + result::result( leaf::error const & e ) noexcept; + + template + result::result( leaf::error_capture const & ec ) noexcept; + +} } +---- + +A `result` object is in one of three states: + +* Value state, in which case it contains an object of type `T`, and `<>`/`<>` can be used to access the contained value. +* Error state, in which case it contains an object of type `<>`, and calling `<>`/`<>` throws `leaf::<>`. +* Error-capture state, which is the same as the Error state, but in addition to the <> object, it contains captured E-objects. + +To get a `result` in value state, initialize it with an object of type `T` or use the default constructor. + +To get a `result` in error state, initialize it with an <> object. + +To get a `result` in error-capture state, call <>. + +TIP: A `result` that is in value state converts to `true` in boolean contexts. A `result` that is not in value state converts to `false` in boolean contexts. + +''' + +[[result::operator_bool]] +==== Conversion to `bool` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + result::operator bool() const noexcept; + +} } +---- + +Returns: :: If `*this` was initialized in value state, returns `true`, otherwise returns `false`. See `<>`. + +''' + +[[result::value]] +==== `value`/`operator*` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + T const & result::value() const; + + template + T & result::value(); + + template + T const & result::operator*() const; + + template + T & result::operator*(); + +} } +---- + +Effects: :: If `*this` was initialized in value state, returns a reference to the stored value, otherwise throws `leaf::<>`. See `<>`. + +''' + +[[result::error]] +==== `error` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + template + leaf::error result::error( E && ... e ) noexcept; + +} } +---- + +This member function is designed to be used in `return` statements in functions that return `result` (or `leaf::<>`) to return an error to the caller. + +Returns: :: +* If `*this` is in value state, returns `<>(std::forward(e...))`, which begins propagating a new `error` value (as opposed to forwarding an existing `error` value); +* If `*this` is in error-capture state, all captured E-objects are moved to available storage in active <>, <> or <> scopes, `*this` is converted to error state, and then +* If `*this` is in error state, returns `err.<>(std::forward(e...))`, where `err` is the <> value stored in `*this`. + +''' + +[[preload]] +=== `preload` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + <> preload( E && ... e ) noexcept; + +} } +---- + +Requirements: :: `<>::value` must be `true` for each `E`. + +Effects: :: All `e...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: +* If a new <> value was created (in the calling thread) since the object returned by `preload` was created, the stored `e...` objects are propagated and become associated with the _last_ such `leaf::error` value; +* Otherwise, if `std::unhandled_exception()` returns `true`, the stored `e...` objects are propagated and become associated with the _first_ `leaf::error` value created later on; +* Otherwise, the stored `e...` objects are discarded. + +''' + +[[defer]] +=== `defer` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + <> defer( F && ... f ) noexcept; + +} } +---- + +Requirements: :: Each of the `f~i~` objects must be a function that does not throw exceptions, takes no arguments and returns an object of a no-throw movable type `E~i~` for which `<>::value` is `true`. + +Effects: :: All `f...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: +* If a new <> value was created (in the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and each returned object is propagated and becomes associated with the _last_ such `leaf::error` value; +* Otherwise, if `std::unhandled_exception()` returns `true`, each of the stored `f...` is called, and the returned objects are propagated and become associated with the _first_ `leaf::error` value created later on; +* Otherwise, the stored `f...` objects are discarded. + +''' + [[error_info]] === `error_info` @@ -702,12 +1011,12 @@ When errors are communicated by `<>`, the `handle_some` functi Requirements: :: -* The `try_block` must be a function callable with no arguments that returns an instance of the <> class template. -* All `handler...` arguments must be functions that return the same type as the `try_block`, or types that convert implicitly to it (e.g. `<>`); +* The `try_block` must be a function callable with no arguments that returns `<>`; +* All `handler...` arguments must be functions that return the same type as the `try_block`, or types that convert implicitly to it (note: a `result` object can be initialized by an <> object, regardless of `T`); * Each of the `handler...` functions: ** may take arguments of <>, either by value or by `const &`, or as a `const *`; -** may take arguments of the predicate type `<>`, where `E` is an E-type, either by value or by `const &`; +** may take arguments, either by value or by `const &`, of the predicate type `<>`, where `E` is an E-type; ** may take an <> argument by `const &`; ** may take an <> argument by `const &`; ** may take a <> argument by `const &`; @@ -734,14 +1043,14 @@ if( file-open-fails ) return leaf::new_error( error_code::file_open_error, e_file_name{fn} ); ---- + -As each E-type object is passed to LEAF, it is immediately moved to the `handle_some` (or <>, or <>) scope highest in the call stack that has available storage for that type, and is associated with the specific <> value returned by `new_error`. Objects of E-types for which none of these scopes have available storage are discarded. +As each E-type object is passed to LEAF, it is immediately moved to the `handle_some` (or <>, or <>) scope highest in the call stack that has available storage for that type, and is uniquely associated with the specific <> value returned by `new_error`. Objects of E-types for which none of these scopes have available storage are discarded. Return Value: :: If the `<>` object `r` returned by the `try_block` function indicates success, `handle_some` returns `r`. + Otherwise, `handle_some` considers each of the `handler...` functions, in order, until it finds one that matches the reported error. The first matching handler is invoked and the `result` it returns is forwarded to the caller of `handle_some`; if no match is found, `handle_some` returns `r`. + -If the value `handle_some` is returning indicates success, all stored E-objects are discarded. Otherwise, each stored E-object is moved to appropriate storage available in `handle_some` (or <>, or <>) scopes higher up the call stack (each moved object retains its association with its specific `error` value). E-objects for which such storage is not available are discarded. +If the value `handle_some` is returning indicates success, all stored E-objects are discarded. Otherwise, each stored E-object is moved to appropriate storage available in `handle_some` (or <>, or <>) scopes higher up the call stack (each moved object retains its unique association with its specific `error` value). E-objects for which such storage is not available are discarded. Handler Matching Procedure: :: + @@ -778,20 +1087,39 @@ namespace boost { namespace leaf { Requirements: :: -* The `try_block` must be a function callable with no arguments that returns a `<>`; -* All `handler...` arguments must be functions that return T (or types that convert implicitly to `T`); -* Each of the `handler...` functions: -** may take any number of arguments of <>, either by value or by `const &`; -** may also take, by `const &`, objects of type <> or <>; -** may not take any other types of arguments. +`handle_all` has the same requirements as <>, with the following change: ++ +-- +* All `handler...` arguments must be functions that return `T` (rather than `result`), or types that convert implicitly to `T`. +-- +And the following addition: * At least one `handler` function (usually the last) must match any error. Effects: :: -The `handle_all` function, like <>, reserves space in its scope for E-objects, then calls the `try_block` function which returns a `result` object `r`. The differences are: +The `handle_all` function works the same as <>, but because it is guaranteed to be able to match any error communicated by the `try_block` with a `handler`, it doesn't need to return a `result`. -* Upon success, `handle_all` returns `r.<>()`, whereas `handle_some` returns r; -* Upon failure, `handle_all` is guaranteed to find a suitable `handler`, returning whatever value it returns (which is usually of type `T`; if not, it must be implicitly convertible to `T`). +.Example +[source,c++] +---- +int main() +{ + return leaf::handle_all( + [ ]() -> leaf::result + { + //do work, bail out returning leaf::error for any failure + .... + + std::cout << "Success!"; + return 0; + }, + [ ] + { + std::cerr << "Error!"; + return 1; + } ); +} +---- ''' @@ -811,21 +1139,38 @@ namespace boost { namespace leaf { Requirements: :: -* The `try_block` must be a function, callable with no arguments; -* All `handler...` arguments must be functions that return the same type as the `try_block` (or types that convert implicitly to it); -* Each of the `handler...` functions: -** may take any number of arguments of <>, either by value or by `const &`; -** may also take, by `const &`, objects of type <> or <>; -** may not take any other types of arguments. +`try_` has the same requirements as <>, with the following change: ++ +-- +* The `try_block` is not required to -- and usually does not -- return a `result` (because, presumably, all failures are communicated by throwing exceptions). +-- +And the following addition: +* Each of the `handler...` functions may take arguments, either by value or by `const &`, of the predicate type `<>`, where `Ex` is a type that derives from `std::exception`. Effects: :: -The `try_` function, like <>, reserves space in its scope for E-objects, then calls the `try_block` function. +`try_` works similarly to <> -- the difference is that `try_` catches exceptions thrown by the `try_block` function with a `catch(std::exception const &)` or, if that fails, `catch(...)`. -However, `try_` assumes that any failures are communicated by throwing exceptions. In case the `try_block` throws, +Return Value: :: -* Upon success, `handle_all` returns `r.<>()`, whereas `handle_some` returns r; -* Upon failure, `handle_all` is guaranteed to find a suitable `handler`, returning whatever value it returns (which is usually of type `T`; if not, it must be implicitly convertible to `T`). +* If the `try_block` succeeds, `try_` forwards the return value to the caller; +* Otherwise, it attempts match the <> communicated with the caught exception with a `handler`. If that succeeds, `try_` forwards the return value from the matched `handler` to the caller; +* Otherwise, `try_` re-throws the caught exception. + +Handler Matching Procedure: :: + +Because each E-object stored in the `try_` scope (see <>) is uniquely associated with a specific <> value, `try_` needs to extract an `error` value from the caught exception in order to access any currently stored E-object (`handle_some` and `handle_all` do not have this challenge, since the <> value they need is readily available in the `<>` object communicating the failure). ++ +When throwing, users are encouraged to pass the exception object through the <> function template -- and `throw` the object it returns. This guarantees that the thrown exception transports a unique `error` value, just like `result` does. ++ +However, this isn't possible when we don't control the `throw` site, for example if the exception is thrown by a standard function. In this case, E-objects communicated to LEAF are associated with the `error` value returned by <>, which is a preview of sorts, of the `error` value that would be returned by the next call to <>. ++ +Similarly, if the exception object caught by `try_` does not transports an `error` value, E-objects are looked up using the `error` value returned by <> just before `try_` goes through its `handler`-matching search. If imperfect, this approach provides the needed association. ++ +With the `error` value thus obtained, the `handler`-matching procedure works the same as in <>, with the following addition to the way each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced: + +* If `a`~i~ is of type `A`~i~ `const &`: +** If `A`~i~ is of the predicate type `<>`, `a`~i~ is initialized with the caught `std::exception const &`. The handler is dropped if the expression `a`~i~`()` evaluates to `false`. ''' @@ -958,318 +1303,6 @@ assert(!c4()); ''' - - -[[expect::expect]] -==== Constructor - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - expect::expect() noexcept; - -} } ----- - -Description: :: Initializes an empty `expect` instance. - -Postcondition: :: `<>

(*this,e)` returns a null pointer for any `P` and any `<>` value `e`. - -''' - -[[expect-dtor]] -==== Destructor - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - expect::~expect() noexcept; - -} } ----- - -Effects: :: Each stored object is moved to a corresponding slot in other existing `expect` instances according to the rules described `<>`. - -''' - -[[capture-expect]] -==== `capture` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - error_capture capture( expect & exp, error const & e ); - -} } ----- - -Effects: :: Moves all objects currently stored in `exp` and associated with the `leaf::<>` value `e`, into the returned `<>` object. The contents of the `error_capture` object are immutable and allocated on the heap. - -NOTE: `error_capture` objects are useful for transporting error objects across thread boundaries. - -''' - -[[handle_error-expect]] -==== `handle_error` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - struct uhnandled_error - { - static constexpr R value( error const & e ) noexcept; - }; - - template - <> handle_error( expect const & exp, error const & e, F && ... f ) noexcept; - -} } ----- - -Requirements: :: -* `sizeof...(F) > 0`; -* Each `f...` must be callable; -* All `f...` must return the same static type. - -Return type: :: The return type `R` of `handle_error` is deduced as the return type of the `f...` functions, except if their return type is `void`, in which case `R` is deduced as `bool`, and the behavior (described below) is as if the `f...` functions return `true`. - -Effects: :: Attempts to match, by type, the objects currently stored in `exp`, associated with the `<>` value `e`, with the arguments of each of the `f...` function objects, in order. -+ -If a complete match is found among `f...`, the matched function is called with the entire set of corresponding objects from `exp` (the function may not modify those values), and the returned value is forwarded to the caller of `handle_error`. -+ -Otherwise, `handle_error` returns `unhandled_error::value(e)`, where `R` is the deduced return type of `handle_error`. The main `unhadled_error` template is defined such that: -+ --- -* if `R` is `bool`, `unhandled_error::value(e)` returns `false`; -* else, if `R` is an instance of the `<>` template, `unhandled_error::value(e)` returns `e` (note, `result` can be implicitly initialized by the `<>` object `e`); -* else, if `std::is_integral::value`, the call to `unhandled_error::value(e)` returns `static_cast(-1)`; -* else, the call to `unhandled_error::value(e)` returns `R()`. --- -+ -NOTE: The `unhandled_error` template may be specialized for user-defined types as needed. - -Example: :: -+ -[source,c++] ----- -bool matched = handle_error( exp, e, - - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; - }, - - [ ] ( e_errno const & errn ) - { - std::cerr << "I/O error, errno=" << errn << std::endl; - } - -); ----- -+ -Assuming `struct e_file_name { std::string value; }` and `struct e_errno { int value; }`, the call to `handle_error` above will: + -+ -* Check if the `expect` object `exp` contains `e_file_name` and `e_errno` objects, associated with the `leaf::<>` value `e`. If it does, it will pass them to the first lambda function, then return `true`; -* Otherwise if it contains just `e_errno`, it will pass it to the second lambda function, then return `true`; -* Otherwise, `handle_error` returns `false`. - -''' - -[[peek-expect]] -==== `peek` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - P const * peek( expect const & exp, error const & e ) noexcept; - -} } ----- - -Returns: :: If `exp` currently stores an object of type `P` associated with the `<>` value `e`, returns a read-only pointer to that object. Otherwise returns a null pointer. - -''' - -[[diagnostic_output-expect]] -==== `diagnostic_output` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - void diagnostic_output( std::ostream & os, expect const & exp ); - - template - void diagnostic_output( std::ostream & os, expect const & exp, error const & e ); - -} } ----- - -Effects: :: Prints diagnostic information about the objects stored in `exp`. The second overload will only print diagnostic information about objects stored in `exp` which are associated with the `leaf::<>` value `e`. - -NOTE: The printing of each individual object is done by the rules described <>. - -''' - -[[error]] -=== `error` - -include::synopses/error.adoc[] - -Objects of class `error` are values that identify an error across the entire program. They can be copied, moved, assigned to, and compared to other error objects. They occupy as much memory, and are as efficient as `unsigned int`. - -When an `e...` sequence is passed to the `error` constructor or to <>, these objects are moved into matching storage provided by `<>` instances, where it is associated with the `error` object (which can later be passed to `<>` or `<>` to retrieve them). - -''' - -[[error::error]] -==== Constructor - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - error::error( E && ... e ) noexcept; - -} } ----- - -Requirements: :: `<>::value` must be `true` for each `E`. - -Effects: :: Each of the `e...` objects is either moved into the corresponding storage provided by `expect` instances (where it is associated with `*this`), or discarded. See `<>`. - -Postconditions: :: `*this` is a unique value across the entire program. The user may create any number of other `error` values that compare equal to `*this`, by copy, move or assignment, just like with any other value type. - -''' - -[[error::propagate]] -==== `propagate` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - error error::propagate( E && ... e ) const noexcept; - -} } ----- - -Effects: :: Each of the `e...` objects is either moved into the corresponding storage provided by `expect` instances (where it is associated with `*this`), or discarded. See `<>`. - -Returns: :: `*this`. - -''' - -[[operator_eq-error]] -==== `operator==` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - bool operator==( error const & e1, error const & e2 ) noexcept; - -} } ----- - -Returns: :: `true` if the two values `e1` and `e2` are equal, `false` otherwise. - -''' - -[[operator_neq-error]] -==== `operator!=` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - bool operator!=( error const & e1, error const & e2 ) noexcept; - -} } ----- - -Returns: :: `!(e1==e2)`. - -''' - -[[operator_shl-error]] -==== `operator<<` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - std::ostream & operator<<( std::ostream & os, error const & e ) - -} } ----- - -Effects: :: Prints an `unsigned int` value that uniquely identifies the value `e`. - -''' - -[[next_error_value]] -==== `next_error_value` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - error next_error_value() noexcept; - -} } ----- - -Returns: :: The `error` value which will be constructed the next time the `<>` constructor is invoked from the calling thread. -+ -This function can be used to associate error objects with the next `error` value to be reported. Use with caution, only when restricted to reporting errors using specific third-party types, incompatible with LEAF -- for example when reporting an error from a C callback. As soon as control exits this critical path, you should create and return a `leaf::error` object (which will be equal to the `error` object returned by the earlier call to `next_error_value`). - -IMPORTANT: `error` values are unique across the entire program. - -''' - -[[last_error_value]] -==== `last_error_value` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - error last_error_value() noexcept; - -} } ----- - -Returns: :: The last `error` value constructed by the calling thread. - -IMPORTANT: `error` values are unique across the entire program. - -''' - [[is_e_type]] ==== `is_e_type` @@ -1320,60 +1353,6 @@ The `is_e_type` template needs not be specialized for: ''' -=== Automatic Propagation - -include::synopses/preload.adoc[] - -These two functions are used to automatically associate error objects with failures that propagate through the scope where they are invoked. - -''' - -[[preload]] -==== `preload` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - <> preload( E && ... e ) noexcept; - -} } ----- - -Requirements: :: `<>::value` must be `true` for each `E`. - -Effects: :: All `e...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: -* If a new `leaf::<>` value was created (in the calling thread) since the object returned by `preload` was created, the stored `e...` objects are propagated and become associated with the _last_ such `leaf::error` value; -* Otherwise, if `std::unhandled_exception()` returns `true`, the stored `e...` objects are propagated and become associated with the _first_ `leaf::error` value created later on; -* Otherwise, the stored `e...` objects are discarded. - -''' - -[[defer]] -==== `defer` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - <> defer( F && ... f ) noexcept; - -} } ----- - -Requirements: :: Each of the `f~i~` objects must be a function that does not throw exceptions, takes no arguments and returns an object of a no-throw movable type `E~i~` for which `<>::value` is `true`. - -Effects: :: All `f...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: -* If a new `leaf::<>` value was created (in the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and the returned object is propagated and becomes associated with the _last_ such `leaf::error` value; -* Otherwise, if `std::unhandled_exception()` returns `true`, each of the stored `f...` is called, and the returned objects are propagated and become associated with the _first_ `leaf::error` value created later on; -* Otherwise, the stored `f...` objects are discarded. - -''' - [[error_capture]] === `error_capture` @@ -1486,7 +1465,7 @@ Otherwise, `handle_error` returns `unhandled_error::value(e)`, where `R` is t + -- * if `R` is `bool`, `unhandled_error::value(e)` returns `false`; -* else, if `R` is an instance of the `<>` template, `unhandled_error::value(e)` returns `e` (note, `result` can be implicitly initialized by the `<>` object `e`); +* else, if `R` is an instance of the <::value(e)` returns `e` (note, `result` can be implicitly initialized by the `<>` object `e`); * else, if `std::is_integral::value`, the call to `unhandled_error::value(e)` returns `static_cast(-1)`; * else, the call to `unhandled_error::value(e)` returns `R()`. -- @@ -1558,137 +1537,16 @@ NOTE: The printing of each individual object is done by the rules described < -[source,c++] ----- -namespace boost { namespace leaf { +include::{sourcedir}/synopses/capture_result.adoc[] - template - result::result() noexcept; +Requirements: :: `F` must be a function type that returns a `<>`. - template - result::result( T const & v ); +Returns: :: a `<>` in - template - result::result( T && v ) noexcept; - - template - result::result( leaf::error const & e ) noexcept; - - template - result::result( leaf::error_capture const & ec ) noexcept; - -} } ----- - -A `result` object is in one of two states: - -* Value state, in which case it contains an object of type `T`, and `<>`/`<>` can be used to access the contained value. -* Error state, in which case it contains an object of type `<>` or an object of type `<>`, and calling `<>`/`<>` throws `leaf::<>`. - -To get a `result` object in error state, initialize it with a `leaf::error` or a `leaf::error_capture` . - -Otherwise a `result` is initialized in value state using the default constructor of `T`, or by copying or moving from `v`. - -IMPORTANT: A `result` that is in value state converts to `true` in boolean contexts. A `result` that is in error state converts to `false` in boolean contexts. - -''' - -[[result::operator_bool]] -==== Conversion to `bool` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - result::operator bool() const noexcept; - -} } ----- - -Returns: :: If `*this` was initialized in value state, returns `true`, otherwise returns `false`. See `<>`. - -''' - -[[result::value]] -==== `value`/`operator*` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - T const & result::value() const; - - template - T & result::value(); - - template - T const & result::operator*() const; - - template - T & result::operator*(); - -} } ----- - -Effects: :: If `*this` was initialized in value state, returns a reference to the stored value, otherwise throws `leaf::<>`. See `<>`. - -''' - -[[result::error]] -==== `error` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - template - leaf::error result::error( E && ... e ) noexcept; - -} } ----- - -This member function is designed to be used in `return` statements in functions that return `result` (or `leaf::<>`) to return an error to the caller. - -Effects: :: -* If `*this` is in value state, returns `leaf::<>(std::forward(e...))`, which begins propagating a new `error` value (as opposed to forwarding an existing `error` value); -* If `*this` is in error state, it stores either an `<>` or a `leaf::<>`: -** if `*this` stores an `<> cap`, `*this` is converted to store the `leaf::<>` value returned from `cap.<>()`, then -** if `*this` stores a `leaf::error` value `err`, returns `err.<>(std::forward(e...))`, which forwards the same `error` to the caller, augmenting it with the additional error objects `e...`. - -''' - -[[capture-result]] -==== `capture` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - result capture( expect & exp, result const & r ); - -} } ----- - -Returns: :: * If `*this` is in value state, returns `*this`. * If `*this` is in error state and stores an `<>` object, returns `*this`. * If `*this` is in error state and stores a `leaf::<>` value `err`, returns `<>(exp,err)`. @@ -1697,103 +1555,6 @@ NOTE: For an example, see < -[source,c++] ----- -namespace boost { namespace leaf { - - template - <> result::handle_error( expect const & exp, result const & r, F && ... f ) noexcept; - -} } ----- - -Preconditions: :: `!r`. - -Returns: :: -* If `r` stores an `<>` object `cap`, returns `<>(cap,f...)`. -* If `r` stores a `leaf::<>` value `err`, returns `<>(exp,err,f...)`. - -NOTE: The return type is deduced as described <>. - -Example: :: -+ -If `g` succeeds, `f` consumes its return value and indicates success to its caller. Otherwise, if `g` fails with a `my_error`, `f` handles it and, again, returns success; all other error types are automatically forwarded to the caller, due to `<>`. -+ -[source,c++] ----- -leaf::result f() -{ - leaf::expect exp; - if( leaf::result r = g() ) - { - //use r, then indicate success: - return { }; - } - else - { - //handle my_error, forward all other errors to the caller: - return leaf::handle_error( exp, r, - [ ]( my_error const & err ) -> result - { - //Deal with my_error: - .... - //Indicate success: - return { }; - } ); - } -} ----- - -''' - -[[peek-result]] -==== `peek` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - P const * peek( expect const & exp, result const & r ) noexcept; - -} } ----- - -Preconditions: :: `!r`. - -Returns: :: -* If `r` stores an `<>` object `cap`, returns `<>

(cap)`. -* If `r` stores a `leaf::<>` value `err`, returns `<>

(exp,err)`. - -''' - -[[diagnostic_output-result]] -==== `diagnostic_output` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - void diagnostic_output( std::ostream & os, expect const & exp, result const & r ); - -} } ----- - -Preconditions: :: `!r`. - -Returns: :: -* If `r` stores an `<>` object `cap`, returns `<>(os,cap)`. -* If `r` stores a `leaf::<>` value `err`, returns `<>(os,exp,err)`. - -''' - [[bad_result]] ==== `bad_result` @@ -1820,7 +1581,7 @@ This exception is thrown by `<>()`/`<> ---- -Effects: :: `LEAF_ERROR(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically passed to the `leaf::error` constructor, in addition to `e...`, in a `<>` object. +Effects: :: `LEAF_ERROR(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically passed to the `leaf::error` constructor, in addition to `e...`, in a `<>` object. NOTE: `LEAF_ERROR` is designed for use in `return` expressions, to automatically communicate the location of the error being reported (but see <>). @@ -1909,7 +1670,7 @@ namespace boost { namespace leaf { `e_errno` is suitable to capture `errno`. `e_errno` objects use `strerror` to convert the `errno` code to a friendlier error message when `<>` is invoked. -WARNING: It is a logic error to use `e_errno` with <>; it should be passed to the `error` <> directly, or used with <>. +WARNING: It is a logic error to use `e_errno` with <>; it should be passed to <> directly, or used with <>. ''' @@ -1988,51 +1749,6 @@ namespace boost { namespace leaf { The <>, <> and <> macros capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` into a `e_source_location` object, if there is currently `<>` storage available for it. When `<>` is invoked, all three items are printed. -''' - -[[e_unexpected]] -==== `e_unexpected` - -[source,c++] ----- -namespace boost { namespace leaf { - - struct e_unexpected - { - char const * (*first_type)(); - int count; - - friend std::ostream & operator<<( std::ostream & os, e_unexpected const & x ); - }; - -} } ----- - -Whenever an error object of unexpected type (a type for which there is currently no `<>` storage available) is passed to the `leaf::error` <> or other similar function, if there is currently `expect` storage available for `e_unexpected`, LEAF will communicate the type of the object in the first such occurrence, as well as the count of these occurrences regardless of type. - -''' - -[[e_unexpected_diagnostic_output]] -==== `e_unexpected_diagnostic_output` - -[source,c++] ----- -namespace boost { namespace leaf { - - struct e_unexpected_diagnostic_output - { - <> - - friend std::ostream & operator<<( std::ostream & os, e_unexpected_diagnostic_output const & x ); - }; - -} } ----- - -Whenever an error object of unexpected type (a type for which there is currently no `<>` storage available) is passed to the `leaf::error` <> or other similar function, if there is currently `expect` storage available for `e_unexpected_diagnostic_output`, even though the error object itself is discarded, it will show in the text printed by <>. - -WARNING: Using `e_unexpected_diagnostic_output` may allocate memory dynamically. - == Exception Handling Reference === Working with Exception Objects @@ -2517,7 +2233,7 @@ LEAF solves this problem without using exception handling: a scope that is able ---- [.text-right] -`<>` | `<>` | <> +`<>` | <> And here is a function which is able to report either `std::error_code` or `boost::error_code`: @@ -2542,7 +2258,7 @@ leaf::result f() ---- [.text-right] -`<>` | `<>` +<> | `<>` Of course, under refactoring `f` can be changed to return any other error type as needed, without requiring a change in its static interface. With LEAF, such changes become transparent to any error-neutral function that calls and forwards errors from `f` to its caller; only the error handling scope needs to be updated to handle the new error types returned by `f`. @@ -2574,7 +2290,7 @@ catch( std::exception const & e ) ---- [.text-right] -<> | `<>` +<> | <> The reason we need to use `<>` is that not all exception types derive from `leaf::<>`. If the caught exception has a `leaf::error` subobject, `get_error` will return that `leaf::error` value. Also, such exceptions can be intercepted by `catch( error e )` if needed. @@ -2583,7 +2299,7 @@ But if the caught exception doesn't have a `leaf::error` subobject, `get_error` ''' [[technique_preload_in_c_callbacks]] -=== Using `next_error_value` in C-callbacks +=== Using `next_error` in C-callbacks Communicating information pertaining to a failure detected in a C callback is tricky, because C callbacks are limited to a specific static signature, which may not use {CPP} types. @@ -2645,13 +2361,13 @@ int do_work( lua_State * L ) noexcept } else { - leaf::next_error_value().propagate(ec1); //<3> + leaf::next_error().propagate(ec1); //<3> return luaL_error(L,"do_work_error"); //<4> } } ---- [.text-right] -<> | `<>` +<> | <> <1> "Sometimes" `do_work` fails. <2> In case of success, push the result on the Lua stack, return back to Lua. @@ -2680,7 +2396,7 @@ leaf::result call_lua( lua_State * L ) } ---- [.text-right] -`<>` | <> | `<>` +<> | <> | <> <1> Ask the Lua interpreter to call the global Lua function `call_do_work`. <2> Something went wrong with the call, so we'll return a `leaf::<>`. If this is a `do_work` failure, the `do_work_error_code` object prepared in `do_work` will become associated with this `leaf::error` value. If not, we will still need to communicate that the `lua_pcall` failed with an error code and an error message. @@ -2719,7 +2435,7 @@ int main() noexcept } ---- [.text-right] -`<>` | `<>` | <> +`<>` | <> <1> Tell LEAF what error objects are expected. <2> If the call to `call_lua` succeeded, just print the answer. @@ -2769,7 +2485,7 @@ std::future> launch_task() ---- [.text-right] -`<>` | `<>` | <> +<> That's it! Later when we `get` the `std::future`, we can process the returned `result` as if it was generated locally: @@ -2801,7 +2517,7 @@ else ---- [.text-right] -`<>` | `<>` | <> +<> NOTE: Follow this link to see a complete example program: https://github.com/zajo/leaf/blob/master/example/capture_result.cpp?ts=3[capture_result.cpp]. @@ -3041,7 +2757,7 @@ else ---- [.text-right] -`<>` | `<>` | <> | <> +<> NOTE: Please post questions and feedback on the Boost Developers Mailing List (LEAF is not part of Boost). @@ -3186,7 +2902,7 @@ This looks nice, until we realize what the `config_error` type means for the aut Conclusion: :: Transporting error objects statically in return values works great if the failure is handled in the immediate caller of the function that reports it, but most error objects must be communicated across multiple layers of function calls and APIs, which leads to excessive physical coupling between these interfaces. -NOTE: While the `leaf::<>>` class template does have value-or-error semantics, it does not carry the actual error objects. Instead, they are forwarded directly to the appropriate error-handling scope and their types do not participate in function signatures. +NOTE: While the `leaf::<>` class template does have value-or-error semantics, it does not carry the actual error objects. Instead, they are forwarded directly to the appropriate error-handling scope and their types do not participate in function signatures. == Alternatives to LEAF diff --git a/doc/synopses/capture_result.adoc b/doc/synopses/capture_result.adoc index 947384c..41409a3 100644 --- a/doc/synopses/capture_result.adoc +++ b/doc/synopses/capture_result.adoc @@ -8,7 +8,3 @@ namespace boost { namespace leaf { } } ---- - -[.text-right] -<> - diff --git a/doc/synopses/error.adoc b/doc/synopses/error.adoc index dbd50e5..29e4bd7 100644 --- a/doc/synopses/error.adoc +++ b/doc/synopses/error.adoc @@ -3,6 +3,15 @@ ---- namespace boost { namespace leaf { + class error; + + template + error new_error( E && ... e ) noexcept; + + error next_error() noexcept; + + error last_error() noexcept; + class error { public: @@ -16,13 +25,6 @@ namespace boost { namespace leaf { friend std::ostream & operator<<( std::ostream & os, error const & e ) }; - template - error new_error( E && ... e ) noexcept; - - error next_error_value() noexcept; - - error last_error_value() noexcept; - template struct is_e_type { @@ -33,4 +35,4 @@ namespace boost { namespace leaf { ---- [.text-right] -<> | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/handle.adoc b/doc/synopses/handle.adoc index 8d03a8f..5f89b9b 100644 --- a/doc/synopses/handle.adoc +++ b/doc/synopses/handle.adoc @@ -15,4 +15,4 @@ namespace boost { namespace leaf { ---- [.text-right] -`<>` | <> | <> | <> | <> | <> | <> | <> +`<>` | <> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/result.adoc b/doc/synopses/result.adoc index 2fa660a..ae0e6a5 100644 --- a/doc/synopses/result.adoc +++ b/doc/synopses/result.adoc @@ -25,18 +25,6 @@ namespace boost { namespace leaf { leaf::error error( E && ... e ) noexcept; }; - template - result capture( expect & exp, result const & r ); - - template - <> handle_error( expect const & exp, result const & r, F && ... f ) noexcept; - - template - P const * peek( expect const &, result const & ) noexcept; - - template - void diagnostic_output( std::ostream & os, expect const & exp, result const & r ); - struct bad_result: std::exception { }; } } @@ -49,4 +37,4 @@ namespace boost { namespace leaf { ---- [.text-right] -`<>` | <> | <> | <> | <> | <> | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> | <> | <> diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index 050df98..edf3dc8 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -49,7 +49,7 @@ int do_work( lua_State * L ) noexcept { // Associate an do_work_error_code object with the *next* leaf::error object we will // definitely return from the call_lua function... - leaf::next_error_value().propagate(ec1); + leaf::next_error().propagate(ec1); //...once control reaches it, after we tell the Lua interpreter to abort the program. return luaL_error(L,"do_work_error"); diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp index 8d19d7d..ef589eb 100644 --- a/include/boost/leaf/capture_exception.hpp +++ b/include/boost/leaf/capture_exception.hpp @@ -49,7 +49,7 @@ namespace boost { namespace leaf { (void) err; } else - ds->unload(next_error_value()); + ds->unload(next_error()); std::rethrow_exception(ex_); } diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index a487cdf..b5f92f1 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -24,15 +24,15 @@ namespace boost { namespace leaf { class error; - error next_error_value() noexcept; - error last_error_value() noexcept; + error next_error() noexcept; + error last_error() noexcept; class error { template friend error new_error( E && ... ) noexcept; - friend error leaf::next_error_value() noexcept; - friend error leaf::last_error_value() noexcept; + friend error leaf::next_error() noexcept; + friend error leaf::last_error() noexcept; unsigned id_; @@ -120,12 +120,12 @@ namespace boost { namespace leaf { return error(error::id_factory::tl_instance().get()).propagate(std::forward(e)...); } - inline error next_error_value() noexcept + inline error next_error() noexcept { return error(error::id_factory::tl_instance().next_id()); } - inline error last_error_value() noexcept + inline error last_error() noexcept { return error(error::id_factory::tl_instance().last_id()); } @@ -375,30 +375,29 @@ namespace boost { namespace leaf { class verbose_diagnostic_info: leaf_detail::monitor_base { - std::stringstream s_; + std::string s_; std::set already_; public: - verbose_diagnostic_info() noexcept - { - } + verbose_diagnostic_info() noexcept = default; using monitor_base::set_error_info; void reset() noexcept { - s_.str(std::string()); + s_.clear(); already_.clear(); } template void add( E const & e ) { - if( already_.find(&type)==already_.end() && leaf_detail::diagnostic::print(s_,e) ) + std::stringstream s; + if( leaf_detail::diagnostic::print(s,e) && already_.insert(&type).second ) { - s_ << std::endl; - already_.insert(&type); + s << std::endl; + s_ += s.str(); } } @@ -407,7 +406,7 @@ namespace boost { namespace leaf { os << x.get_error_info() << "verbose_diagnostic_info:" << std::endl << - x.s_.str(); + x.s_; return os; } }; diff --git a/include/boost/leaf/preload.hpp b/include/boost/leaf/preload.hpp index 0e57776..2279163 100644 --- a/include/boost/leaf/preload.hpp +++ b/include/boost/leaf/preload.hpp @@ -80,7 +80,7 @@ namespace boost { namespace leaf { explicit preloaded( E && ... e ) noexcept: p_(preloaded_item(std::forward(e))...), - e_(last_error_value()), + e_(last_error()), moved_(false) { } @@ -97,11 +97,11 @@ namespace boost { namespace leaf { { if( moved_ ) return; - error const e = last_error_value(); + error const e = last_error(); if( e==e_ ) { if( std::uncaught_exception() ) - leaf_detail::tuple_for_each_preload::trigger(p_,next_error_value()); + leaf_detail::tuple_for_each_preload::trigger(p_,next_error()); } else leaf_detail::tuple_for_each_preload::trigger(p_,e); @@ -164,7 +164,7 @@ namespace boost { namespace leaf { explicit deferred( F && ... f ) noexcept: d_(deferred_item(std::forward(f))...), - e_(last_error_value()), + e_(last_error()), moved_(false) { } @@ -181,11 +181,11 @@ namespace boost { namespace leaf { { if( moved_ ) return; - error const e = last_error_value(); + error const e = last_error(); if( e==e_ ) { if( std::uncaught_exception() ) - leaf_detail::tuple_for_each_preload::trigger(d_,next_error_value()); + leaf_detail::tuple_for_each_preload::trigger(d_,next_error()); } else leaf_detail::tuple_for_each_preload::trigger(d_,e); diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp index fa44e7e..a53bb69 100644 --- a/include/boost/leaf/try.hpp +++ b/include/boost/leaf/try.hpp @@ -18,7 +18,7 @@ namespace boost { namespace leaf { if( auto e = dynamic_cast(&ex) ) return *e; else - return next_error_value(); + return next_error(); } template @@ -45,7 +45,7 @@ namespace boost { namespace leaf { } catch( ... ) { - return ss.handle_error(error_info(next_error_value(),0,&cap,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(next_error(),0,&cap,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); } } catch( std::exception const & ex ) @@ -54,7 +54,7 @@ namespace boost { namespace leaf { } catch( ... ) { - return ss.handle_error(error_info(next_error_value(),0,0,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(next_error(),0,0,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); } } From ec9a047efc4293aa8fbd4a132b334e4d7a357e25 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Sun, 13 Jan 2019 17:09:33 -0800 Subject: [PATCH 28/34] travis --- include/boost/leaf/error.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index b5f92f1..f128954 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -380,7 +380,9 @@ namespace boost { namespace leaf { public: - verbose_diagnostic_info() noexcept = default; + verbose_diagnostic_info() noexcept + { + } using monitor_base::set_error_info; From e7d212c8e94132a6081fc18352237d3e6145efa1 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Sun, 13 Jan 2019 23:38:46 -0800 Subject: [PATCH 29/34] tweaked the behavior of handle_all --- doc/leaf.adoc | 999 +++++++-------------- doc/synopses/common.adoc | 10 +- example/lua_callback_eh.cpp | 45 +- example/lua_callback_result.cpp | 36 +- include/boost/leaf/detail/static_store.hpp | 27 +- meson.build | 6 +- 6 files changed, 396 insertions(+), 727 deletions(-) diff --git a/doc/leaf.adoc b/doc/leaf.adoc index 0327aa3..aa17bc5 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -528,26 +528,26 @@ include::{sourcedir}/synopses/exception_to_result.adoc[] `<>` [[reference]] -== Reference -[[e_types]] -=== E-Types +[[e_objects]] +== E-Objects -==== Definition +[[is_e_type]] +=== `is_e_type` With LEAF, users can efficiently associate with errors or with exceptions any number of values that pertain to a failure. These values may be of any no-throw movable type `E` for which `<>::value` is `true`. The expectation is that this template will be specialized as needed for e.g. all user-defined error enums. -Throughout this text, types for which `is_e_type` is `true` are called E-types. Objects of those types are called E-objects or E-values. +Throughout this text, types for which `is_e_type` is `true` are called E-types. Objects of those types are called E-objects. The main `is_e_type` template is defined so that `is_e_type::value` is `true` when `E` is: * `std::error_code`, * `boost::system::error_code`, * `std::exception_ptr`, -* any type for which `std::is_base_of::value` is `true`, +* any type `E` for which `std::is_base_of::value` is `true`, * any type which defines an accessible data member `value`. -Usually, error values that need to be communicated are of generic types (e.g. `std::string`). Such values should be enclosed in a C-`struct` that acts as their compile-time identifier and gives them semantic meaning. Examples: +Often, error values that need to be communicated are of generic types (e.g. `std::string`). Such values should be enclosed in a C-`struct` that acts as their compile-time identifier and gives them semantic meaning. Examples: [source,c++] ---- @@ -558,16 +558,23 @@ struct e_minimum_temperature { float value; }; struct e_maximum_temperature { float value; }; ---- -Various LEAF functions take a list of E-objects to associate with an `<>`. For example, if a `copy_file` function that takes the name of the input file and the name of the output file as its arguments detects a failure, it would communicate an error code `ec`, plus the two relevant file names: +By convention, the enclosing C-`struct` names use the `e_` prefix. + +[[propagation]] +=== Propagation + +"To propagate" an E-object is to associate it with a particular <> value, making it available to functions that handle that error. + +More formally, when an E-object is propagated, it is immediately moved to available storage in a `handle_some`, a `handle_all` or a `try_` scope currently active in the calling thread, where it becomes uniquely associated with a specific <> value -- or discarded if storage is not available; see <>. + +Various LEAF functions take a list of E-objects to propagate. As an example, if a `copy_file` function that takes the name of the input file and the name of the output file as its arguments detects a failure, it could communicate an error code `ec`, plus the two relevant file names using <>: [source,c++] ---- return leaf::new_error( ec, e_input_name{n1}, e_output_name{n2} ); ---- -By convention, the enclosing C-`struct` names use the `e_` prefix. - -==== Diagnostic Information +=== Diagnostic Information LEAF is able to automatically generate diagnostic messages that include information about all E-objects available in an error-handling scope. For this purpose, it needs to be able to print objects of user-defined E-types. @@ -592,6 +599,12 @@ The `e_errno` type above is designed to hold `errno` values. The defined `operat TIP: This automatically-generated diagnostic messages are developer-friendly, but not user-friendly. Therefore, `operator<<` overloads for E-types should only print technical information in English, and should not attempt to localize strings or to format a user-friendly message; this should be done in error-handling functions specifically designed for that purpose. +''' + +[[reporting]] +== Reporting Errors + + ''' [[error]] @@ -619,7 +632,7 @@ namespace boost { namespace leaf { Requirements: :: `<>::value` must be `true` for each `E`. -Effects: :: Each of the `e...` objects is either moved into the corresponding storage inside an active `handle_some`, `handle_all` or `try_` scope (where it is uniquely associated with `*this`), or discarded. See <>. +Effects: :: Each of the `e...` objects is <> and uniquely associated with the returned value. Returns: :: A new `error` value, which is unique across the entire program. @@ -679,7 +692,7 @@ namespace boost { namespace leaf { } } ---- -Effects: :: Each of the `e...` objects is either moved into the corresponding storage inside an active `handle_some`, `handle_all` or `try_` scope (where it is uniquely associated with `*this`), or discarded. See <>. +Effects: :: Each of the `e...` objects is <> and uniquely associated with `*this`. Returns: :: `*this`. @@ -854,6 +867,51 @@ Returns: :: ''' +[[bad_result]] +==== `bad_result` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + struct bad_result: std::exception { }; + +} } +---- + +This exception is thrown by `<>()`/`<>()` if they're invoked for a `result` object that is in not in value state. + +''' + +[[exception]] +=== `exception` + +[source,c++] +.#include +---- +namespace boost { namespace leaf { + + template + <> exception( Ex && ex, E && ... e ) noexcept; + +} } +---- + +Requirements: :: +* `Ex` must derive from `std::exception`. +* For all of E, `<>::value` is `true`. + +Returns: :: An object of unspecified type which derives publicly from `Ex` *and* from class `<>` such that: +* its `Ex` subobject is initialized by `std::forward(ex)`; +* its `error` subojbect is initialized by `<>(std::forward(e)...`). + +TIP: If thrown, the returned object can be caught as `Ex &` or as `leaf::<>`. + +NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` with the returned object, use <> instead of `leaf::exception`. + +''' + [[preload]] === `preload` @@ -871,7 +929,7 @@ namespace boost { namespace leaf { Requirements: :: `<>::value` must be `true` for each `E`. Effects: :: All `e...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: -* If a new <> value was created (in the calling thread) since the object returned by `preload` was created, the stored `e...` objects are propagated and become associated with the _last_ such `leaf::error` value; +* If a new <> value was created (in the calling thread) since the object returned by `preload` was created, the stored `e...` objects are <> and become associated with the _last_ such `leaf::error` value; * Otherwise, if `std::unhandled_exception()` returns `true`, the stored `e...` objects are propagated and become associated with the _first_ `leaf::error` value created later on; * Otherwise, the stored `e...` objects are discarded. @@ -894,105 +952,162 @@ namespace boost { namespace leaf { Requirements: :: Each of the `f~i~` objects must be a function that does not throw exceptions, takes no arguments and returns an object of a no-throw movable type `E~i~` for which `<>::value` is `true`. Effects: :: All `f...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: -* If a new <> value was created (in the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and each returned object is propagated and becomes associated with the _last_ such `leaf::error` value; +* If a new <> value was created (in the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and each returned object is <> and becomes associated with the _last_ such `leaf::error` value; * Otherwise, if `std::unhandled_exception()` returns `true`, each of the stored `f...` is called, and the returned objects are propagated and become associated with the _first_ `leaf::error` value created later on; * Otherwise, the stored `f...` objects are discarded. ''' -[[error_info]] -=== `error_info` +[[LEAF_ERROR]] +=== `LEAF_ERROR` +.#include [source,c++] ---- -namespace boost { namespace leaf { - - class error_info - { - //Constructors unspecified - - public: - - leaf::error const & get_error() const noexcept; - - bool exception_caught() const noexcept; - - std::exception const * get_exception() const noexcept; - - friend std::ostream & operator<<( std::ostream & os, error_info const & x ); - }; - -} } +#define LEAF_ERROR(...) <> ---- -Handlers passed to <>, <> or <> may take an argument of type `error_info const &` to receive information about the error. - -The `get_error` member function returns the program-wide unique identifier of the <>. - -The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` otherwise. - -The `get_exception` member function returns a pointer to the `std::exception` subobject of the exception caught by `try_`, or `0` if that exception could not be converted to `std::exception`. It is illegal to call `get_exception` unless `exception_caught()` is `true`. - -The `operator<<` overload prints diagnostic information about all E-objects, associated with the <> value returned by `get_error()`, currently stored in any active <>, <> or <> scope. +Effects: :: `LEAF_ERROR(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically passed to `new_error`, in addition to all E-objects `e...`, in a `<>` object. ''' -[[diagnostic_info]] -=== `diagnostic_info` +[[LEAF_AUTO]] +=== `LEAF_AUTO` +.#include [source,c++] ---- -namespace boost { namespace leaf { - - class diagnostic_info - { - //Constructors unspecified - - public: - - friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x ); - }; - -} } +#define LEAF_AUTO(v,r)\ + auto _r_##v = r;\ + if( !_r_##v )\ + return _r_##v.error();\ + auto & v = *_r_##v ---- -Handlers passed to <>, <> or <> may take an argument of type `diagnostic_info const &` if they need to print diagnostic information about the error. +`LEAF_AUTO` is useful when calling a function that returns `result` (other than `result`), if the desired behavior is to forward any errors to the caller verbatim. -The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. +Example: -The additional information includes the total count, as well as the type of the first such E-object. +.Compute two int values, return their sum as a float, using LEAF_AUTO: +[source,c++] +---- +leaf::result compute_value(); + +leaf::result add_values() +{ + LEAF_AUTO(v1, compute_value()); + LEAF_AUTO(v2, compute_value()); + return v1 + v2; +} +---- + +Of course, we could write `add_value` without using `LEAF_AUTO`. This is equivalent: + +.Compute two int values, return their sum as a float, without LEAF_AUTO: +---- +leaf::result add_values() +{ + auto v1 = compute_value(); + if( !v1 ) + return v1.error(); + + auto v2 = compute_value(); + if( !v2 ) + return v2.error(); + + return *v1 + *v2; +} +---- ''' -[[verbose_diagnostic_info]] -=== `verbose_diagnostic_info` +[[LEAF_CHECK]] +=== `LEAF_CHECK` +.#include [source,c++] ---- -namespace boost { namespace leaf { - - class verbose_diagnostic_info - { - //Constructors unspecified - - public: - - friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x ); - }; - -} } +#define LEAF_CHECK(r)\ + {\ + auto _r = r;\ + if(!_r)\ + return _r.error();\ + } ---- -Handlers passed to <>, <> or <> may take an argument of type `verbose_diagnostic_info const &` if they need to print diagnostic information about the error. +`LEAF_CHECK` is useful when calling a function that returns `result`, if the desired behavior is to forward any errors to the caller verbatim. -The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. +Example: -The additional information includes the types and the values of all such E-objects. +.Try to send a message, then compute a value, report errors using LEAF_CHECK: +[source,c++] +---- +leaf::result send_message( char const * msg ); -WARNING: Using `verbose_diagnostic_info` may allocate memory dynamically. +leaf::result compute_value(); + +leaf::result say_hello_and_compute_value() +{ + LEAF_CHECK(send_message("Hello!")); + return compute_value(); +} +---- + +Equivalent implementation without `LEAF_CHECK`: + +.Try to send a message, then compute a value, report errors without LEAF_CHECK: +---- +leaf::result add_values() +{ + auto r = send_message("Hello!"); + if( !r ) + return r.error(); + + return compute_value(); +} +---- + +Alternatively: + +.Try to send a message, then compute a value, report errors without LEAF_CHECK: +---- +leaf::result add_values() +{ + if( auto r = send_message("Hello!") ) + return compute_value(); + else + return r.error(); +} +---- ''' +[[LEAF_EXCEPTION]] +=== `LEAF_EXCEPTION` + +[source,c++] +.#include +---- +#define LEAF_EXCEPTION(...) <> +---- + +Effects: :: This is a variadic macro which forwards its arguments to the function template <>, in addition capturing `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]`, in a `<>` object. + +''' + +[[LEAF_THROW]] +=== `LEAF_THROW` + +[source,c++] +.#include +---- +#define LEAF_THROW(...) throw LEAF_EXCEPTION(__VA_ARGS__) +---- + +Effects: :: Throws the exception object returned by <>. + +== Error Handling + [[handle_some]] === `handle_some` @@ -1015,7 +1130,7 @@ Requirements: :: * All `handler...` arguments must be functions that return the same type as the `try_block`, or types that convert implicitly to it (note: a `result` object can be initialized by an <> object, regardless of `T`); * Each of the `handler...` functions: -** may take arguments of <>, either by value or by `const &`, or as a `const *`; +** may take arguments of <>, either by value or by `const &`, or as a `const *`; ** may take arguments, either by value or by `const &`, of the predicate type `<>`, where `E` is an E-type; ** may take an <> argument by `const &`; ** may take an <> argument by `const &`; @@ -1303,322 +1418,165 @@ assert(!c4()); ''' -[[is_e_type]] -==== `is_e_type` +[[error_info]] +=== `error_info` -.#include [source,c++] ---- namespace boost { namespace leaf { - template - struct is_e_type + class error_info { - static constexpr bool value = <>; + //Constructors unspecified + + public: + + leaf::error const & get_error() const noexcept; + + bool exception_caught() const noexcept; + + std::exception const * get_exception() const noexcept; + + friend std::ostream & operator<<( std::ostream & os, error_info const & x ); }; } } ---- -The `is_e_type` template should be specialized for each user-defined type which the user desires to be used as an error type within LEAF, for example: +Handlers passed to <>, <> or <> may take an argument of type `error_info const &` to receive information about the error. -[source,c++] ----- -enum my_error -{ - error1, - error2, - .... -}; +The `get_error` member function returns the program-wide unique identifier of the <>. -namespace boost { namespace leaf { +The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` otherwise. - template<> struct is_e_type: std::true_type { }; +The `get_exception` member function returns a pointer to the `std::exception` subobject of the exception caught by `try_`, or `0` if that exception could not be converted to `std::exception`. It is illegal to call `get_exception` unless `exception_caught()` is `true`. -} } ----- - -This requirement is designed to trigger a diagnostic in case the user unintentionally passes some random object to the `leaf::error` constructor. - -[TIP] --- -The `is_e_type` template needs not be specialized for: - -* types that define an accessible data member `value`, - -* `std::error_code`, - -* `boost::system::error_code`. --- +The `operator<<` overload prints diagnostic information about all E-objects, associated with the <> value returned by `get_error()`, currently stored in any active <>, <> or <> scope. ''' -[[error_capture]] -=== `error_capture` +[[diagnostic_info]] +=== `diagnostic_info` -include::synopses/error_capture.adoc[] - -Objects of class `error_capture` are similar to `<>` instances in that they contain error objects and can be examined by (their own overloads of) `<>` and `<>`. However, unlike `expect` objects, `error_capture` objects: - -* are immutable; -* are allocated on the heap; -* associate all of the stored objects with exactly one `error` value; -* when probed with `peek`/`handle_error`, the lookup is dynamic; -* define `noexcept` copy/move/assignment operations. - -The default constructor can be used to initialize an empty `error_capture`. Use `<>` to capture all objects associated with a given `error` value from a given `expect` object. - -[NOTE] --- -Typical use of `error_capture` objects is to transport error objects across threads, however they are rarely used directly. Instead: - -* With exception handling, use `<>` / `<>`; -* Without exception handling, simply return a <> `result` from a worker thread. --- - -''' - -[[error_capture::error_capture]] -==== Constructor - -.#include [source,c++] ---- namespace boost { namespace leaf { - error_capture::error_capture() noexcept; - -} } ----- - -Effects: Initializes an empty `error_capture` instance. - -Postcondition: :: `<>

(*this,e)` returns a null pointer for any `P` and any `<>` value `e`. - -''' - -[[error_capture::operator_bool]] -==== Conversion to `bool` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - error_capture::operator bool() const noexcept; - -} } ----- - -Returns: :: `false` if `*this` is empty, `true` otherwise. - -''' - -[[error_capture::unload]] -==== `unload` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - error error::unload() noexcept; - -} } ----- - -Effects: :: The objects stored in `*this` are moved into storage provided by `<>` objects in the calling thread, as if each stored object is passed to the constructor of `<>`. - -Returns: :: The `error` value the unloaded objects are associated with. - -Postcondition: :: `!(*this)`. - -''' - -[[handle_error-error_capture]] -==== `handle_error` - -.#include -[source,c++] ----- - -namespace boost { namespace leaf { - - template - <> error_capture::handle_error( error_capture const & ec, F && ... f ) noexcept; - -} } ----- - -Requirements: :: -* `sizeof...(F) > 0`; -* Each `f...` must be callable; -* All `f...` must return the same static type. - -Return type: :: The return type `R` of `handle_error` is deduced as the return type of the `f...` functions, except if their return type is `void`, in which case `R` is deduced as `bool`, and the behavior (described below) is as if the `f...` functions return `true`. - -Effects: :: Attempts to match, by type, the objects currently stored in `ec`, with the arguments of each of the `f...` function objects, in order. -+ -If a complete match is found among `f...`, the matched function is called with the entire set of corresponding objects from `ec` (the function may not modify those values), and the returned value is forwarded to the caller of `handle_error`. -+ -Otherwise, `handle_error` returns `unhandled_error::value(e)`, where `R` is the deduced return type of `handle_error`, and `e` is the `<>` object captured in `ec`. The main `unhadled_error` template is defined such that: -+ --- -* if `R` is `bool`, `unhandled_error::value(e)` returns `false`; -* else, if `R` is an instance of the <::value(e)` returns `e` (note, `result` can be implicitly initialized by the `<>` object `e`); -* else, if `std::is_integral::value`, the call to `unhandled_error::value(e)` returns `static_cast(-1)`; -* else, the call to `unhandled_error::value(e)` returns `R()`. --- -+ -NOTE: The `unhandled_error` template may be specialized for user-defined types as needed. - -Example: :: -+ -[source,c++] ----- -bool matched = handle_error( ec, - - [ ] ( e_file_name const & fn, e_errno const & errn ) + class diagnostic_info { - std::cerr << "Failed to access " << fn << ", errno=" << errn << std::endl; - }, + //Constructors unspecified - [ ] ( e_errno const & errn ) + public: + + friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x ); + }; + +} } +---- + +Handlers passed to <>, <> or <> may take an argument of type `diagnostic_info const &` if they need to print diagnostic information about the error. + +The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. + +The additional information includes the total count, as well as the type of the first such E-object. + +''' + +[[verbose_diagnostic_info]] +=== `verbose_diagnostic_info` + +[source,c++] +---- +namespace boost { namespace leaf { + + class verbose_diagnostic_info { - std::cerr << "I/O error, errno=" << errn << std::endl; - } + //Constructors unspecified -); ----- -+ -Assuming `struct e_file_name { std::string value; }` and `struct e_errno { int value; }`, the call to `handle_error` above will: + -+ -* Check if the `error_capture` object `ec` contains `e_file_name` and `e_errno` objects. If it does, it will pass them to the first lambda function, then return `true`; -* Otherwise if it contains just `e_errno`, it will pass it to the second lambda function, then return `true`; -* Otherwise, `handle_error` returns `false`. + public: -''' - -[[peek-error_capture]] -==== `peek` - -.#include -[source,c++] ----- - -namespace boost { namespace leaf { - - template - P const * peek( error_capture const & ec ) noexcept; + friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x ); + }; } } ---- -Returns: :: If `ec` currently stores an object of type `P`, returns a read-only pointer to that object. Otherwise returns a null pointer. +Handlers passed to <>, <> or <> may take an argument of type `verbose_diagnostic_info const &` if they need to print diagnostic information about the error. -''' +The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. -[[diagnostic_output-error_capture]] -==== `diagnostic_output` +The additional information includes the types and the values of all such E-objects. -.#include -[source,c++] ----- -namespace boost { namespace leaf { +WARNING: Using `verbose_diagnostic_info` may allocate memory dynamically. - void diagnostic_output( std::ostream & os, error_capture const & ec ); - -} } ----- - -Effects: :: Prints diagnostic information about the objects stored in `ec`. - -NOTE: The printing of each individual object is done by the rules described <>. - -''' +== Multi-Thread Programming [[capture_result]] ==== `capture_result` -.#include +.#include include::{sourcedir}/synopses/capture_result.adoc[] -Requirements: :: `F` must be a function type that returns a `<>`. +The `capture_result` function can be used to transport E-objects <> when `f` is invoked in one thread, to a different thread which handles errors through <> or <>. -Returns: :: a `<>` in +Requirements: :: +* `F` must be a function type that returns a `<>`; +* Each of the `E...` types must be an <>. -* If `*this` is in value state, returns `*this`. -* If `*this` is in error state and stores an `<>` object, returns `*this`. -* If `*this` is in error state and stores a `leaf::<>` value `err`, returns `<>(exp,err)`. - -NOTE: For an example, see <`>>. +Returns: :: A function object `fw` of unspecified type, which acts as a wrapper for `f`, taking the same argument types and returning the same `result` type. ++ +When the caller invokes `fw` (presumably from a worker thread) with arguments `a...` of types `A...`: ++ +-- +* Like <>, `fw` reserves storage in its scope for objects of the `E...` types, then +* invokes `f(std::forward(a...))`, which in general will call other functions as needed (any <> objects of the `E...` types are captured in the reserved storage); +-- ++ +If the returned `result` indicates success, it is forwarded to the caller; any E-objects stored in the `fw` scope are discarded. ++ +Otherwise, all stored E-objects are moved to dynamically-allocated memory, which is used to initialize a `result` object in <>. This object is returned to the caller of `fw`. ++ +The returned `result` can safely cross thread boundaries (presumably sent back to the main thread). When it is returned from the `try_block` function passed to <> or <>, its contents are <> (in that thread), and then error handling proceeds as usual. ''' -[[bad_result]] -==== `bad_result` +[[capture_exception]] +==== `capture_exception` -.#include -[source,c++] ----- -namespace boost { namespace leaf { +.#include +include::{sourcedir}/synopses/capture_exception.adoc[] - struct bad_result: std::exception { }; +The `capture_exception` function can be used to transport E-objects <> when `f` is invoked in one thread, to a different thread which handles errors through <>. -} } ----- +Requirements: :: +* `F` must be a function type; +* Each of the `E...` types must be an <>. -This exception is thrown by `<>()`/`<>()` if they`re invoked for a `result` object that is in error state. - -''' - -[[LEAF_ERROR]] -==== `LEAF_ERROR` - -.#include -[source,c++] ----- -#define LEAF_ERROR(...) <> ----- - -Effects: :: `LEAF_ERROR(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically passed to the `leaf::error` constructor, in addition to `e...`, in a `<>` object. - -NOTE: `LEAF_ERROR` is designed for use in `return` expressions, to automatically communicate the location of the error being reported (but see <>). - -''' - -[[LEAF_AUTO]] -==== `LEAF_AUTO` - -.#include -[source,c++] ----- -#define LEAF_AUTO(v,r) auto _r_##v = r; if( !_r_##v ) return _r_##v.error(); auto & v = *_r_##v ----- - -''' - -[[LEAF_CHECK]] -==== `LEAF_CHECK` - -.#include -[source,c++] ----- -#define LEAF_CHECK(r) {auto _r = r; if( !_r ) return _r.error();} ----- - -''' +Returns: :: A function object `fw` of unspecified type, which acts as a wrapper for `f`, taking the same argument types and returning the same type as `f`. ++ +When the caller invokes `fw` (presumably from a worker thread) with arguments `a...` of types `A...`: ++ +-- +* Like <>, `fw` reserves storage in its scope for objects of the `E...` types, then +* invokes `f(std::forward(a...))`, which in general will call other functions as needed (any <> objects of the `E...` types are captured in the reserved storage); +-- ++ +Except if `f` throws, any E-objects stored in the `fw` scope are discarded and the returned value is forwarded to the caller. ++ +Otherwise, `fw` throws another exception object (of unspecified type) which holds all stored `E` objects and the original exception via `std::exception_ptr`. ++ +If this new exception is thrown by the `try_block` function passed to <> (presumably after it was transported to the main thread), its contents are <> (in that thread), and then error handling proceeds as usual. [[common]] -=== Common Error Types -include::synopses/common.adoc[] +== Common E-Types +include::{sourcedir}/synopses/common.adoc[] This header defines some common error types which can be used directly. ''' [[e_api_function]] -==== `e_api_function` +=== `e_api_function` [source,c++] ---- @@ -1632,12 +1590,12 @@ namespace boost { namespace leaf { The `e_api_function` type is designed to capture the name of the API function which returned an error. For example, if you're reporting an error from `fread`, you could use `leaf::e_api_function { "fread" }`. -WARNING: The passed value is stored as a C string (`char const *`), so you should only pass string literals for `value`. +WARNING: The passed value is stored as a C string (`char const *`), so `value` should only be initialized with a string literal. ''' [[e_file_name]] -==== `e_file_name` +=== `e_file_name` [source,c++] ---- @@ -1653,7 +1611,7 @@ When a file operation fails, you could use `e_file_name` to store the name of th ''' [[e_errno]] -==== `e_errno` +=== `e_errno` [source,c++] ---- @@ -1668,14 +1626,14 @@ namespace boost { namespace leaf { } } ---- -`e_errno` is suitable to capture `errno`. `e_errno` objects use `strerror` to convert the `errno` code to a friendlier error message when `<>` is invoked. +To capture `errno`, use `e_errno`. When printed in automatically-generated diagnostic messages, `e_errno` objects use `strerror` to convert the `errno` code to string. WARNING: It is a logic error to use `e_errno` with <>; it should be passed to <> directly, or used with <>. ''' [[e_LastError]] -==== `e_LastError` +=== `e_LastError` [source,c++] ---- @@ -1697,7 +1655,7 @@ namespace boost { namespace leaf { ''' [[e_at_line]] -==== `e_at_line` +=== `e_at_line` [source,c++] ---- @@ -1713,7 +1671,7 @@ namespace boost { namespace leaf { ''' [[e_type_info_name]] -==== `e_type_info_name` +=== `e_type_info_name` [source,c++] ---- @@ -1729,7 +1687,7 @@ namespace boost { namespace leaf { ''' [[e_source_location]] -==== `e_source_location` +=== `e_source_location` [source,c++] ---- @@ -1747,259 +1705,7 @@ namespace boost { namespace leaf { } } ---- -The <>, <> and <> macros capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` into a `e_source_location` object, if there is currently `<>` storage available for it. When `<>` is invoked, all three items are printed. - -== Exception Handling Reference - -=== Working with Exception Objects -include::synopses/exception.adoc[] - -''' - -[[exception]] -==== `exception` - -[source,c++] -.#include ----- -namespace boost { namespace leaf { - - template - <> exception( Ex && ex, E && ... e ) noexcept; - -} } ----- - -Requirements: :: -* `Ex` must derive from `std::exception`. -* For all of E, `<>::value` is `true`. - -Returns: :: An object of unspecified type which derives publicly from `Ex` *and* from class `<>` such that: -* its `Ex` subobject is initialized by `std::forward(ex)`; -* its `error` subojbect is initialized by `std::forward(e)...`. - -TIP: If thrown, the returned object can be caught as `Ex &` or as `leaf::<>`. - -NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` with the returned object, use <> instead of `leaf::exception`. - -''' - -[[get_error]] -==== `get_error` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - error get_error( std::exception const & ex ) noexcept; - -} } ----- - -Returns: :: -* If `auto e = dynamic_cast> const *>(&ex)` succeeds, returns `*e`. -* Othrewise, it returns an unspecified `leaf::error` value, which is "temporarily" associated with any and all currently unhandled exceptions. -+ -IMPORTANT: A successful call to `<>` breaks this association. - -This function is designed to augment exceptions with additional error objects in exception-neutral contexts, when it is known that at least some of the intercepted exceptions are not thrown using `<>`, and therefore do not derive from `leaf::error`. Example: - -[source,c++] ----- -try -{ - f(); -} -catch( std::exception const & ex ) -{ - leaf::get_error(ex).propagate( e_this{x}, e_that{y} ); - throw; -} ----- - -If it is known that all intercepted exceptions are thrown using `leaf::exception`, the following would be more optimal: - -[source,c++] ----- -try -{ - f(); -} -catch( leaf::error e ) -{ - e.propagate( e_this{x}, e_that{y} ); - throw; -} ----- - -''' - -[[peek-exception]] -==== `peek` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - P const * peek( expect const & exp, std::exception const & ex ) noexcept; - -} } ----- - -Effects: :: As if: `return leaf::<>

( exp, <>(ex) );` - -''' - -[[handle_exception]] -==== `handle_exception` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - void handle_exception( expect & exp, std::exception const & ex, F && ... f ); - -} } ----- - -Effects: :: Equivalent to: `if( !<>( exp, <>(ex), std::forward(f)...) ) throw;` - -IMPORTANT: In case the dynamic type of `ex` does not derive from `leaf::error` and the call to `handle_error` succeeds, the association between the `leaf::error` value returned by `<>` and the currently unhandled exceptions is broken. - -''' - -[[diagnostic_output-exception]] -==== `diagnostic_output` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - void diagnostic_output( std::ostream & os, expect const & exp, std::exception const & ex ); - -} } ----- - -Effects: :: Equivalent to: `<>( os, exp, <>(ex) );` - -''' - -[[LEAF_EXCEPTION]] -==== `LEAF_EXCEPTION` - -[source,c++] -.#include ----- -#define LEAF_EXCEPTION(...) <> ----- - -Effects: :: This is a variadic macro which forwards its arguments to the function template <>, in addition capturing `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]`, in a `<>` object. - -''' - -[[LEAF_THROW]] -==== `LEAF_THROW` - -[source,c++] -.#include ----- -#define LEAF_THROW(...) throw LEAF_EXCEPTION(__VA_ARGS__) ----- - -Effects: :: Throws the exception object returned by <>. - -''' - -[[diagnostic_output_current_exception]] -=== Diagnostic Output for the Current Exception -include::synopses/diagnostic_output_current_exception.adoc[] - -Effects: :: This function prints a developer-friendly (but not user-friendly) diagnostic information about the current exception to `os`. - -Example: :: -+ -[source,c++] ----- -int main() -{ - leaf::expect exp; - - try - { - f(); - return 0; - } - catch(...) - { - leaf::diagnostic_output_current_exception(std::cerr,exp); - return 1; - } -} ----- -+ -[.text-right] -<> | `<>` - -''' - -=== Transporting of Exceptions between Threads - -include::synopses/exception_capture.adoc[] - -This header defines functions that can be used to transport exceptions and associated error objects from one thread to another. - -''' - -[[capture_exception]] -==== `capture_exception` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - <> capture_exception( F && f ) noexcept; - -} } ----- - -Requirements: :: `F` must be a function type. - -Returns: :: A function of unspecified type which wraps `f` and, when called, forwards all of the arguments to `f` and returns its return value to the caller, except if `f` throws, in which case the specified `E...` types and the exception object are captured and transported in another exception object. - -NOTE: The `capture_exception` function is designed for use with `<>`, to effectively transport error objects across thread boundaries (for example see <>). - -''' - -[[get]] -==== `get` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - decltype(std::declval().get()) get( Future && f ); - -} } ----- - -Requirements: :: `Future` must be a `std::future` or other similar type used to recover future values by a member function `get()`. - -Returns: :: `f.get()`. - -Throws: :: Any exception thrown by `f.get()`. If the future function was launched using `<>`, all `E...` type objects captured in the worker thread are transported to the calling thread. - -IMPORTANT: To store and to access the transported error objects, the calling thread must provide suitable `<>` object(s). +The <>, <> and <> macros capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` into a `e_source_location` object. [[techniques]] == Programming Techniques @@ -2007,7 +1713,7 @@ IMPORTANT: To store and to access the transported error objects, the calling thr [[technique_preload]] === Preloading Errors -Consider the following exception type, designed for use without LEAF: +Consider the following exception type: [source,c++] ---- @@ -2052,6 +1758,7 @@ Using LEAF, a file name may be associated with any exception after it has been t [source,c++] ---- +class file_open_error: public std::exception { }; class file_read_error: public std::exception { }; struct e_file_name { std::string value; }; struct e_errno { int value; }; @@ -2083,32 +1790,27 @@ void process_file( char const * name ) [.text-right] `<>` | <> -The key is the call to `<>`: it gets the file name ready to be associated with any exception that escapes `process_file`. This is fully automatic, and works regardless of whether the exception is thrown later in the same function, or by `read_file`, or by some third-party function we call. +The key is the call to `<>`: it ensures tha the passed `e_file_name` will be associated with any exception that escapes `process_file`. This is fully automatic, and works regardless of whether the exception is thrown later in the same function, or by `read_file`, or by some third-party function we call. -Now, the `try...catch` that handles exceptions thrown by `process_file` may look like this: +Now, exceptions thrown by `process_file` may be handled like this: [source,c++] ---- -leaf::expect exp; -try -{ - process_file("example.txt"); -} -catch( file_io_error & e ) -{ - std::cerr << "I/O error!\n"; - - leaf::handle_exception( exp, e, - [ ]( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "File name: " << fn.value << ", errno=" << errn << "\n"; - } - ); -} +leaf::try_( + [ ] + { + process_file("example.txt"); + }, + [ ]( leaf::catch_, e_file_name const & fn, e_errno const & errn ) + { + std::cerr << + "I/O error!" << std::endl << + "File name: " << fn.value << ", errno=" << errn << std::endl; + } ); ---- [.text-right] -`<>` | <> +<> | <> NOTE: This technique works exactly the same way when errors are reported using `leaf::<>` rather than by throwing exceptions. @@ -2154,9 +1856,6 @@ void read_file(FILE * f) { } ---- -[.text-right] -`<>` - Ideally, associating `e_errno` with each exception should be automated. One way to achieve this is to not call `fread` directly, but wrap it in another function which checks for `ferror` and associates the `e_errno` with the exception it throws. <> describes how to solve a very similar problem without a wrapper function, but that technique does not work for `e_errno` because `<>` would capture `errno` before a `fread` call was attempted, at which point `errno` is probably `0` -- or, worse, leftover from a previous I/O failure. @@ -2195,47 +1894,41 @@ This works similarly to `preload`, except that capturing of the `errno` is defer [[technique_disparate_error_types]] === Working with Disparate Error Codes -Because most libraries define their own mechanism for reporting errors, programmers often need to use multiple incompatible error-reporting interfaces in the same program. This led to the introduction of `boost::error_code` which later became `std::error_code`. Each `std::error_code` object is assigned an `error_category`. Libraries that communicate errors in terms of `std::error_code` define their own `error_category`. For libraries that do not, the user can "easily" define a custom `error_category` and still translate domain-specific error codes to `std::error_code`. +Because most libraries define their own mechanism for reporting errors, programmers often need to use multiple incompatible error-reporting interfaces in the same program. This led to the introduction of `boost::system::error_code` which later became `std::error_code`. Each `std::error_code` object is assigned an `error_category`. Libraries that communicate errors in terms of `std::error_code` define their own `error_category`. For libraries that do not, the user can "easily" define a custom `error_category` and still translate domain-specific error codes to `std::error_code`. -But let`s take a step back and consider _why_ do we have to express every error in terms of the same static type, `std::error_code`? We need this translation because the {CPP} static type-checking system makes it difficult to write functions that may return error objects of the disparate static types used by different libraries. Outside of this limitation, it would be preferable to be able to write functions that can communicate errors in terms of arbitrary {CPP} types, as needed. +But let's take a step back and consider _why_ do we have to express every error in terms of the same static type, `std::error_code`? We need this translation because the {CPP} static type-checking system makes it difficult to write functions that may return error objects of the disparate static types used by different libraries. Outside of this limitation, it would be preferable to be able to write functions that can communicate errors in terms of arbitrary {CPP} types, as needed. -To drive this point further, consider the real world problem of mixing `boost::error_code` and `std::error_code` in the same program. In theory, both systems are designed to be able to express one error code in terms of the other. In practice, describing a _generic_ system for error categorization in terms of another _generic_ system for error categorization is not trivial. +To drive this point further, consider the real world problem of mixing `boost::system::error_code` and `std::error_code` in the same program. In theory, both systems are designed to be able to express one error code in terms of the other. In practice, describing a _generic_ system for error categorization in terms of another _generic_ system for error categorization is not trivial. Ideally, functions should be able to communicate different error types without having to translate between them, and {CPP} does offer a mechanism that does just that, it's called exception handling. And it is not a coincidence that the attempt to bind {CPP} exception types with the interface of each function, A.K.A. exception specifications, was so problematic that it had to be abandoned (while I am an outspoken proponent of exception handling, I do acknowledge that in practice, often for good reasons, exception handling may not be available or permitted). -LEAF solves this problem without using exception handling: a scope that is able to handle either `std::error_code` or `boost::error_code` would look like this: +LEAF solves this problem without using exception handling: a scope that is able to handle either `std::error_code` or `boost::system::error_code` would look like this: [source,c++] ---- -{ - leaf::expect exp; +return handle_some( - if( leaf::result r = f() ) + [ ]() -> leaf::result { - //Success - } - else + //Call operations which may report std::error_code and boost::system::error_code. + //Return errors via result. + }, + + [ ]( std::error_code const & e ) { - bool matched = leaf::handle_error( exp, r, + //Handle std::error_code + }, - [ ]( std::error_code const & e ) - { - //Handle std::error_code - }, - - [ ]( boost::error_code const & e ) - { - //Handle boost::error_code - } - ); - } -} + [ ]( boost::system::error_code const & e ) + { + //Handle boost::system::error_code + } ); ---- [.text-right] -`<>` | <> +<> | <> -And here is a function which is able to report either `std::error_code` or `boost::error_code`: +And here is a function which, using LEAF, forwards either `std::error_code` or `boost::system::error_code` objects reported by lower level functions: [source,c++] ---- @@ -2246,21 +1939,19 @@ leaf::result f() //Success } else - return leaf::error(ec); + return leaf::new_error(ec); - if( boost::error_code ec = g2() ) + if( boost::system::error_code ec = g2() ) { //Success } else - return leaf::error(ec); + return leaf::new_error(ec); } ---- [.text-right] -<> | `<>` - -Of course, under refactoring `f` can be changed to return any other error type as needed, without requiring a change in its static interface. With LEAF, such changes become transparent to any error-neutral function that calls and forwards errors from `f` to its caller; only the error handling scope needs to be updated to handle the new error types returned by `f`. +<> | <> ''' @@ -2292,9 +1983,9 @@ catch( std::exception const & e ) [.text-right] <> | <> -The reason we need to use `<>` is that not all exception types derive from `leaf::<>`. If the caught exception has a `leaf::error` subobject, `get_error` will return that `leaf::error` value. Also, such exceptions can be intercepted by `catch( error e )` if needed. +The reason we need to use <> is that not all exception types derive from `leaf::<>`. If the caught exception has a `leaf::error` subobject, `get_error` will return that `leaf::error` value. Also, such exceptions can be intercepted by `catch( error e )` if needed. -But if the caught exception doesn't have a `leaf::error` subobject, `get_error` returns an unspecified `leaf::error` value, which is temporarily associated with any and all current exceptions, until successfully handled by `<>`. While this association is imperfect (because it does not pertain to a specific exception object) it is the best that can be done in this case. +But if the caught exception doesn't have a `leaf::error` subobject, `get_error` returns an unspecified `leaf::error` value, which is temporarily associated with any and all current exceptions, until successfully handled by <>. While this association is imperfect (because it does not pertain to a specific exception object) it is the best that can be done in this case. ''' diff --git a/doc/synopses/common.adoc b/doc/synopses/common.adoc index cce5ffd..11d468a 100644 --- a/doc/synopses/common.adoc +++ b/doc/synopses/common.adoc @@ -34,16 +34,8 @@ namespace boost { namespace leaf { friend std::ostream & operator<<( std::ostream & os, e_source_location const & x ); }; - struct e_unexpected - { - char const * (*first_type)(); - int count; - - friend std::ostream & operator<<( std::ostream & os, e_unexpected const & x ); - }; - } } ---- [.text-right] -<> | <> | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> diff --git a/example/lua_callback_eh.cpp b/example/lua_callback_eh.cpp index bc73e93..4791c46 100644 --- a/example/lua_callback_eh.cpp +++ b/example/lua_callback_eh.cpp @@ -18,8 +18,6 @@ extern "C" { namespace leaf = boost::leaf; -struct lua_failure: std::exception { }; - enum do_work_error_code { ec1=1, @@ -50,7 +48,7 @@ int do_work( lua_State * L ) else { // Remarkably, the Lua interpreter is exception-safe. So, just throw. - throw leaf::exception(lua_failure(),ec1); + throw leaf::exception(std::exception(),ec1); } } @@ -88,11 +86,11 @@ int call_lua( lua_State * L ) lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); if( int err=lua_pcall(L,0,1,0) ) { - // Something went wrong with the call, so we'll throw lua_failure. + // Something went wrong with the call, so we'll throw std::exception. // This is definitely not a do_work failure, because it throws on error. auto propagate = leaf::preload( e_lua_error_message{lua_tostring(L,1)} ); lua_pop(L,1); - throw leaf::exception( lua_failure(), e_lua_pcall_error{err} ); + throw leaf::exception( std::exception(), e_lua_pcall_error{err} ); } else { @@ -106,29 +104,22 @@ int call_lua( lua_State * L ) int main() noexcept { std::shared_ptr L=init_lua_state(); - leaf::static_store exp; + for( int i=0; i!=10; ++i ) - try - { - int r = call_lua(&*L); - std::cout << "do_work succeeded, answer=" << r << '\n'; - } - catch( lua_failure const & e ) - { - handle_exception( exp, e, + leaf::try_( + [&] + { + int r = call_lua(&*L); + std::cout << "do_work succeeded, answer=" << r << '\n'; + }, + [ ]( do_work_error_code const & e ) + { + std::cout << "Got do_work_error_code = " << e << "!\n"; + }, + [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) + { + std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; + } ); - // Handle do_work failures: - [ ]( do_work_error_code const & e ) - { - std::cout << "Got do_work_error_code = " << e << "!\n"; - }, - - // Handle all other lua_pcall failures: - [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) - { - std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; - } - ); - } return 0; } diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index edf3dc8..d219e89 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -112,28 +112,22 @@ int main() noexcept { std::shared_ptr L=init_lua_state(); - leaf::static_store exp; - for( int i=0; i!=10; ++i ) - if( leaf::result r = call_lua(&*L) ) - std::cout << "do_work succeeded, answer=" << *r << '\n'; - else - { - bool matched = exp.handle_error( r, + leaf::handle_all( + [&]() -> leaf::result + { + LEAF_AUTO(answer, call_lua(&*L)); + std::cout << "do_work succeeded, answer=" << answer << '\n'; + return { }; + }, + [ ]( do_work_error_code e ) + { + std::cout << "Got do_work_error_code = " << e << "!\n"; + }, + [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) + { + std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; + } ); - // Handle e_do_work failures: - [ ]( do_work_error_code e ) - { - std::cout << "Got do_work_error_code = " << e << "!\n"; - }, - - // Handle all other lua_pcall failures: - [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) - { - std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; - } - ); - assert(matched); - } return 0; } diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index 4f492a9..ebb742a 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -356,25 +356,25 @@ namespace boost { namespace leaf { //////////////////////////////////////// - template struct acceptable_last_handler_argument: std::false_type { }; - template struct acceptable_last_handler_argument: is_e_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; - template <> struct acceptable_last_handler_argument: std::true_type { }; + template struct argument_matches_any_error: std::false_type { }; + template struct argument_matches_any_error: is_e_type { }; + template <> struct argument_matches_any_error: std::true_type { }; + template <> struct argument_matches_any_error: std::true_type { }; + template <> struct argument_matches_any_error: std::true_type { }; template - struct ensure_last_handler_matches: std::false_type + struct handler_matches_any_error: std::false_type { }; template class L, class Car, class... Cdr> - struct ensure_last_handler_matches> + struct handler_matches_any_error> { - constexpr static bool value = acceptable_last_handler_argument::value && ensure_last_handler_matches>::value; + constexpr static bool value = argument_matches_any_error::value && handler_matches_any_error>::value; }; template class L> - struct ensure_last_handler_matches>: std::true_type + struct handler_matches_any_error>: std::true_type { }; } @@ -443,16 +443,17 @@ namespace boost { namespace leaf { typename function_traits::return_type handle_error( error_info const & ei, F && f ) const { using namespace static_store_internal; - static_assert(ensure_last_handler_matches::mp_args>::value, - "The last handler for handle_all must match any error."); - return call_handler( ei, std::forward(f), typename function_traits::mp_args{ } ); + if( handler_matches_any_error::mp_args>::value || check_handler( ei, typename function_traits::mp_args{ } ) ) + return call_handler( ei, std::forward(f), typename function_traits::mp_args{ } ); + else + std::terminate(); } template typename function_traits::return_type handle_error( error_info const & ei, CarF && car_f, CdarF && cdar_f, CddrF && ... cddr_f ) const { using namespace static_store_internal; - if( check_handler( ei, typename function_traits::mp_args{ } ) ) + if( handler_matches_any_error::mp_args>::value || check_handler( ei, typename function_traits::mp_args{ } ) ) return call_handler( ei, std::forward(car_f), typename function_traits::mp_args{ } ); else return handle_error( ei, std::forward(cdar_f), std::forward(cddr_f)...); diff --git a/meson.build b/meson.build index 3477db0..bf0091a 100644 --- a/meson.build +++ b/meson.build @@ -89,7 +89,7 @@ foreach e : examples endforeach if get_option('build_lua_examples') -# lua=subproject('lua').get_variable('all') -# executable('lua_callback_result', 'example/lua_callback_result.cpp', dependencies: [ leaf, lua ] ) -# executable('lua_callback_eh', 'example/lua_callback_eh.cpp', dependencies: [ leaf, lua ] ) + lua=subproject('lua').get_variable('all') + executable('lua_callback_result', 'example/lua_callback_result.cpp', dependencies: [ leaf, lua ] ) + executable('lua_callback_eh', 'example/lua_callback_eh.cpp', dependencies: [ leaf, lua ] ) endif From 0f4dc4f6377742bd01955a6d6d04ebe7bef404dd Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Mon, 14 Jan 2019 17:24:52 -0800 Subject: [PATCH 30/34] cosmetic changes --- doc/leaf.adoc | 61 ++++++++++++---------- example/lua_callback_eh.cpp | 23 ++++++-- example/lua_callback_result.cpp | 15 +++++- example/print_file_eh.cpp | 56 +++++++++++++++----- example/print_file_result.cpp | 46 +++++++--------- include/boost/leaf/detail/static_store.hpp | 6 +-- 6 files changed, 127 insertions(+), 80 deletions(-) diff --git a/doc/leaf.adoc b/doc/leaf.adoc index aa17bc5..d58b391 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -2,13 +2,13 @@ :icons: font :prewrap!: :source-highlighter: coderay -:stylesheet: ../zajo.css +:stylesheet: zajo.css = LEAF Low-latency Error Augmentation Framework for C++11 :toclevels: 3 -:toc: left +//:toc: right :toc-title: -:sourcedir: .. +:sourcedir: . [abstract] == Abstract @@ -2036,7 +2036,15 @@ enum do_work_error_code ec1=1, ec2 }; + +namespace boost { namespace leaf { + + template<> struct is_e_type: std::true_type { }; + +} } ---- +[.text-right] +<> We're now ready to define the `do_work` callback function: @@ -2076,7 +2084,7 @@ leaf::result call_lua( lua_State * L ) { auto propagate = leaf::preload( e_lua_error_message{lua_tostring(L,1)} ); //<2> lua_pop(L,1); - return leaf::error( e_lua_pcall_error{err} ); + return leaf::new_error( e_lua_pcall_error{err} ); } else { @@ -2101,37 +2109,32 @@ int main() noexcept { std::shared_ptr L=init_lua_state(); - leaf::expect exp; //<1> - for( int i=0; i!=10; ++i ) - if( leaf::result r = call_lua(&*L) ) - std::cout << "do_work succeeded, answer=" << *r << '\n'; //<2> - else - { - bool matched = handle_error( exp, r, + leaf::handle_all( + [&]() -> leaf::result + { + LEAF_AUTO(answer, call_lua(&*L)); + std::cout << "do_work succeeded, answer=" << answer << '\n'; <1> + return { }; + }, + [ ]( do_work_error_code e ) <2> + { + std::cout << "Got do_work_error_code = " << e << "!\n"; + }, + [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) <3> + { + std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; + } ); - [ ]( do_work_error_code e ) //<3> - { - std::cout << "Got do_work_error_code = " << e << "!\n"; - }, - - [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) //<4> - { - std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; - } - ); - assert(matched); - } return 0; } ---- [.text-right] -`<>` | <> +<> | <> -<1> Tell LEAF what error objects are expected. -<2> If the call to `call_lua` succeeded, just print the answer. -<3> Handle `e_do_work` failures. -<4> Handle all other `lua_pcall` failures. +<1> If the call to `call_lua` succeeded, just print the answer. +<2> Handle `e_do_work` failures. +<3> Handle all other `lua_pcall` failures. [NOTE] -- @@ -2143,7 +2146,7 @@ Remarkably, the Lua interpreter is {CPP} exception-safe, even though it is writt ''' [[technique_transport]] -=== Transporting Error Objects between Threads +=== Transporting E-Objects Between Threads With LEAF, error objects use automatic storage duration, stored inside `<>` instances. When using concurrency, we need a mechanism to detach error objects from a worker thread and transport them to another thread where errors are handled. diff --git a/example/lua_callback_eh.cpp b/example/lua_callback_eh.cpp index 4791c46..5374f2f 100644 --- a/example/lua_callback_eh.cpp +++ b/example/lua_callback_eh.cpp @@ -78,8 +78,8 @@ std::shared_ptr init_lua_state() // in Lua, and returns the value from do_work, which is written in C++ and // registered with the Lua interpreter as a C callback. -// If do_work succeeds, we return the resulting int answer. If it fails, we'll -// communicate that failure to our caller. +// If do_work succeeds, we return the resulting int answer. +// If it fails, we'll communicate that failure to our caller. int call_lua( lua_State * L ) { // Ask the Lua interpreter to call the global Lua function call_do_work. @@ -106,20 +106,33 @@ int main() noexcept std::shared_ptr L=init_lua_state(); for( int i=0; i!=10; ++i ) + { leaf::try_( + [&] { - int r = call_lua(&*L); - std::cout << "do_work succeeded, answer=" << r << '\n'; + int answer = call_lua(&*L); + std::cout << "do_work succeeded, answer=" << answer << '\n'; }, - [ ]( do_work_error_code const & e ) + + [ ]( do_work_error_code e ) { std::cout << "Got do_work_error_code = " << e << "!\n"; }, + [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) { std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; + }, + + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; } ); + } return 0; } diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index d219e89..8b99874 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -113,21 +113,34 @@ int main() noexcept std::shared_ptr L=init_lua_state(); for( int i=0; i!=10; ++i ) + { leaf::handle_all( + [&]() -> leaf::result - { + { LEAF_AUTO(answer, call_lua(&*L)); std::cout << "do_work succeeded, answer=" << answer << '\n'; return { }; }, + [ ]( do_work_error_code e ) { std::cout << "Got do_work_error_code = " << e << "!\n"; }, + [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) { std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; + }, + + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; } ); + } return 0; } diff --git a/example/print_file_eh.cpp b/example/print_file_eh.cpp index 6ebfb77..56b5882 100644 --- a/example/print_file_eh.cpp +++ b/example/print_file_eh.cpp @@ -4,9 +4,8 @@ // 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 the basic use of LEAF to augment error conditions with -// additional information when using exceptions to report failures. See print_file_result.cpp -// for the variant that doesn't use exceptions. +// This is a short but complete program that reads a text file in a buffer and prints it to std::cout, +// using LEAF to handle errors. It uses exception handling. #include #include @@ -31,7 +30,7 @@ struct input_eof_error : virtual input_file_error { }; // We will handle all failures in our main function, but first, here are the declarations of the functions it calls, each -// communicating failures using leaf::result: +// communicating failures by throwing exceptions // Parse the command line, return the file name. char const * parse_command_line( int argc, char const * argv[ ] ); @@ -55,11 +54,14 @@ int main( int argc, char const * argv[ ] ) [&] { char const * file_name = parse_command_line(argc,argv); + + auto propagate = leaf::preload( leaf::e_file_name{file_name} ); + std::shared_ptr f = file_open( file_name ); - auto propagate1 = leaf::preload( leaf::e_file_name{file_name} ); + int s = file_size(*f); - std::string buffer( 1+file_size(*f), '\0' ); + std::string buffer( 1 + s, '\0' ); file_read(*f,&buffer[0],buffer.size()-1); auto propagate2 = leaf::defer([ ] { return leaf::e_errno{errno}; } ); @@ -69,36 +71,58 @@ int main( int argc, char const * argv[ ] ) return 0; }, + // Each of the lambdas below is an error handler. LEAF will consider them, in order, and call the first one that matches + // the available error information. + + // This handler will be called if the error includes: + // - a caught exception of type input_file_open_error, and + // - an object of type leaf::e_errno that has .value equal to ENOENT, and + // - an object of type leaf::e_file_name. [ ]( leaf::catch_, leaf::match, leaf::e_file_name const & fn ) { std::cerr << "File not found: " << fn.value << std::endl; return 1; }, + // This handler will be called if the error includes: + // - a caught exception of type input_file_open_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. [ ]( leaf::catch_, leaf::e_errno const & errn, leaf::e_file_name const & fn ) { std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; return 2; }, + // This handler will be called if the error includes: + // - a caught exception of type input_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. [ ]( leaf::catch_, leaf::e_errno const & errn, leaf::e_file_name const & fn ) { std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; return 3; }, + // This handler will be called if the error includes: + // - a caught exception of type std::ostream::failure, and + // - an object of type leaf::e_errno (regardless of its .value), [ ]( leaf::catch_, leaf::e_errno const & errn ) { std::cerr << "Output error, errno=" << errn << std::endl; return 4; }, + // This handler will be called if the error includes a caught exception of type bad_command_line. [ ]( leaf::catch_ ) { std::cout << "Bad command line argument" << std::endl; return 5; }, + // This last handler matches any error: it prints diagnostic information to help debug logic errors in the program, since it + // failed to match an appropriate error handler to the error condition it encountered. In this program this handler will + // never be called. [ ]( leaf::error_info const & unmatched ) { std::cerr << @@ -110,12 +134,16 @@ int main( int argc, char const * argv[ ] ) } +// Implementations of the functions called by main: + + // Parse the command line, return the file name. char const * parse_command_line( int argc, char const * argv[ ] ) { - if( argc!=2 ) - throw bad_command_line(); - return argv[1]; + if( argc==2 ) + return argv[1]; + else + throw leaf::exception(bad_command_line()); } @@ -125,7 +153,7 @@ std::shared_ptr file_open( char const * file_name ) if( FILE * f = fopen(file_name,"rb") ) return std::shared_ptr(f,&fclose); else - throw leaf::exception( input_file_open_error(), leaf::e_errno{errno} ); + throw leaf::exception(input_file_open_error(), leaf::e_errno{errno}); } @@ -136,14 +164,14 @@ int file_size( FILE & f ) auto propagate = leaf::defer( [ ] { return leaf::e_errno{errno}; } ); if( fseek(&f,0,SEEK_END) ) - throw input_file_size_error(); + throw leaf::exception(input_file_size_error()); int s = ftell(&f); if( s==-1L ) - throw input_file_size_error(); + throw leaf::exception(input_file_size_error()); if( fseek(&f,0,SEEK_SET) ) - throw input_file_size_error(); + throw leaf::exception(input_file_size_error()); return s; } @@ -158,5 +186,5 @@ void file_read( FILE & f, void * buf, int size ) throw leaf::exception( input_file_read_error(), leaf::e_errno{errno} ); if( n!=size ) - throw input_eof_error(); + throw leaf::exception(input_eof_error()); } diff --git a/example/print_file_result.cpp b/example/print_file_result.cpp index 9df97b7..1633e8f 100644 --- a/example/print_file_result.cpp +++ b/example/print_file_result.cpp @@ -55,29 +55,22 @@ int main( int argc, char const * argv[ ] ) [&]() -> leaf::result { - leaf::result file_name = parse_command_line(argc,argv); - if( !file_name ) - return file_name.error(); + LEAF_AUTO(file_name, parse_command_line(argc,argv)); - auto propagate = leaf::preload( leaf::e_file_name{*file_name} ); + auto propagate = leaf::preload( leaf::e_file_name{file_name} ); - leaf::result> f = file_open(*file_name); - if( !f ) - return f.error(); + LEAF_AUTO(f, file_open(file_name)); - leaf::result s = file_size(**f); - if( !s ) - return s.error(); + LEAF_AUTO(s, file_size(*f)); - std::string buffer( 1 + *s, '\0' ); - leaf::result fr = file_read(**f, &buffer[0], buffer.size()-1); - if( !fr ) - return fr.error(); + std::string buffer( 1 + s, '\0' ); + LEAF_CHECK(file_read(*f, &buffer[0], buffer.size()-1)); + auto propagate2 = leaf::defer([ ] { return leaf::e_errno{errno}; } ); std::cout << buffer; std::cout.flush(); if( std::cout.fail() ) - return leaf::new_error( cout_error, leaf::e_errno{errno} ); + return leaf::new_error( cout_error ); return 0; }, @@ -133,9 +126,7 @@ int main( int argc, char const * argv[ ] ) // This last handler matches any error: it prints diagnostic information to help debug logic errors in the program, since it // failed to match an appropriate error handler to the error condition it encountered. In this program this handler will - // never be called, but it is required by handle_all because, well, it must handle all errors (the alternative is to use - // handle_some instead, which doesn't require a "catch all" last-resort handler; instead, if it fails to find a suitable - // handler for an error, it returns the error to its caller.) + // never be called. [ ]( leaf::error_info const & unmatched ) { std::cerr << @@ -143,8 +134,7 @@ int main( int argc, char const * argv[ ] ) "Cryptic diagnostic information follows" << std::endl << unmatched; return 6; - } - ); + } ); } @@ -167,24 +157,25 @@ leaf::result> file_open( char const * file_name ) if( FILE * f = fopen(file_name,"rb") ) return std::shared_ptr(f,&fclose); else - return leaf::new_error( input_file_open_error, leaf::e_errno{errno} ); + return leaf::new_error(input_file_open_error, leaf::e_errno{errno}); } // Return the size of the file. leaf::result file_size( FILE & f ) { - auto propagate = leaf::defer([ ] { return leaf::e_errno{errno}; } ); + // All exceptions escaping this function will automatically propagate errno. + auto propagate = leaf::defer([ ] { return leaf::e_errno{errno}; }); if( fseek(&f,0,SEEK_END) ) - return leaf::new_error( input_file_size_error ); + return leaf::new_error(input_file_size_error); int s = ftell(&f); if( s==-1L ) - return leaf::new_error( input_file_size_error ); + return leaf::new_error(input_file_size_error); if( fseek(&f,0,SEEK_SET) ) - return leaf::new_error( input_file_size_error ); + return leaf::new_error(input_file_size_error); return s; } @@ -194,11 +185,12 @@ leaf::result file_size( FILE & f ) leaf::result file_read( FILE & f, void * buf, int size ) { int n = fread(buf,1,size,&f); + if( ferror(&f) ) - return leaf::new_error( input_file_read_error, leaf::e_errno{errno} ); + return leaf::new_error(input_file_read_error, leaf::e_errno{errno}); if( n!=size ) - return leaf::new_error( input_eof_error ); + return leaf::new_error(input_eof_error); return { }; } diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index ebb742a..f93d7e7 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -443,10 +443,8 @@ namespace boost { namespace leaf { typename function_traits::return_type handle_error( error_info const & ei, F && f ) const { using namespace static_store_internal; - if( handler_matches_any_error::mp_args>::value || check_handler( ei, typename function_traits::mp_args{ } ) ) - return call_handler( ei, std::forward(f), typename function_traits::mp_args{ } ); - else - std::terminate(); + static_assert( handler_matches_any_error::mp_args>::value, "The last handler passed to handle_all must match any error." ); + return call_handler( ei, std::forward(f), typename function_traits::mp_args{ } ); } template From 17fc004d55c495748ca438d5d1ce5ab2bf114edd Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Mon, 14 Jan 2019 23:51:53 -0800 Subject: [PATCH 31/34] cosmetic changes, documentation update --- doc/leaf.adoc | 638 ++++++++++++++++---------- doc/synopses/capture_exception.adoc | 4 - doc/synopses/error.adoc | 2 +- doc/synopses/exception_to_result.adoc | 6 +- doc/synopses/handle.adoc | 2 +- doc/synopses/throw.adoc | 2 +- doc/synopses/try.adoc | 8 +- example/capture_eh.cpp | 2 +- example/capture_result.cpp | 2 +- example/exception_to_result.cpp | 4 +- 10 files changed, 419 insertions(+), 251 deletions(-) diff --git a/doc/leaf.adoc b/doc/leaf.adoc index d58b391..03442aa 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -6,7 +6,7 @@ = LEAF Low-latency Error Augmentation Framework for C++11 :toclevels: 3 -//:toc: right +:toc: right :toc-title: :sourcedir: . @@ -20,16 +20,16 @@ LEAF is a {CPP}11 error handling library. Features: * No dynamic memory allocations.footnote:[Except when error objects are transported between threads, see <>.] -* Any error-related object of any type is efficiently delivered to the correct error handler. +* Any error-related object of any movable type is efficiently delivered to the correct error handler. -* Compatible with `std::error_code`, `errno` and any other `noexcept`-moveable type. - -* Use with or without exception handling. +* Compatible with `std::error_code`, `errno` and any other error code type. * Support for multi-thread programming. +* Can be used with or without exception handling. + [.text-right] -https://github.com/zajo/leaf[GitHub] | <> <> | <> | <> | <> +https://github.com/zajo/leaf[GitHub] | <> | <> | <> ==== [[introduction]] @@ -89,17 +89,17 @@ leaf::result file_read( FILE & f, void * buf, int size ); What's this `result` template, you ask? It's a simple variant: it holds a value of type `T` or else it represents an error condition. This way, functions can communicate failures -- and we don't even need an error code to represent success. Which makes sense if you think about it. -Now on to the `main` function: it brings everything together and handles all the errors that may occur. Did I say *all* the errors? I did, so don't be surprised when I tell you that this is done using `leaf::handle_all`. It has the following signature: +Now on to the `main` function: it brings everything together and handles all the errors that may occur. Did I say *all* the errors? I did, so we'll use `leaf::handle_all`. It has the following signature: [source,c++] ---- template -<> handle_all( TryBlock && try_block, Handler && ... handler ); +<> handle_all( TryBlock && try_block, Handler && ... handler ); ---- `TryBlock` is a function type, almost always a lambda, which is required to return an instance of the `result` template. The first thing `handle_all` does is invoke the `try_block`. If the returned object indicates success, `handle_all` returns the contained value; otherwise it calls a suitable error handling function from the `handler...` list. -Now, let's see just what kind of a `TryBlock` does `main` pass to `handle_all`: +Now, let's see just what kind of a `TryBlock` will our `main` function pass to `handle_all`: [source,c++] ---- @@ -141,20 +141,18 @@ int main( int argc, char const * argv[ ] ) ); <7> } ---- -[.text-right] -<> | <> | <> | <> | <> <1> Our `TryBlock` returns a `result`. In case of success, it will hold `0`, which will be returned from `main` to the OS. <2> If `parse_command_line` returns an error, we return that error to `handle_all` (which invoked us). Otherwise, the returned `file_name` stores a value of type `char const *`, which we can access by "dereferencing" it: `*file_name`. <3> From now on, all errors escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name { std::string value; };` ). It's as if every time one of the following functions reports an error, `preload` says "wait, put this `e_file_name` thing with the error, it's important!" -<4> Call more functions, forward any failures to the caller... +<4> Call more functions, forward each failure to the caller... <5> ...but this is slightly different: we didn't get a failure via `result` from another function, this is our own error we've detected! We return a `new_error`, passing the `cout_error` error code and the system `errno` (LEAF defines `struct e_errno { int value; };` ). <6> List of error handler goes here. We'll see that later. <7> This concludes the `handle_all` arguments -- as well as our program! Nice and simple! Writing the `TryBlock`, we concentrate on the "no errors" code path -- if we encounter any error we just return it to `handle_all` for processing. Well, that's if we're being good and using RAII for automatic clean-up -- which we are, `shared_ptr` will automatically close the file for us. -Now let's look at the juicy second part of the call to `handle_all`, which lists our error handler: +Now let's look at the juicy second part of the call to `handle_all`, which lists our error handlers: [source,c++] ---- @@ -215,10 +213,8 @@ int main( int argc, char const * argv[ ] ) ); } ---- -[.text-right] -<> | <> | <> -<1> This is the `TryBlock` from the previous listing, it does all the work and bails out if it encounters an error. In that case, `handle_all` will consider the error handler that follow, in order, and it will call the first one that can deal with the error: +<1> This is the `TryBlock` from the previous listing, it does all the work and bails out if it encounters an error. In that case, `handle_all` will consider the error handlers that follow, in order, and it will call the first one that can deal with the error: <2> This handler will be called if the error includes: + pass:[•] an object of type `error_code` equal to `input_file_open_error`, and + @@ -241,7 +237,7 @@ pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), <6> This handler will be called if the error includes an object of type `error_code` equal to `bad_command_line`. -<7> This last handler matches any error: it prints diagnostic information to help debug logic errors in the program, since it failed to match an appropriate error handler to the error condition it encountered. In this program this handler will never be called, but it is required by `handle_all` because, well, it must handle all errors (the alternative is to use `handle_some` instead, which doesn't require a "catch all" last-resort handler; instead, if it fails to find a suitable handler for an error, it returns the error to its caller.) +<7> This last handler matches any error: it prints diagnostic information to help debug logic errors in the program, since it failed to match an appropriate error handler to the error condition it encountered. In this program this handler will never be called, but it is required by `handle_all` because, well, it must handle all errors (the alternative is to use `handle_some` instead, which doesn't require a "catch all" last-resort handler; instead, if it fails to find a suitable handler for an error, it returns the error to its caller). To conclude this introduction, let's look at one of the error-reporting functions that our `TryBlock` calls, for example `file_open`: @@ -255,10 +251,8 @@ leaf::result> file_open( char const * file_name ) return leaf::new_error( input_file_open_error, leaf::e_errno{errno} ); } ---- -[.text-right] -<> | <> | <> -If `fopen` succeeds, it returns a `shared_ptr` which will automatically call `fclose` when needed. If it fails, we report an error by calling `new_error`, which takes any number of error objects to send with the error. In this case we pass the error code (`input_file_open_error`), as well as the system `errno` (LEAF defines `struct e_errno { int value; }`). +If `fopen` succeeds, we return a `shared_ptr` which will automatically call `fclose` as needed. If `fopen` fails, we report an error by calling `new_error`, which takes any number of error objects to send with the error. In this case we pass the error code (`input_file_open_error`), as well as the system `errno` (LEAF defines `struct e_errno { int value; }`). NOTE: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[here]. The https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[other] version of the same program uses exception handling to report errors (see <>). @@ -310,7 +304,7 @@ The `main` function brings everything together and handles all the exceptions th [source,c++] ---- template -<> try_( TryBlock && try_block, Handler && ... handler ); +<> try_( TryBlock && try_block, Handler && ... handler ); ---- `TryBlock` is a function type, almost always a lambda; `try_` simply returns the value returned by the `try_block`, catching any exception it throws. In that case `try_` calls a suitable error handling function from the `handler...` list. @@ -328,9 +322,10 @@ int main( int argc, char const * argv[ ] ) [&] <2> { char const * file_name = parse_command_line(argc,argv); <3> - std::shared_ptr f = file_open( file_name ); - auto propagate1 = leaf::preload( leaf::e_file_name{file_name} ); <4> + auto propagate = leaf::preload( leaf::e_file_name{file_name} ); <4> + + std::shared_ptr f = file_open( file_name ); std::string buffer( 1+file_size(*f), '\0' ); file_read(*f,&buffer[0],buffer.size()-1); @@ -360,7 +355,7 @@ int main( int argc, char const * argv[ ] ) As it is always the case when using exception handling, as long as our `TryBlock` is exception-safe, we can concentrate on the "no errors" code path. Of course, our `TryBlock` is exception-safe, since `shared_ptr` will automatically close the file for us in case an exception is thrown. -Now let's look at the second part of the call to `try_`, which lists the error handler: +Now let's look at the second part of the call to `try_`, which lists the error handlers: [source,c++] ---- @@ -421,12 +416,10 @@ int main( int argc, char const * argv[ ] ) } ); } ---- -[.text-right] -<> | <> | <> | <> <1> Configure std::cout to throw on error. -<2> This is the `TryBlock` from the previous listing; if it throws, `try_` will catch the exception, then consider the error handler below, in order, and it will call the first one that can deal with the error: +<2> This is the `TryBlock` from the previous listing; if it throws, `try_` will catch the exception, then consider the error handlers that follow, in order, and it will call the first one that can deal with the error: <3> This handler will be called if: + pass:[•] an `input_file_open_error` exception was caught, with + @@ -449,7 +442,7 @@ pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), <7> This handler will be called if a `bad_command_line` exception was caught. -<8> If `try_` fails to find an appropriate handler, it will re-throw the exception. But this is the `main` function which should handle all exceptions, so this last handler matches any error and prints diagnostic information, to help debug logic errors in the program. +<8> If `try_` fails to find an appropriate handler, it will re-throw the exception. But this is the `main` function which should handle all exceptions, so this last handler matches any error and prints diagnostic information, to help debug logic errors. To conclude this introduction, let's look at one of the error-reporting functions that our `TryBlock` calls, for example `file_open`: @@ -463,52 +456,64 @@ std::shared_ptr file_open( char const * file_name ) throw leaf::exception( input_file_open_error(), leaf::e_errno{errno} ); } ---- -[.text-right] -<> | <> -If `fopen` succeeds, it returns a `shared_ptr` which will automatically call `fclose` when needed. If it fails, we throw the exception object returned by `leaf::exception`, which takes as its first argument an exception object, followed by any number of error objects to send with it. In this case we pass the system `errno` (LEAF defines `struct e_errno { int value; }`). The returned object can be caught as `input_file_open_error`. +If `fopen` succeeds, it returns a `shared_ptr` which will automatically call `fclose` as needed. If `fopen` fails, we throw the exception object returned by `leaf::exception`, which takes as its first argument an exception object, followed by any number of error objects to send with it. In this case we pass the system `errno` (LEAF defines `struct e_errno { int value; }`). The returned object can be caught as `input_file_open_error`. NOTE: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[here]. The https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[other] version of the same program does not use exception handling to report errors (see the <>). [[synopsis]] == Synopsis -=== `error.hpp` +=== Reporting Errors + +==== `error.hpp` include::{sourcedir}/synopses/error.adoc[] ''' -=== `preload.hpp` -include::{sourcedir}/synopses/preload.adoc[] - -''' - -=== `result.hpp` +==== `result.hpp` include::{sourcedir}/synopses/result.adoc[] ''' -=== `handle.hpp` -include::{sourcedir}/synopses/handle.adoc[] - -''' - -=== `throw.hpp` +==== `throw.hpp` include::{sourcedir}/synopses/throw.adoc[] ''' -=== `try.hpp` -include::{sourcedir}/synopses/try.adoc[] +==== `exception_to_result.hpp` +include::{sourcedir}/synopses/exception_to_result.adoc[] + +[.text-right] +<> ''' -=== `common.hpp` +==== `preload.hpp` +include::{sourcedir}/synopses/preload.adoc[] + +''' + +==== `common.hpp` include::{sourcedir}/synopses/common.adoc[] ''' -=== `capture_result.hpp` +=== Error Handling + +==== `handle.hpp` +include::{sourcedir}/synopses/handle.adoc[] + +''' + +==== `try.hpp` +include::{sourcedir}/synopses/try.adoc[] + +''' + +=== Multi-Thread Programming + +==== `capture_result.hpp` include::{sourcedir}/synopses/capture_result.adoc[] [.text-right] @@ -516,18 +521,14 @@ include::{sourcedir}/synopses/capture_result.adoc[] ''' -=== `capture_exception.hpp` +==== `capture_exception.hpp` include::{sourcedir}/synopses/capture_exception.adoc[] -''' - -=== `exception_to_result.hpp` -include::{sourcedir}/synopses/exception_to_result.adoc[] - [.text-right] -`<>` +<> [[reference]] +== pass:[]Referencepass:[] [[e_objects]] == E-Objects @@ -912,6 +913,96 @@ NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[_ ''' +[[exception_to_result]] +=== `exception_to_result` + +include::{sourcedir}/synopses/exception_to_result.adoc[] + +This function can be used to catch exceptions from a lower-level library and conevrt them to `<>`. + +Returns: :: If `f` returns `T`, `exception_to_result` returns `result`. + +Effects: :: Invokes `f` in a `try` block, catching each individual exception type `Ex`, by value, proceeding as follows: ++ +[source,c++] +---- +catch( Ex ex ) +{ + return leaf::new_error(std::move(ex)); +} +---- ++ +A final catch-all is added, as follows: ++ +[source,c++] +---- +catch(...) +{ + return leaf::new_error(std::current_exception()); +} +---- + +[WARNING] +-- +* Catching by value slices each individual exception object. +* The order of the types in the `Ex...` list is significant. +* `std::current_exception()` likely allocates memory dynamically. +* Handlers passed to <> / <> should take the converted-to-result exception objects by `const &` (whereas, in case exceptions are handled directly by <> handlers, you would use <> instead). +-- + +Example: + +[source,c++] +---- +int compute_answer(); //throws + +//Call compute_answer, convert exceptions to result +leaf::result catch_exceptions() +{ + return leaf::catch_exceptions< + ex_type1, + ex_type2>( compute_answer() ); +} +---- + +Later, the `ex_type1` and `ex_type2` exceptions can be processed by <> / <>: + +[source,c++] +---- +return leaf::handle_some( + + [ ] -> leaf::result + { + LEAF_AUTO(answer, catch_exceptions()); + //Use answer + .... + return { }; + }, + + [ ]( ex_tppe1 const & ex1 ) + { + //Handle ex_type1 + .... + return { }; + }, + + [ ]( ex_tppe2 const & ex2 ) + { + //Handle ex_type2 + .... + return { }; + }, + + [ ]( std::exception_ptr const & p ) + { + //Handle any other exception from compute_answer. + .... + return { }; + } ); +---- + +''' + [[preload]] === `preload` @@ -1282,6 +1373,8 @@ However, this isn't possible when we don't control the `throw` site, for example + Similarly, if the exception object caught by `try_` does not transports an `error` value, E-objects are looked up using the `error` value returned by <> just before `try_` goes through its `handler`-matching search. If imperfect, this approach provides the needed association. + +NOTE: <> implements the procedure described above. ++ With the `error` value thus obtained, the `handler`-matching procedure works the same as in <>, with the following addition to the way each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced: * If `a`~i~ is of type `A`~i~ `const &`: @@ -1289,6 +1382,26 @@ With the `error` value thus obtained, the `handler`-matching procedure works the ''' +[[get_error]] +=== `get_error` + +[source,c++] +---- +namespace boost { namespace leaf { + + error get_error( std::exception const & ex ) noexcept; + +} } +---- + +Returns: :: If the dynamic type of `ex` derives from <>, returns the `error` slice of `ex`. Otherwise, returns the result of <>. + +NOTE: Please read the documentation for <> to understand why `get_error` is needed. + +TIP: Whenever possible, pass exceptions you throw throught the <> function template (or use <>). This ensures that the thrown exceptions derive from <>, which is always preferable to the <> hack. + +''' + [[match]] === `match` @@ -1955,12 +2068,98 @@ leaf::result f() ''' +[[technique_exception_to_result]] +=== Converting exceptions to `result` + +It is sometimes necessary to catch exceptions thrown by lower-level library fuction, and report the error through different means, to a higher-level library which may not use exception handling. + +Suppose we have an exception type hierarchy and a function `compute_answer_throws`: + +[source,c++] +---- +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 { }; + +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(); + } +} +---- + +We can write a simple wrapper using <>, which calls `compute_answer_throws and switches to `result` for error handling: + +[source,c++] +---- +leaf::result compute_answer() noexcept +{ + return leaf::exception_to_result( + [ ] + { + return compute_answer_throws(); + } ); +} +---- + +NOTE: As a demonstration, `compute_answer` specifically converts exceptions of type `error_a` or `error_b`, while it leaves `error_c` to be captured by `std::exception_ptr`. + +Here is a simple function which prints successfully computed answers, forwarding any error (originally reported by throwing an exception) to its caller: + +[source,c++] +---- +leaf::result print_answer() noexcept +{ + LEAF_AUTO( answer, compute_answer()); + std::cout << "Answer: " << answer << std::endl; + return { }; +} +---- + +Finally, here is a scope that handles the errors (which used to be exception objects): + +[source,c++] +---- +leaf::handle_all( + + [ ]() -> leaf::result + { + 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; + }, + + [ ] + { + std::cerr << "Unknown error!" << std::endl; + } ); +---- + +NOTE: The complete program illustrating this technique is available https://github.com/zajo/leaf/blob/master/example/exception_to_result.cpp?ts=3[here]. + +''' + [[technique_augment_in_catch]] === Augmenting Exceptions in a `catch` -What makes `<>` and `<>` useful (see <> and <>) is that they automatically include error objects with any exception or error reported by a function. +What makes `<>` and `<>` useful (see <> and <>) is that they automatically include E-objects with any exception or error reported by a function. -But what if we need to include some error object conditionally? When using exception handling, it would be nice to be able to do this in a `catch` statement which selectively augments passing exceptions. +But what if we need to include some E-object conditionally? When using exception handling, it would be nice to be able to do this in a `catch` statement which selectively augments passing exceptions. LEAF supports the following approach, assuming all exceptions derive from `std::exception`: @@ -2110,30 +2309,40 @@ int main() noexcept std::shared_ptr L=init_lua_state(); for( int i=0; i!=10; ++i ) + { leaf::handle_all( + [&]() -> leaf::result { LEAF_AUTO(answer, call_lua(&*L)); std::cout << "do_work succeeded, answer=" << answer << '\n'; <1> return { }; }, + [ ]( do_work_error_code e ) <2> { std::cout << "Got do_work_error_code = " << e << "!\n"; }, + [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) <3> { std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; - } ); + }, - return 0; -} + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + } ); + } ---- [.text-right] <> | <> <1> If the call to `call_lua` succeeded, just print the answer. -<2> Handle `e_do_work` failures. +<2> Handle `do_work` failures. <3> Handle all other `lua_pcall` failures. [NOTE] @@ -2148,15 +2357,13 @@ Remarkably, the Lua interpreter is {CPP} exception-safe, even though it is writt [[technique_transport]] === Transporting E-Objects Between Threads -With LEAF, error objects use automatic storage duration, stored inside `<>` instances. When using concurrency, we need a mechanism to detach error objects from a worker thread and transport them to another thread where errors are handled. +With LEAF, <> use automatic storage duration, stored inside the scope of <>, <> or <> functions. When using concurrency, we need a mechanism to detach E-objects from a worker thread and transport them to another thread where errors are handled. LEAF offers two interfaces for this purpose, one using `result`, and another designed for programs that use exception handling. [[technique_transport-result]] ==== Using `result` -Without exceptions, transporting error objects between threads is as easy as calling `<>`, passing the `<>` object whose contents needs to be transported, and a `<>` which may be in either value state or error state. This gets us a new `<>` object which can be safely sent across thread boundaries. - Let's assume we have a `task` which produces a result but could also fail: [source,c++] @@ -2164,17 +2371,15 @@ Let's assume we have a `task` which produces a result but could also fail: leaf::result task(); ---- -To prepare the returned `result` to be sent across the thread boundary, when we launch the asynchronous task, we wrap it in a lambda function that captures its result: +To prepare the returned `result` to be sent across the thread boundary, when we launch the asynchronous task, we wrap it in call to <>, specifying which E-types we want captured: [source,c++] ---- std::future> launch_task() { - return std::async( std::launch::async, [ ] - { - leaf::expect exp; - return capture(exp,task()); - } ); + return std::async( + std::launch::async, + leaf::capture_result(&task)); } ---- @@ -2185,33 +2390,35 @@ That's it! Later when we `get` the `std::future`, we can process the returned `r [source,c++] ---- -.... -leaf::expect exp; +//std::future> fut; +fut.wait(); -if( leaf::result r = fut.get() ) -{ - //Success! Use *r to access task_result. -} -else -{ - handle_error( exp, r, +return leaf::handle_some( - [ ] ( E1 e1, E2 e2 ) - { - //Deal with E1, E2 - }, + [&]() -> leaf::result + { + LEAF_AUTO(r, fut.get()); + //Success! + return { } + }, - [ ] ( E3 e3 ) - { - //Deal with E3 - } + [ ]( E1 e1, E2 e2 ) + { + //Deal with E1, E2 + .... + return { }; + }, - ); -} + [ ]( E3 e3 ) + { + //Deal with E3 + .... + return { }; + } ); ---- [.text-right] -<> +<> | <> NOTE: Follow this link to see a complete example program: https://github.com/zajo/leaf/blob/master/example/capture_result.cpp?ts=3[capture_result.cpp]. @@ -2220,64 +2427,58 @@ NOTE: Follow this link to see a complete example program: https://github.com/zaj [[technique_transport-exceptions]] ==== Using Exception Handling -When using exception handling, we need to capture the exception using `std::exception_ptr`, then capture the current error objects in an `<>` and wrap both into another exception. In the main thread we unwrap and throw the original exception. - -This, of course, is done automatically by LEAF. Let's assume we have a `task` which produces a `task_result` and throws on errors: +Let's assume we have a `task` which produces a result but could also throw: [source,c++] ---- task_result task(); ---- -When we launch the asynchronous task, we wrap it in a simple lambda function which calls `<>`, specifying which error types we need transported: +To prepare exceptions thrown by `task` to be sent across the thread boundary, when we launch the asynchronous task, we wrap it in call to <>, specifying which E-types we want captured: [source,c++] ---- std::future launch_task() { - return std::async( std::launch::async, - leaf::capture_exception( [ ] - { - return task(); - } ) ); + return std::async( + std::launch::async, + leaf::capture_exception(&task)); } ---- [.text-right] -<> +<> -Later, instead of using `std::future::get`, we use `leaf::<>`, then catch exceptions as if the function was called locally: +That's it! Later when we `get` the `std::future`, we can catch exceptions as if they're thrown locally: [source,c++] ---- -.... -leaf::expect exp; +//std::future fut; +fut.wait(); -try -{ - task_result r = leaf::get(fut); - //Success! -} -catch( my_exception & e ) -{ - handle_exception( exp, e, +try_( - [ ] ( E1 e1, E2 e2 ) - { - //Deal with E1, E2 - }, + [&] + { + auto r = fut.get(); + //Success! + }, - [ ] ( E3 e3 ) - { - //Deal with E3 - } + [ ]( E1 e1, E2 e2 ) + { + //Deal with E1, E2 + .... + }, - ); -} + [ ]( E3 e3 ) + { + //Deal with E3 + .... + } ); ---- [.text-right] -`<>` | <> | <> +<> | <> NOTE: Follow this link to see a complete example program: https://github.com/zajo/leaf/blob/master/example/capture_eh.cpp?ts=3[capture_eh.cpp]. @@ -2312,17 +2513,19 @@ Design goals: :: The difficulty in reaching these design goals is in that they seem to require that all error objects be allocated dynamically (the Boost Exception library meets these design goals at the cost of dynamic memory allocation). -As it turns out, dynamic memory allocation is not necessary with the following +As it turns out, dynamic memory allocation is not necessary due to the following -Adjustment: :: -* *Error-handling* functions should specify which of the information _error-initiating_ and _error-neutral_ functions are [.underline]#able# to communicate is [.underline]#actually needed# in order to deal with failures in a particular program domain. Ideally, no resources should be [.line-through]#used# wasted storing or communicating information which is not currently needed, even if it is relevant to the failure. +Fact: :: +* *Error-handling* functions already tell us which of the information _error-initiating_ and _error-neutral_ functions are [.underline]#able# to communicate is [.underline]#actually needed# in order to deal with failures in a particular program domain. Ideally, no resources should be [.line-through]#used# wasted storing or communicating information which is not currently needed to handle errors, even if it is relevant to the failure. -The `leaf::<>` class template implements this idea: it provides local storage for error objects of the `E...` types. Users instantiate this template in _error-handling_ functions, knowing which types of error objects are needed. When an `expect` object is created, `thread_local` pointers of the `E...` types are set to point to the corresponding storage within it. Later on, _error-initiating_ or _error-neutral_ functions wanting to communicate an error object of type `E` access the corresponding `thread_local` pointer to see if there is currently storage available for this type: +The error-handling LEAF functions <>, <> and <> implement this idea. Users provide error-handling lambda funcitons which, naturally, take the arguments they need. LEAF examines these arguments and reserves the space needed to hold the objects it needs to pass to a handler. -* If the pointer is not null, storage is available and the object is moved into that storage, exactly once -- regardless of how many levels of function calls must unwind before an appropriate _error-handling_ function is reached (that's the function where the `expect` object resides). -* If the pointer is null, storage is not available and the error object is discarded, since there is no use for it -- saving resources. +When this space is created, `thread_local` pointers of the needed E-types are set to point to the corresponding storage within the scope of <>, <> or <>. Later on, _error-initiating_ or _error-neutral_ functions wanting to communicate an error object of type `E` access the corresponding `thread_local` pointer to see if there is currently storage available for this type: -This almost works, except we need to make sure that _error-handling_ functions are protected from accessing stale error objects stored in `expect` objects, left there from previous failures, which would be a serious logic error. To this end, each failure is assigned a unique serial number, which is transported inside `leaf::<>` objects that communicate failures. Each of the `E...` objects stored in an `expect` object is assigned the same unique identifier, permanently associating it with a particular failure. +* If the pointer is not null, storage is available and the object is moved into that storage, exactly once -- regardless of how many levels of function calls must unwind before an appropriate _error-handling_ function. +* If the pointer is null, storage is not available and the error object is discarded, since no error-handling function makes any use of it -- saving resources. + +This almost works, except we need to make sure that _error-handling_ functions are protected from accessing stale error objects stored in an error-handling scope, left there from previous failures, which would be a serious logic error. To this end, each failure is assigned a unique serial number, which is transported inside `leaf::<>` objects that communicate failures. Each of the `E...` objects stored in error-handling scopes is assigned the same unique identifier, permanently associating it with a particular error. Lastly, in _error-handling_ functions it makes sense to be able to not only recognize individual error conditions, but match specific error-handling code with the complete set of error objects that is required in each case. @@ -2332,126 +2535,103 @@ In terms of {CPP} exception handling, it would be nice to be able to say somethi ---- try { - T r = process_file(); + auto r = process_file(); //Success, use r: .... } -catch( file_read_error & e ) + +catch( file_read_error const &, e_file_name const & fn, e_errno const & err ) { std::cerr << - "Could not read " << e.get() << - ", errno=" << e.get() << std::endl; + "Could not read " << fn << ", errno=" << err << std::endl; } -catch( file_read_error & e ) + +catch( file_read_error const &, e_errno const & err ) { std::cerr << - "File read error, errno=" << e.get() << std::endl; + "File read error, errno=" << err << std::endl; } -catch( file_read_error<> & e ) + +catch( file_read_error const & ) { std::cerr << "File read error!" << std::endl; } ---- -That is to say, it is desirable to be able to dispatch error handling based not only on the kind of failure being handled, but also based on the kind of error objects available. Unfortunately this syntax is not possible and, even if it were, not all programs use exceptions to handle errors. +That is to say, it is desirable to be able to dispatch error handling based not only on the kind of failure being handled, but also based on the kind of E-objects available. Unfortunately this syntax is not possible and, even if it were, not all programs use exceptions to handle errors. -Below is the same snippet, expressed in terms of LEAF: +LEAF achieves the same effect using a slightly different syntax: [source,c++] ---- -leaf::expect exp; +leaf::try_( -try -{ - T r = process_file(); //Throws in case of failure, error objects stored inside exp. + [ ] + { + auto r = process_file(); //Throws in case of failure, E-objects stored inside the try_ scope - //Success, use r: - .... -} -catch( file_read_error & e ) -{ - //Match available error objects stored in exp, - //associated with the specific failure communicated by e. - leaf::handle_exception( exp, e, + //Success, use r: + .... + } - [ ]( e_file_name const & file_name, e_errno const & errno_ ) - { - std::cerr << - "Could not read " << file_name.value << - ", errno=" << errno_ << std::endl; - }, + [ ]( leaf::catch_, e_file_name const & fn, e_errno const & err ) + { + std::cerr << + "Could not read " << fn << ", errno=" << err << std::endl; + }, - [ ]( e_errno const & errno_ ) - { - std::cerr << - "File read error, errno=" << errno_ << std::endl; - }, + [ ]( leaf::catch_, e_errno const & err ) + { + std::cerr << + "File read error, errno=" << err << std::endl; + }, - [ ] - { - std::cerr << "File read error!" << std::endl; - } - - ); -} + [ ]( leaf::catch_ ) + { + std::cerr << "File read error!" << std::endl; + } ); ---- [.text-right] -`<>` | <> +<> | <> Of course LEAF works without exception handling as well. Below is the same snippet, written using `<>`: [source,c++] ---- -leaf::expect exp; +return leaf::handle_some( -if( leaf::result r = process_file() ) //In case of failure error objects are stored inside exp. -{ - //Success, use r.value(): - .... -} -else -{ - //e_what is used to dispatch between error conditions: - switch( *leaf::peek(exp,r) ) + [ ]() -> leaf::result { - case file_read_error: - { - //Match available error objects stored in exp, - //associated with the specific failure communicated by r. - bool handled = leaf::handle_error( exp, r, + LEAF_AUTO(r, process_file()); //In case of errors, E-objects are stored inside the handle_some scope - [ ]( e_file_name const & file_name, e_errno const & errno_ ) - { - std::cerr << - "Could not read " << file_name.value << - ", errno=" << errno_ << std::endl; - }, + //Success, use r: + .... - [ ]( e_errno const & errno_ ) - { - std::cerr << - "File read error, errno=" << errno_ << std::endl; - }, + return { }; + } - [ ] - { - std::cerr << "File read error!" << std::endl; - } + [ ]( leaf::match, e_file_name const & fn, e_errno const & err ) + { + std::cerr << + "Could not read " << fn << ", errno=" << err << std::endl; + }, - ); - if( handled ) - break; - } - //fallthrough: - default: - return r; //Error not handled, forward to the caller. -} + [ ]( leaf::match, e_errno const & err ) + { + std::cerr << + "File read error, errno=" << err << std::endl; + }, + + [ ]( leaf::match ) + { + std::cerr << "File read error!" << std::endl; + } ); ---- [.text-right] -<> +<> | <> | <> NOTE: Please post questions and feedback on the Boost Developers Mailing List (LEAF is not part of Boost). @@ -2592,9 +2772,9 @@ struct config_error result read_config(); ---- -This looks nice, until we realize what the `config_error` type means for the automatic mapping API we wanted to define: an enum can not represent a struct, and therefore we can not assume that all error conditions can be fully specified by an enum; it must support arbitrary static types. +This looks nice, until we realize what the `config_error` type means for the automatic mapping API we wanted to define: an enum can not represent a struct. It is a fact that we can not assume that all error conditions can be fully specified by an enum; an error handling library must be able to transport arbitrary static types efficiently. -Conclusion: :: Transporting error objects statically in return values works great if the failure is handled in the immediate caller of the function that reports it, but most error objects must be communicated across multiple layers of function calls and APIs, which leads to excessive physical coupling between these interfaces. +Conclusion: :: Transporting error objects statically in return values works great if the failure is handled by the immediate caller of the function that reports it, but most error objects must be communicated across multiple layers of function calls and APIs, which leads to excessive physical coupling between these interfaces. NOTE: While the `leaf::<>` class template does have value-or-error semantics, it does not carry the actual error objects. Instead, they are forwarded directly to the appropriate error-handling scope and their types do not participate in function signatures. @@ -2714,33 +2894,29 @@ https://www.boost.org/doc/libs/release/libs/exception/doc/get_error_info.html[`b | [source,c++,options="nowrap"] ---- -leaf::expect exp; -try -{ - f(); -} -catch( my_exception & e ) -{ - leaf::handle_exception( exp, e, - [ ]( my_info const & x ) - { - //my_info is available in e. - } ); -} +leaf::try_( + [ ] + { + f(); + } + [ ]( leaf::catch_, my_info const & x ) + { + //my_info is available with e. + } ); ---- [.text-right] -`<>` \| <> \| <> +<> | <> |==== -.Error object propagation +.Propagation of <> [cols="1a,1a",options="header",stripes=none] |==== | Boost Exception | LEAF | All supplied https://www.boost.org/doc/libs/release/libs/exception/doc/error_info.html[`boost::error_info`] objects are allocated dynamically, stored in the https://www.boost.org/doc/libs/release/libs/exception/doc/exception.html[`boost::exception`] object, and propagated. -| User-defined error objects are stored statically in `leaf::<>` objects but only if their types are used in the instantiation the `expect` class template; otherwise they are discarded. +| User-defined error objects are stored statically in the scope of <>, but only if their types are used to handle errors; otherwise they are discarded. |==== -.Transporting of error objects across thread boundaries +.Transporting of <> across thread boundaries [cols="1a,1a",options="header",stripes=none] |==== | Boost Exception | LEAF @@ -2758,9 +2934,9 @@ catch( my_exception & e ) [WARNING] ==== -The fact that Boost Exception stores all supplied `boost::error_info` objects while LEAF discards them if they aren't needed affects the completeness of the message printed by `leaf::<>` compared to the string returned by https://www.boost.org/doc/libs/release/libs/exception/doc/diagnostic_information.html[`boost::diagnostic_information`]. +The fact that Boost Exception stores all supplied `boost::error_info` objects -- while LEAF discards them if they aren't needed -- affects the completeness of the message we get when we print `leaf::<>` objects, compared to the string returned by https://www.boost.org/doc/libs/release/libs/exception/doc/diagnostic_information.html[`boost::diagnostic_information`]. -By default, the `leaf::<>` includes only error objects of types used in the instantiation of the `<>` class template. If the user requires a complete `diagnostic_output` message, the solution is to use <> with `expect`. In this case, before unused error objects are discarded by LEAF, they are converted to string and stored in the `e_unexpected_diagnostic_output` object. Note that this allocates memory dynamically. +If the user requires a complete diagnostic message, the solution is to use `leaf::<>`. In this case, before unused error objects are discarded by LEAF, they are converted to string and captured. Note that this allocates memory dynamically. ==== ''' @@ -2788,7 +2964,7 @@ Similar reasoning drives the design of LEAF as well. The difference is that whil The `leaf::result<>` template deletes both `EC` and `EP`, which decouples it from the type of the error objects that are transported in case of a failure. This enables lower-level functions to freely communicate anything and everything they "know" about the failure: error code, even multiple error codes, file names, request IDs, etc. At the same time, the higher-level error-handling functions control which of this information is needed in a specific client program and which is not. This is ideal, because: * Authors of lower-level library functions lack context to determine which of the information that is both relevant to the error _and_ naturally available to them needs to be communicated in order for a particular client program to recover from that error; -* Authors of higher-level error-handling functions can easily and confidently make this determination, and instantiate the `<>` class template with the error types they do in fact need; LEAF automatically and efficiently discards error objects of all other types that error-reporting functions attempt to communicate, saving resources. +* Authors of higher-level error-handling functions easily and confidently make this determination, which they communicate naturally to LEAF, by simply writing the necessary error handlers. LEAF automatically and efficiently transports the needed E-objects while discarding the ones handlers don't use, saving resources. TIP: The LEAF examples include an adaptation of the program from the https://ned14.github.io/outcome/tutorial/result/[Boost Outcome `result<>` tutorial]. You can https://github.com/zajo/leaf/blob/master/example/print_half.cpp?ts=3[view it on GitHub]. @@ -2815,7 +2991,7 @@ The analysis above is clear and precise, but LEAF and Boost Outcome tackle the i * The Boost Outcome design asserts that the "cleanest" solution based on type-erasure is suboptimal ("almost certainly requires the use of `malloc`pass:[]"), and instead provides a system for injecting custom converters into the `outcome::convert` namespace, used to translate between library-specific and program-wide error types. -* The LEAF design asserts that coupling the signatures of <> functions with the static types of the error objects they need to forward to the caller is not practical, and instead transports error objects directly to error-handling contexts where they are stored statically (without the use of `malloc`). +* The LEAF design asserts that coupling the signatures of <> functions with the static types of the error objects they need to forward to the caller <>, and instead transports error objects directly to error-handling scopes where they are stored statically (without the use of `malloc`). [[distribution]] == Distribution diff --git a/doc/synopses/capture_exception.adoc b/doc/synopses/capture_exception.adoc index 6338c74..5246755 100644 --- a/doc/synopses/capture_exception.adoc +++ b/doc/synopses/capture_exception.adoc @@ -8,7 +8,3 @@ namespace boost { namespace leaf { } } ---- - -[.text-right] -<> - diff --git a/doc/synopses/error.adoc b/doc/synopses/error.adoc index 29e4bd7..80fa405 100644 --- a/doc/synopses/error.adoc +++ b/doc/synopses/error.adoc @@ -35,4 +35,4 @@ namespace boost { namespace leaf { ---- [.text-right] -<> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/exception_to_result.adoc b/doc/synopses/exception_to_result.adoc index 227a70c..5917a9c 100644 --- a/doc/synopses/exception_to_result.adoc +++ b/doc/synopses/exception_to_result.adoc @@ -4,11 +4,7 @@ namespace boost { namespace leaf { template - leaf::result exception_to_result( F && f ) noexcept; + <> exception_to_result( F && f ) noexcept; } } ---- - -[.text-right] -<> - diff --git a/doc/synopses/handle.adoc b/doc/synopses/handle.adoc index 5f89b9b..e3fca17 100644 --- a/doc/synopses/handle.adoc +++ b/doc/synopses/handle.adoc @@ -15,4 +15,4 @@ namespace boost { namespace leaf { ---- [.text-right] -`<>` | <> | <> | <> | <> | <> | <> | <> +<> | <> \ No newline at end of file diff --git a/doc/synopses/throw.adoc b/doc/synopses/throw.adoc index 29be587..6c93e93 100644 --- a/doc/synopses/throw.adoc +++ b/doc/synopses/throw.adoc @@ -1,4 +1,4 @@ -.#include +.#include [source,c++] ---- namespace boost { namespace leaf { diff --git a/doc/synopses/try.adoc b/doc/synopses/try.adoc index fccabfa..9754f3e 100644 --- a/doc/synopses/try.adoc +++ b/doc/synopses/try.adoc @@ -1,15 +1,15 @@ -.#include +.#include [source,c++] ---- namespace boost { namespace leaf { - error get_error( std::exception const & ex ) noexcept; - template decltype(std::declval()()) try_( TryBlock && try_block, Handler && ... handler ); + error get_error( std::exception const & ex ) noexcept; + } } ---- [.text-right] -<> +<> | <> diff --git a/example/capture_eh.cpp b/example/capture_eh.cpp index 0af5169..eb0e636 100644 --- a/example/capture_eh.cpp +++ b/example/capture_eh.cpp @@ -55,7 +55,7 @@ int main() { return std::async( std::launch::async, - leaf::capture_exception(task) ); + leaf::capture_exception(&task) ); } ); // Wait on the futures, get the task results, handle errors. diff --git a/example/capture_result.cpp b/example/capture_result.cpp index 4b96c33..de10976 100644 --- a/example/capture_result.cpp +++ b/example/capture_result.cpp @@ -55,7 +55,7 @@ int main() { return std::async( std::launch::async, - leaf::capture_result(task) ); + leaf::capture_result(&task) ); } ); // Wait on the futures, get the task results, handle errors. diff --git a/example/exception_to_result.cpp b/example/exception_to_result.cpp index 83f8a3b..1f6b3cc 100644 --- a/example/exception_to_result.cpp +++ b/example/exception_to_result.cpp @@ -53,8 +53,8 @@ leaf::result compute_answer() noexcept // Print the answer if the call to compute_answer is successful. leaf::result print_answer() noexcept { - LEAF_AUTO( r, compute_answer()); - std::cout << "Answer: " << r << std::endl; + LEAF_AUTO( answer, compute_answer()); + std::cout << "Answer: " << answer << std::endl; return { }; } From ff6f7716cdbf5b51db5d8d72afcf2769e23cf496 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Tue, 15 Jan 2019 00:36:44 -0800 Subject: [PATCH 32/34] error -> error_id --- example/lua_callback_eh.cpp | 2 +- example/lua_callback_result.cpp | 8 +- example/print_file_result.cpp | 2 +- include/boost/leaf/capture_exception.hpp | 8 +- include/boost/leaf/detail/dynamic_store.hpp | 8 +- .../boost/leaf/detail/dynamic_store_impl.hpp | 14 ++-- include/boost/leaf/detail/static_store.hpp | 22 +++--- include/boost/leaf/error.hpp | 76 +++++++++---------- include/boost/leaf/preload.hpp | 16 ++-- include/boost/leaf/result.hpp | 18 ++--- include/boost/leaf/throw.hpp | 4 +- include/boost/leaf/try.hpp | 8 +- test/capture_exception_unload_test.cpp | 4 +- test/defer_basic_test.cpp | 4 +- test/defer_nested_error_exception_test.cpp | 2 +- test/defer_nested_error_result_test.cpp | 6 +- .../defer_nested_new_error_exception_test.cpp | 4 +- test/defer_nested_new_error_result_test.cpp | 6 +- test/handle_some_test.cpp | 4 +- test/is_error_type_fail_test.cpp | 2 +- test/multiple_errors_test.cpp | 8 +- test/preload_basic_test.cpp | 4 +- test/preload_nested_error_exception_test.cpp | 2 +- test/preload_nested_error_result_test.cpp | 6 +- ...reload_nested_new_error_exception_test.cpp | 4 +- test/preload_nested_new_error_result_test.cpp | 6 +- test/result_state_test.cpp | 36 ++++----- test/static_store_test.cpp | 14 ++-- 28 files changed, 149 insertions(+), 149 deletions(-) diff --git a/example/lua_callback_eh.cpp b/example/lua_callback_eh.cpp index 5374f2f..f71972b 100644 --- a/example/lua_callback_eh.cpp +++ b/example/lua_callback_eh.cpp @@ -4,7 +4,7 @@ // 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 is a simple program that shows how to propagate leaf::error objects out +// This is a simple program that shows how to propagate error objects out // of a C-callback, and converting them to leaf::result as soon as control // reaches C++. diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index 8b99874..d001b69 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -4,7 +4,7 @@ // 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 is a simple program that shows how to propagate leaf::error objects out +// This is a simple program that shows how to propagate error objects out // of a C-callback, and converting them to leaf::result as soon as control // reaches C++. @@ -47,7 +47,7 @@ int do_work( lua_State * L ) noexcept } else { - // Associate an do_work_error_code object with the *next* leaf::error object we will + // Associate an do_work_error_code object with the *next* leaf::error_id object we will // definitely return from the call_lua function... leaf::next_error().propagate(ec1); @@ -90,9 +90,9 @@ leaf::result call_lua( lua_State * L ) lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); if( int err=lua_pcall(L,0,1,0) ) { - // Something went wrong with the call, so we'll return a leaf::error. + // Something went wrong with the call, so we'll return a leaf::new_error. // If this is a do_work failure, the do_work_error_code object prepared in - // do_work will become associated with this leaf::error value. If not, + // do_work will become associated with this leaf::error_id value. If not, // we will still need to communicate that the lua_pcall failed with an // error code and an error message. auto propagate = leaf::preload( e_lua_error_message{lua_tostring(L,1)} ); diff --git a/example/print_file_result.cpp b/example/print_file_result.cpp index 1633e8f..805b40d 100644 --- a/example/print_file_result.cpp +++ b/example/print_file_result.cpp @@ -26,7 +26,7 @@ enum error_code cout_error }; -// To enable leaf::error to work with our error_code enum, we need to specialize the is_e_type template: +// To enable LEAF to work with our error_code enum, we need to specialize the is_e_type template: namespace boost { namespace leaf { template<> struct is_e_type: std::true_type { }; } } diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp index ef589eb..ef6d671 100644 --- a/include/boost/leaf/capture_exception.hpp +++ b/include/boost/leaf/capture_exception.hpp @@ -18,7 +18,7 @@ namespace boost { namespace leaf { { class captured_exception_impl: public captured_exception, - public leaf::error + public error_id { std::exception_ptr ex_; std::shared_ptr ds_; @@ -28,7 +28,7 @@ namespace boost { namespace leaf { public: captured_exception_impl( std::exception_ptr && ex, std::shared_ptr && ds, bool had_error, void (*print_captured_types)(std::ostream &) ) noexcept: - error(ds->get_error()), + error_id(ds->error()), ex_(std::move(ex)), ds_(std::move(ds)), had_error_(had_error), @@ -44,7 +44,7 @@ namespace boost { namespace leaf { assert(ds); if( had_error_ ) { - error err = ds->unload(); + error_id err = ds->unload(); assert(err==*this); (void) err; } @@ -109,7 +109,7 @@ namespace boost { namespace leaf { { throw; } - catch( error const & e ) + catch( error_id const & e ) { throw captured_exception_impl( std::current_exception(), std::make_shared>(e,std::move(ss)), true, &print_types::print ); } diff --git a/include/boost/leaf/detail/dynamic_store.hpp b/include/boost/leaf/detail/dynamic_store.hpp index 700c8bf..f3b7bfd 100644 --- a/include/boost/leaf/detail/dynamic_store.hpp +++ b/include/boost/leaf/detail/dynamic_store.hpp @@ -11,7 +11,7 @@ namespace boost { namespace leaf { - class error; + class error_id; namespace leaf_detail { @@ -29,9 +29,9 @@ namespace boost { namespace leaf { public: - virtual error const & get_error() const noexcept = 0; - virtual error unload() noexcept = 0; - virtual error unload( error const & ) noexcept = 0; + virtual error_id const & error() const noexcept = 0; + virtual error_id unload() noexcept = 0; + virtual error_id unload( error_id const & ) noexcept = 0; }; } diff --git a/include/boost/leaf/detail/dynamic_store_impl.hpp b/include/boost/leaf/detail/dynamic_store_impl.hpp index 292b78b..74d743f 100644 --- a/include/boost/leaf/detail/dynamic_store_impl.hpp +++ b/include/boost/leaf/detail/dynamic_store_impl.hpp @@ -19,7 +19,7 @@ namespace boost { namespace leaf { template struct tuple_for_each { - static void unload( error const & e, Tuple && tup ) noexcept + static void unload( error_id const & e, Tuple && tup ) noexcept { tuple_for_each::unload(e,std::move(tup)); auto && opt = std::get(std::move(tup)); @@ -40,7 +40,7 @@ namespace boost { namespace leaf { template struct tuple_for_each<0, Tuple> { - static void unload( error const &, Tuple && ) noexcept { } + static void unload( error_id const &, Tuple && ) noexcept { } static void print( std::ostream &, Tuple const & ) noexcept { } }; } @@ -49,20 +49,20 @@ namespace boost { namespace leaf { class dynamic_store_impl: public dynamic_store { - error e_; + error_id e_; std::tuple...> s_; - error const & get_error() const noexcept + error_id const & error() const noexcept { return e_; } - error unload() noexcept + error_id unload() noexcept { return unload(e_); } - error unload( error const & e ) noexcept + error_id unload( error_id const & e ) noexcept { dynamic_store_internal::tuple_for_each::unload(e,std::move(s_)); return e; @@ -70,7 +70,7 @@ namespace boost { namespace leaf { public: - dynamic_store_impl( error const & e, static_store && ss ) noexcept: + dynamic_store_impl( error_id const & e, static_store && ss ) noexcept: e_(e), s_(std::make_tuple( std::get,decltype(ss.s_)>::value>(std::move(ss.s_)).extract_optional(e)... )) { diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index f93d7e7..6823356 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -141,7 +141,7 @@ namespace boost { namespace leaf { public slot { public: - optional extract_optional( error const & e ) && noexcept + optional extract_optional( error_id const & e ) && noexcept { slot const & s = *this; if( s.has_value() && s.value().e==e ) @@ -199,7 +199,7 @@ namespace boost { namespace leaf { static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept { auto & sl = std::get,SlotsTuple>::value>(tup); - return sl.has_value() && sl.value().e==ei.get_error(); + return sl.has_value() && sl.value().e==ei.error(); } }; @@ -221,7 +221,7 @@ namespace boost { namespace leaf { if( sl.has_value() ) { auto const & v = sl.value(); - return v.e==ei.get_error() && match(v.v)(); + return v.e==ei.error() && match(v.v)(); } else return false; @@ -233,7 +233,7 @@ namespace boost { namespace leaf { { static bool check( SlotsTuple const &, error_info const & ei ) noexcept { - if( std::exception const * ex = ei.get_exception() ) + if( std::exception const * ex = ei.exception() ) return catch_(*ex)(); else return false; @@ -278,7 +278,7 @@ namespace boost { namespace leaf { template static T const & get( StaticStore const & ss, error_info const & ei ) noexcept { - T const * arg = ss.template peek(ei.get_error()); + T const * arg = ss.template peek(ei.error()); assert(arg!=0); return *arg; } @@ -290,7 +290,7 @@ namespace boost { namespace leaf { template static T const * get( StaticStore const & ss, error_info const & ei ) noexcept { - return ss.template peek(ei.get_error()); + return ss.template peek(ei.error()); } }; @@ -300,7 +300,7 @@ namespace boost { namespace leaf { template static match get( StaticStore const & ss, error_info const & ei ) noexcept { - E const * arg = ss.template peek(ei.get_error()); + E const * arg = ss.template peek(ei.error()); assert(arg!=0); return match(*arg); } @@ -312,7 +312,7 @@ namespace boost { namespace leaf { template static catch_ get( StaticStore const &, error_info const & ei ) noexcept { - std::exception const * ex = ei.get_exception(); + std::exception const * ex = ei.exception(); assert(ex!=0); return catch_(*ex); } @@ -334,7 +334,7 @@ namespace boost { namespace leaf { template static diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept { - diagnostic_info const * uei = ss.template peek(ei.get_error()); + diagnostic_info const * uei = ss.template peek(ei.error()); assert(uei!=0); uei->set_error_info(ei); return *uei; @@ -347,7 +347,7 @@ namespace boost { namespace leaf { template static verbose_diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept { - verbose_diagnostic_info const * vdi = ss.template peek(ei.get_error()); + verbose_diagnostic_info const * vdi = ss.template peek(ei.error()); assert(vdi!=0); vdi->set_error_info(ei); return *vdi; @@ -427,7 +427,7 @@ namespace boost { namespace leaf { } template - P const * peek( error const & e ) const noexcept + P const * peek( error_id const & e ) const noexcept { auto & opt = std::get::value>(s_); if( opt.has_value() ) diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index f128954..7ffdaf6 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -22,21 +22,21 @@ namespace boost { namespace system { class error_code; } } namespace boost { namespace leaf { - class error; + class error_id; - error next_error() noexcept; - error last_error() noexcept; + error_id next_error() noexcept; + error_id last_error() noexcept; - class error + class error_id { template - friend error new_error( E && ... ) noexcept; - friend error leaf::next_error() noexcept; - friend error leaf::last_error() noexcept; + friend error_id new_error( E && ... ) noexcept; + friend error_id leaf::next_error() noexcept; + friend error_id leaf::last_error() noexcept; unsigned id_; - explicit error( unsigned id ) noexcept: + explicit error_id( unsigned id ) noexcept: id_(id) { } @@ -89,45 +89,45 @@ namespace boost { namespace leaf { public: - friend bool operator==( error const & e1, error const & e2 ) noexcept + friend bool operator==( error_id const & e1, error_id const & e2 ) noexcept { return e1.id_==e2.id_; } - friend bool operator!=( error const & e1, error const & e2 ) noexcept + friend bool operator!=( error_id const & e1, error_id const & e2 ) noexcept { return e1.id_!=e2.id_; } - error propagate() const noexcept + error_id propagate() const noexcept { return *this; } - friend std::ostream & operator<<( std::ostream & os, error const & e ) + friend std::ostream & operator<<( std::ostream & os, error_id const & e ) { os << e.id_; return os; } template - error propagate( E && ... ) const noexcept; + error_id propagate( E && ... ) const noexcept; }; template - error new_error( E && ... e ) noexcept + error_id new_error( E && ... e ) noexcept { - return error(error::id_factory::tl_instance().get()).propagate(std::forward(e)...); + return error_id(error_id::id_factory::tl_instance().get()).propagate(std::forward(e)...); } - inline error next_error() noexcept + inline error_id next_error() noexcept { - return error(error::id_factory::tl_instance().next_id()); + return error_id(error_id::id_factory::tl_instance().next_id()); } - inline error last_error() noexcept + inline error_id last_error() noexcept { - return error(error::id_factory::tl_instance().last_id()); + return error_id(error_id::id_factory::tl_instance().last_id()); } //////////////////////////////////////// @@ -139,11 +139,11 @@ namespace boost { namespace leaf { slot_base( slot_base const & ) = delete; slot_base & operator=( slot_base const & ) = delete; - virtual bool slot_print( std::ostream &, error const & e ) const = 0; + virtual bool slot_print( std::ostream &, error_id const & e ) const = 0; public: - static void print( std::ostream & os, error const & e ) + static void print( std::ostream & os, error_id const & e ) { for( slot_base const * p = first(); p; p=p->next_ ) if( p->slot_print(os,e) ) @@ -184,14 +184,14 @@ namespace boost { namespace leaf { error_info( error_info const & ) = delete; error_info & operator=( error_info const & ) = delete; - error const e_; + error_id const e_; std::exception const * const ex_; leaf_detail::captured_exception const * const cap_; void (* const print_ex_)( std::ostream &, std::exception const *, leaf_detail::captured_exception const * ); public: - explicit error_info( error const & e ) noexcept: + explicit error_info( error_id const & e ) noexcept: e_(e), ex_(0), cap_(0), @@ -199,7 +199,7 @@ namespace boost { namespace leaf { { } - error_info( error const & e, std::exception const * ex, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: + error_info( error_id const & e, std::exception const * ex, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: e_(e), ex_(ex), cap_(0), @@ -208,7 +208,7 @@ namespace boost { namespace leaf { assert(print_ex_!=0); } - error_info( error const & e, std::exception const * ex, leaf_detail::captured_exception const * cap, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: + error_info( error_id const & e, std::exception const * ex, leaf_detail::captured_exception const * cap, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: e_(e), ex_(ex), cap_(cap), @@ -217,7 +217,7 @@ namespace boost { namespace leaf { assert(print_ex_!=0); } - leaf::error const & get_error() const noexcept + error_id const & error() const noexcept { return e_; } @@ -227,7 +227,7 @@ namespace boost { namespace leaf { return print_ex_!=0; } - std::exception const * get_exception() const noexcept + std::exception const * exception() const noexcept { assert(exception_caught()); return ex_; @@ -235,7 +235,7 @@ namespace boost { namespace leaf { friend std::ostream & operator<<( std::ostream & os, error_info const & x ) { - os << "leaf::error serial number: " << x.e_ << std::endl; + os << "leaf::error_id: " << x.e_ << std::endl; if( x.print_ex_ ) x.print_ex_(os,x.ex_,x.cap_); leaf_detail::slot_base::print(os,x.e_); @@ -434,21 +434,21 @@ namespace boost { namespace leaf { template struct ev_type { - error e; + error_id e; E v; - explicit ev_type( error const & e ) noexcept: + explicit ev_type( error_id const & e ) noexcept: e(e) { } - ev_type( error const & e, E const & v ): + ev_type( error_id const & e, E const & v ): e(e), v(v) { } - ev_type( error const & e, E && v ) noexcept: + ev_type( error_id const & e, E && v ) noexcept: e(e), v(std::forward(v)) { @@ -477,7 +477,7 @@ namespace boost { namespace leaf { slot * prev_; static_assert(is_e_type::value,"Not an error type"); - bool slot_print( std::ostream &, error const & ) const; + bool slot_print( std::ostream &, error_id const & ) const; protected: @@ -545,7 +545,7 @@ namespace boost { namespace leaf { } template - int put_slot( E && v, error const & e ) noexcept + int put_slot( E && v, error_id const & e ) noexcept { using T = typename std::remove_cv::type>::type; if( slot * p = tl_slot_ptr() ) @@ -587,7 +587,7 @@ namespace boost { namespace leaf { } template - bool slot::slot_print( std::ostream & os, error const & e ) const + bool slot::slot_print( std::ostream & os, error_id const & e ) const { if( tl_slot_ptr()==this ) if( ev_type const * ev = has_value() ) @@ -597,13 +597,13 @@ namespace boost { namespace leaf { } template - error make_error( char const * file, int line, char const * function, E && ... e ) noexcept + error_id make_error( char const * file, int line, char const * function, E && ... e ) noexcept { assert(file&&*file); assert(line>0); assert(function&&*function); e_source_location sl { file, line, function }; // Temp object MSVC workaround - return error( std::move(sl), std::forward(e)... ); + return error_id( std::move(sl), std::forward(e)... ); } enum class result_variant @@ -615,7 +615,7 @@ namespace boost { namespace leaf { } // leaf_detail template - error error::propagate( E && ... e ) const noexcept + error_id error_id::propagate( E && ... e ) const noexcept { auto _ = { leaf_detail::put_slot(std::forward(e),*this)... }; (void) _; diff --git a/include/boost/leaf/preload.hpp b/include/boost/leaf/preload.hpp index 2279163..0003e23 100644 --- a/include/boost/leaf/preload.hpp +++ b/include/boost/leaf/preload.hpp @@ -17,7 +17,7 @@ namespace boost { namespace leaf { template struct tuple_for_each_preload { - static void trigger( Tuple & tup, error const & e ) noexcept + static void trigger( Tuple & tup, error_id const & e ) noexcept { tuple_for_each_preload::trigger(tup,e); std::get(tup).trigger(e); @@ -27,7 +27,7 @@ namespace boost { namespace leaf { template struct tuple_for_each_preload<0, Tuple> { - static void trigger( Tuple const &, error const & ) noexcept { } + static void trigger( Tuple const &, error_id const & ) noexcept { } }; } // leaf_detail @@ -49,7 +49,7 @@ namespace boost { namespace leaf { { } - void trigger( error const & e ) noexcept + void trigger( error_id const & e ) noexcept { if( s_ ) { @@ -73,7 +73,7 @@ namespace boost { namespace leaf { preloaded & operator=( preloaded const & ) = delete; std::tuple...> p_; - error e_; + error_id e_; bool moved_; public: @@ -97,7 +97,7 @@ namespace boost { namespace leaf { { if( moved_ ) return; - error const e = last_error(); + error_id const e = last_error(); if( e==e_ ) { if( std::uncaught_exception() ) @@ -134,7 +134,7 @@ namespace boost { namespace leaf { { } - void trigger( error const & e ) noexcept + void trigger( error_id const & e ) noexcept { if( s_ ) { @@ -157,7 +157,7 @@ namespace boost { namespace leaf { { deferred & operator=( deferred const & ) = delete; std::tuple...> d_; - error e_; + error_id e_; bool moved_; public: @@ -181,7 +181,7 @@ namespace boost { namespace leaf { { if( moved_ ) return; - error const e = last_error(); + error_id const e = last_error(); if( e==e_ ) { if( std::uncaught_exception() ) diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index e9eb086..79aa39b 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -31,7 +31,7 @@ namespace boost { namespace leaf { union { T value_; - error err_; + error_id err_; dynamic_store_ptr cap_; }; @@ -45,7 +45,7 @@ namespace boost { namespace leaf { value_.~T(); break; case leaf_detail::result_variant::err: - err_.~error(); + err_.~error_id(); break; default: assert(which_==leaf_detail::result_variant::cap); @@ -63,7 +63,7 @@ namespace boost { namespace leaf { (void) new(&value_) T(x.value_); break; case leaf_detail::result_variant::err: - (void) new(&err_) leaf::error(x.err_); + (void) new(&err_) error_id(x.err_); break; default: assert(x.which_==leaf_detail::result_variant::cap); @@ -82,7 +82,7 @@ namespace boost { namespace leaf { which_ = x.which_; break; case leaf_detail::result_variant::err: - (void) new(&err_) leaf::error(std::move(x.err_)); + (void) new(&err_) error_id(std::move(x.err_)); which_ = x.which_; break; default: @@ -90,7 +90,7 @@ namespace boost { namespace leaf { if( dynamic_store_ptr cap = std::move(x.cap_) ) { x.destroy(); - (void) new(&x.err_) leaf::error(cap->get_error()); + (void) new(&x.err_) error_id(cap->error()); x.which_ = leaf_detail::result_variant::err; (void) new(&cap_) dynamic_store_ptr(std::move(cap)); } @@ -149,7 +149,7 @@ namespace boost { namespace leaf { { } - result( leaf::error const & e ) noexcept: + result( error_id const & e ) noexcept: err_(e), which_(leaf_detail::result_variant::err) { @@ -203,7 +203,7 @@ namespace boost { namespace leaf { } template - leaf::error error( E && ... e ) noexcept + error_id error( E && ... e ) noexcept { switch( which_ ) { @@ -213,7 +213,7 @@ namespace boost { namespace leaf { { dynamic_store_ptr cap = cap_; destroy(); - (void) new(&err_) leaf::error(cap->unload()); + (void) new(&err_) error_id(cap->unload()); which_ = leaf_detail::result_variant::err; } default: @@ -249,7 +249,7 @@ namespace boost { namespace leaf { result() = default; - result( leaf::error const & e ) noexcept: + result( error_id const & e ) noexcept: base(e) { } diff --git a/include/boost/leaf/throw.hpp b/include/boost/leaf/throw.hpp index faf76f0..f3c15f8 100644 --- a/include/boost/leaf/throw.hpp +++ b/include/boost/leaf/throw.hpp @@ -23,7 +23,7 @@ namespace boost { namespace leaf { template class exception: public Ex, - public error + public error_id { public: @@ -33,7 +33,7 @@ namespace boost { namespace leaf { template exception( Ex && ex, E && ... e ) noexcept: Ex(std::move(ex)), - error(new_error(std::forward(e)...)) + error_id(new_error(std::forward(e)...)) { leaf_detail::enforce_std_exception(*this); } diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp index a53bb69..1c3169e 100644 --- a/include/boost/leaf/try.hpp +++ b/include/boost/leaf/try.hpp @@ -13,9 +13,9 @@ namespace boost { namespace leaf { - inline error get_error( std::exception const & ex ) noexcept + inline error_id get_error_id( std::exception const & ex ) noexcept { - if( auto e = dynamic_cast(&ex) ) + if( auto e = dynamic_cast(&ex) ) return *e; else return next_error(); @@ -41,7 +41,7 @@ namespace boost { namespace leaf { } catch( std::exception const & ex ) { - return ss.handle_error(error_info(get_error(ex),&ex,&cap,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(get_error_id(ex),&ex,&cap,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); } catch( ... ) { @@ -50,7 +50,7 @@ namespace boost { namespace leaf { } catch( std::exception const & ex ) { - return ss.handle_error(error_info(get_error(ex),&ex,0,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); + return ss.handle_error(error_info(get_error_id(ex),&ex,0,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); } catch( ... ) { diff --git a/test/capture_exception_unload_test.cpp b/test/capture_exception_unload_test.cpp index 04d6ff9..18df648 100644 --- a/test/capture_exception_unload_test.cpp +++ b/test/capture_exception_unload_test.cpp @@ -157,14 +157,14 @@ int main() test( [ ] { - throw leaf::exception( std::exception(), info<1>{1}, info<3>{3} ); // Derives from leaf::error + throw leaf::exception( std::exception(), info<1>{1}, info<3>{3} ); // Derives from leaf::error_id } ); test( [ ] { auto propagate = leaf::preload( info<1>{1}, info<3>{3} ); - throw std::exception(); // Does not derive from leaf::error + throw std::exception(); // Does not derive from leaf::error_id } ); return boost::report_errors(); diff --git a/test/defer_basic_test.cpp b/test/defer_basic_test.cpp index 72253a1..4d1e839 100644 --- a/test/defer_basic_test.cpp +++ b/test/defer_basic_test.cpp @@ -22,7 +22,7 @@ struct info int value; }; -leaf::error g() +leaf::error_id g() { global = 0; auto propagate = leaf::defer( [ ] { return info{get_global()}; } ); @@ -30,7 +30,7 @@ leaf::error g() return leaf::new_error(); } -leaf::error f() +leaf::error_id f() { return g(); } diff --git a/test/defer_nested_error_exception_test.cpp b/test/defer_nested_error_exception_test.cpp index 3fbc53f..e994494 100644 --- a/test/defer_nested_error_exception_test.cpp +++ b/test/defer_nested_error_exception_test.cpp @@ -35,7 +35,7 @@ void f2() { f1(); } - catch( leaf::error e ) + catch( leaf::error_id e ) { e.propagate( info<3>{3} ); throw; diff --git a/test/defer_nested_error_result_test.cpp b/test/defer_nested_error_result_test.cpp index f38f5ea..7589d01 100644 --- a/test/defer_nested_error_result_test.cpp +++ b/test/defer_nested_error_result_test.cpp @@ -16,19 +16,19 @@ struct info int value; }; -leaf::error f0() +leaf::error_id f0() { auto propagate = leaf::defer( [ ] { return info<0>{0}; } ); return leaf::new_error( info<2>{2} ); } -leaf::error f1() +leaf::error_id f1() { auto propagate = leaf::defer( [ ] { return info<0>{-1}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{-1}; } ); return f0(); } -leaf::error f2() +leaf::error_id f2() { return f1().propagate( info<3>{3} ); } diff --git a/test/defer_nested_new_error_exception_test.cpp b/test/defer_nested_new_error_exception_test.cpp index 1e09e3d..bdfb596 100644 --- a/test/defer_nested_new_error_exception_test.cpp +++ b/test/defer_nested_new_error_exception_test.cpp @@ -30,14 +30,14 @@ void f1() throw leaf::exception(std::exception()); } -leaf::error f2() +leaf::error_id f2() { try { f1(); BOOST_TEST(false); } - catch( leaf::error e ) + catch( leaf::error_id e ) { e.propagate( info<3>{3} ); throw; diff --git a/test/defer_nested_new_error_result_test.cpp b/test/defer_nested_new_error_result_test.cpp index 0e80e26..6465426 100644 --- a/test/defer_nested_new_error_result_test.cpp +++ b/test/defer_nested_new_error_result_test.cpp @@ -16,20 +16,20 @@ struct info int value; }; -leaf::error f0() +leaf::error_id f0() { auto propagate = leaf::defer( [ ] { return info<0>{-1}; } ); return leaf::new_error( info<1>{-1} ); } -leaf::error f1() +leaf::error_id f1() { auto propagate = leaf::defer( [ ] { return info<0>{0}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{2}; } ); (void) f0(); return leaf::new_error(); } -leaf::error f2() +leaf::error_id f2() { return f1().propagate( info<3>{3} ); } diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp index fb7f0fd..83972df 100644 --- a/test/handle_some_test.cpp +++ b/test/handle_some_test.cpp @@ -50,7 +50,7 @@ int main() { BOOST_TEST(c==0); c = 1; - return unmatched.get_error(); + return unmatched.error(); } ); BOOST_TEST(r); BOOST_TEST(c==42); @@ -578,7 +578,7 @@ int main() }, [ ]( leaf::error_info const & unmatched ) { - return unmatched.get_error(); + return unmatched.error(); } ); BOOST_TEST(r && *r==42); } diff --git a/test/is_error_type_fail_test.cpp b/test/is_error_type_fail_test.cpp index f4de1a3..2c2c0e6 100644 --- a/test/is_error_type_fail_test.cpp +++ b/test/is_error_type_fail_test.cpp @@ -10,7 +10,7 @@ namespace leaf = boost::leaf; struct no_member_value { }; -leaf::error f() +leaf::error_id f() { // Note: the line below should trigger a compile error (via static_assert). return leaf::new_error( no_member_value{ } ); diff --git a/test/multiple_errors_test.cpp b/test/multiple_errors_test.cpp index 30eb294..4ab2f50 100644 --- a/test/multiple_errors_test.cpp +++ b/test/multiple_errors_test.cpp @@ -15,12 +15,12 @@ struct info int value; }; -leaf::error f12() +leaf::error_id f12() { return leaf::new_error( info<1>{1}, info<2>{2} ); } -leaf::error f34() +leaf::error_id f34() { return leaf::new_error( info<3>{3}, info<4>{4} ); } @@ -28,8 +28,8 @@ leaf::error f34() int main() { leaf::static_store,info<2>,info<3>,info<4>> exp; - leaf::error e1=f12(); - leaf::error e2=f34(); + leaf::error_id e1=f12(); + leaf::error_id e2=f34(); { int e1c1=0, e1c2=0; bool handled = exp.handle_error( e1, diff --git a/test/preload_basic_test.cpp b/test/preload_basic_test.cpp index 200c80c..f31e2c3 100644 --- a/test/preload_basic_test.cpp +++ b/test/preload_basic_test.cpp @@ -15,13 +15,13 @@ struct info int value; }; -leaf::error g() +leaf::error_id g() { auto propagate = leaf::preload( info{42} ); return leaf::new_error(); } -leaf::error f() +leaf::error_id f() { return g(); } diff --git a/test/preload_nested_error_exception_test.cpp b/test/preload_nested_error_exception_test.cpp index 435848d..3992eba 100644 --- a/test/preload_nested_error_exception_test.cpp +++ b/test/preload_nested_error_exception_test.cpp @@ -35,7 +35,7 @@ void f2() { f1(); } - catch( leaf::error e ) + catch( leaf::error_id e ) { e.propagate( info<3>{3} ); throw; diff --git a/test/preload_nested_error_result_test.cpp b/test/preload_nested_error_result_test.cpp index 97dea1e..3f229c3 100644 --- a/test/preload_nested_error_result_test.cpp +++ b/test/preload_nested_error_result_test.cpp @@ -16,19 +16,19 @@ struct info int value; }; -leaf::error f0() +leaf::error_id f0() { auto propagate = leaf::preload( info<0>{0} ); return leaf::new_error( info<2>{2} ); } -leaf::error f1() +leaf::error_id f1() { auto propagate = leaf::preload( info<0>{-1}, info<1>{1}, info<2>{-1} ); return f0(); } -leaf::error f2() +leaf::error_id f2() { return f1().propagate( info<3>{3} ); } diff --git a/test/preload_nested_new_error_exception_test.cpp b/test/preload_nested_new_error_exception_test.cpp index 0b871e0..b06979a 100644 --- a/test/preload_nested_new_error_exception_test.cpp +++ b/test/preload_nested_new_error_exception_test.cpp @@ -30,14 +30,14 @@ void f1() throw leaf::exception(std::exception()); } -leaf::error f2() +leaf::error_id f2() { try { f1(); BOOST_TEST(false); } - catch( leaf::error e ) + catch( leaf::error_id e ) { e.propagate( info<3>{3} ); throw; diff --git a/test/preload_nested_new_error_result_test.cpp b/test/preload_nested_new_error_result_test.cpp index 7ece7e0..97e8235 100644 --- a/test/preload_nested_new_error_result_test.cpp +++ b/test/preload_nested_new_error_result_test.cpp @@ -16,20 +16,20 @@ struct info int value; }; -leaf::error f0() +leaf::error_id f0() { auto propagate = leaf::preload( info<0>{-1} ); return leaf::new_error( info<1>{-1} ); } -leaf::error f1() +leaf::error_id f1() { auto propagate = leaf::preload( info<0>{0}, info<1>{1}, info<2>{2} ); (void) f0(); return leaf::new_error(); } -leaf::error f2() +leaf::error_id f2() { return f1().propagate( info<3>{3} ); } diff --git a/test/result_state_test.cpp b/test/result_state_test.cpp index 7b366e1..0d572d7 100644 --- a/test/result_state_test.cpp +++ b/test/result_state_test.cpp @@ -422,7 +422,7 @@ int main() { // error copy -> copy static_store exp; exp.set_reset(true); - leaf::error e = leaf::new_error( e_err{ } ); + leaf::error_id e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -449,7 +449,7 @@ int main() BOOST_TEST(val::count==0); { // error copy -> move static_store exp; - leaf::error e = leaf::new_error( e_err{ } ); + leaf::error_id e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -475,7 +475,7 @@ int main() BOOST_TEST(val::count==0); { // error copy -> assign copy static_store exp; - leaf::error e = leaf::new_error( e_err{ } ); + leaf::error_id e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -495,7 +495,7 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=std::move(r1); BOOST_TEST(!r2); - leaf::error const e = r2.error(); + leaf::error_id const e = r2.error(); exp.handle_error(leaf::error_info(e),[ ]{ }); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -504,7 +504,7 @@ int main() BOOST_TEST(val::count==0); { // error copy -> assign move static_store exp; - leaf::error e = leaf::new_error( e_err{ } ); + leaf::error_id e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -528,7 +528,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { // error copy -> capture -> copy - leaf::result r1 = capture_result( [ ] { leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ] { leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -551,7 +551,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { // error copy -> capture -> move - leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -574,7 +574,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { // error copy -> capture -> assign-copy - leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -597,7 +597,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { // error copy -> capture -> assign-move - leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -687,7 +687,7 @@ int main() { // void error copy -> copy static_store exp; exp.set_reset(true); - leaf::error e = leaf::new_error( e_err{ } ); + leaf::error_id e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -710,7 +710,7 @@ int main() BOOST_TEST(err::count==0); { // void error copy -> move static_store exp; - leaf::error e = leaf::new_error( e_err{ } ); + leaf::error_id e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -732,7 +732,7 @@ int main() BOOST_TEST(err::count==0); { // void error copy -> assign copy static_store exp; - leaf::error e = leaf::new_error( e_err{ } ); + leaf::error_id e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -749,14 +749,14 @@ int main() BOOST_TEST(err::count==1); leaf::result r2; r2=std::move(r1); BOOST_TEST(!r2); - leaf::error const e = r2.error(); + leaf::error_id const e = r2.error(); exp.handle_error(leaf::error_info(e),[ ]{ }); BOOST_TEST(err::count==1); } BOOST_TEST(err::count==0); { // void error copy -> assign move static_store exp; - leaf::error e = leaf::new_error( e_err{ } ); + leaf::error_id e = leaf::new_error( e_err{ } ); leaf::result r1 = e; BOOST_TEST(!r1); BOOST_TEST(err::count==1); @@ -776,7 +776,7 @@ int main() } BOOST_TEST(err::count==0); { // void error copy -> capture -> copy - leaf::result r1 = capture_result( [ ] { leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ] { leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = r1; @@ -795,7 +795,7 @@ int main() } BOOST_TEST(err::count==0); { // void error copy -> capture -> move - leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = std::move(r1); @@ -814,7 +814,7 @@ int main() } BOOST_TEST(err::count==0); { // void error copy -> capture -> assign-copy - leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=r1; @@ -833,7 +833,7 @@ int main() } BOOST_TEST(err::count==0); { // void error copy -> capture -> assign-move - leaf::result r1 = capture_result( [ ]{ leaf::error e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=std::move(r1); diff --git a/test/static_store_test.cpp b/test/static_store_test.cpp index a8b325f..1195ac2 100644 --- a/test/static_store_test.cpp +++ b/test/static_store_test.cpp @@ -15,22 +15,22 @@ struct info int value; }; -leaf::error f1() +leaf::error_id f1() { return leaf::new_error( info<1>{1} ); } -leaf::error f2() +leaf::error_id f2() { return f1().propagate( info<2>{2} ); } -leaf::error f3() +leaf::error_id f3() { return f2().propagate( info<3>{3} ); } -leaf::error f4() +leaf::error_id f4() { return f3().propagate(); } @@ -40,7 +40,7 @@ int main() using leaf::leaf_detail::static_store; static_store,info<2>,info<4>> exp0; - leaf::error e0 = f4(); + leaf::error_id e0 = f4(); { info<1> const * p = exp0.peek>(e0); BOOST_TEST(p && p->value==1); @@ -53,7 +53,7 @@ int main() BOOST_TEST(!exp0.peek>(e0)); static_store,info<2>,info<4>> exp; - leaf::error e1 = f4(); + leaf::error_id e1 = f4(); { info<1> const * p = exp0.peek>(e0); @@ -93,7 +93,7 @@ int main() } ); BOOST_TEST(r==2); } - leaf::error e2 = f4(); + leaf::error_id e2 = f4(); { info<1> const * p = exp.peek>(e2); From 3f9d1821ddf9ce735dd379adf342e65d5c939082 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Tue, 15 Jan 2019 11:28:17 -0800 Subject: [PATCH 33/34] renamed types and variable --- doc/leaf.adoc | 1284 ++++++++--------- doc/synopses/error.adoc | 22 +- doc/synopses/result.adoc | 6 +- doc/synopses/try.adoc | 4 +- example/lua_callback_eh.cpp | 2 +- example/lua_callback_result.cpp | 2 +- example/print_file_result.cpp | 4 +- example/print_half.cpp | 2 +- include/boost/leaf/capture_exception.hpp | 10 +- .../boost/leaf/detail/dynamic_store_impl.hpp | 24 +- include/boost/leaf/detail/static_store.hpp | 20 +- include/boost/leaf/error.hpp | 108 +- include/boost/leaf/preload.hpp | 52 +- include/boost/leaf/result.hpp | 8 +- include/boost/leaf/try.hpp | 4 +- test/capture_exception_state_test.cpp | 2 +- test/capture_result_state_test.cpp | 2 +- test/defer_nested_error_exception_test.cpp | 4 +- .../defer_nested_new_error_exception_test.cpp | 4 +- test/handle_all_test.cpp | 2 +- test/handle_some_basic_test.cpp | 2 +- test/handle_some_test.cpp | 2 +- test/is_error_type_test.cpp | 10 +- test/preload_nested_error_exception_test.cpp | 4 +- ...reload_nested_new_error_exception_test.cpp | 4 +- test/result_state_test.cpp | 56 +- test/static_store_deduction_test.cpp | 2 +- 27 files changed, 815 insertions(+), 831 deletions(-) diff --git a/doc/leaf.adoc b/doc/leaf.adoc index 03442aa..98bbcb0 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -18,7 +18,7 @@ LEAF is a {CPP}11 error handling library. Features: ==== * Header-only, no dependencies. -* No dynamic memory allocations.footnote:[Except when error objects are transported between threads, see <>.] +* No dynamic memory allocations.footnote:[Except when error objects are transported between threads, see <> / <>.] * Any error-related object of any movable type is efficiently delivered to the correct error handler. @@ -61,12 +61,10 @@ Very nice, but how does LEAF know that this `enum` represents error codes and no ---- namespace boost { namespace leaf { - template<> struct is_e_type: std::true_type { }; + template<> struct is_error_type: std::true_type { }; } } ---- -[.text-right] -<> Now, reading and printing a file may not seem like a complex job, but let's split it into several functions, each communicating failures using `leaf::result`: @@ -84,8 +82,6 @@ leaf::result file_size( FILE & f ); //Read size bytes from f into buf. leaf::result file_read( FILE & f, void * buf, int size ); ---- -[.text-right] -<> What's this `result` template, you ask? It's a simple variant: it holds a value of type `T` or else it represents an error condition. This way, functions can communicate failures -- and we don't even need an error code to represent success. Which makes sense if you think about it. @@ -342,10 +338,8 @@ int main( int argc, char const * argv[ ] ) ); <7> } ---- -[.text-right] -<> | <> | <> | <> -<1> Configure std::cout to throw on error. +<1> Configure `std::cout` to throw on error. <2> Except if it throws, our `TryBlock` returns `0`, which will be returned from `main` to the OS. <3> If any of the functions we call throws, `try_` will find an appropriate handler to invoke. We'll look at that later. <4> From now on, all exceptions escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name { std::string value; };` ). It's as if every time one of the following functions throws an exception, `preload` says "wait, put this `e_file_name` thing with the exception, it's important!" @@ -417,7 +411,7 @@ int main( int argc, char const * argv[ ] ) } ---- -<1> Configure std::cout to throw on error. +<1> Configure `std::cout` to throw on error. <2> This is the `TryBlock` from the previous listing; if it throws, `try_` will catch the exception, then consider the error handlers that follow, in order, and it will call the first one that can deal with the error: @@ -471,6 +465,11 @@ include::{sourcedir}/synopses/error.adoc[] ''' +==== `common.hpp` +include::{sourcedir}/synopses/common.adoc[] + +''' + ==== `result.hpp` include::{sourcedir}/synopses/result.adoc[] @@ -494,11 +493,6 @@ include::{sourcedir}/synopses/preload.adoc[] ''' -==== `common.hpp` -include::{sourcedir}/synopses/common.adoc[] - -''' - === Error Handling ==== `handle.hpp` @@ -528,19 +522,19 @@ include::{sourcedir}/synopses/capture_exception.adoc[] <> [[reference]] -== pass:[]Referencepass:[] +== Reference [[e_objects]] -== E-Objects +=== E-Objects -[[is_e_type]] -=== `is_e_type` +[[is_error_type]] +==== `is_error_type` -With LEAF, users can efficiently associate with errors or with exceptions any number of values that pertain to a failure. These values may be of any no-throw movable type `E` for which `<>::value` is `true`. The expectation is that this template will be specialized as needed for e.g. all user-defined error enums. +With LEAF, users can efficiently associate with errors or with exceptions any number of values that pertain to a failure. These values may be of any no-throw movable type `E` for which `<>::value` is `true`. The expectation is that this template will be specialized as needed for e.g. all user-defined error enums. -Throughout this text, types for which `is_e_type` is `true` are called E-types. Objects of those types are called E-objects. +Throughout this text, types for which `is_error_type` is `true` are called E-types. Objects of those types are called E-objects. -The main `is_e_type` template is defined so that `is_e_type::value` is `true` when `E` is: +The main `is_error_type` template is defined so that `is_error_type::value` is `true` when `E` is: * `std::error_code`, * `boost::system::error_code`, @@ -561,12 +555,14 @@ struct e_maximum_temperature { float value; }; By convention, the enclosing C-`struct` names use the `e_` prefix. +''' + [[propagation]] -=== Propagation +==== Propagation -"To propagate" an E-object is to associate it with a particular <> value, making it available to functions that handle that error. +"To propagate" an E-object is to associate it with a particular error (identified by an <>), making it available to functions that handle that error. -More formally, when an E-object is propagated, it is immediately moved to available storage in a `handle_some`, a `handle_all` or a `try_` scope currently active in the calling thread, where it becomes uniquely associated with a specific <> value -- or discarded if storage is not available; see <>. +More formally, when an E-object is propagated, it is immediately moved to available storage in a `handle_some`, a `handle_all` or a `try_` scope currently active in the calling thread, where it becomes uniquely associated with a specific <> -- or discarded if storage is not available; see <>. Various LEAF functions take a list of E-objects to propagate. As an example, if a `copy_file` function that takes the name of the input file and the name of the output file as its arguments detects a failure, it could communicate an error code `ec`, plus the two relevant file names using <>: @@ -575,7 +571,9 @@ Various LEAF functions take a list of E-objects to propagate. As an example, if return leaf::new_error( ec, e_input_name{n1}, e_output_name{n2} ); ---- -=== Diagnostic Information +''' + +==== Diagnostic Information LEAF is able to automatically generate diagnostic messages that include information about all E-objects available in an error-handling scope. For this purpose, it needs to be able to print objects of user-defined E-types. @@ -602,18 +600,146 @@ TIP: This automatically-generated diagnostic messages are developer-friendly, bu ''' -[[reporting]] -== Reporting Errors +[[e_api_function]] +==== `e_api_function` +[source,c++] +---- +namespace boost { namespace leaf { + + struct e_api_function { char const * value; }; + +} } +---- + + +The `e_api_function` type is designed to capture the name of the API function which returned an error. For example, if you're reporting an error from `fread`, you could use `leaf::e_api_function { "fread" }`. + +WARNING: The passed value is stored as a C string (`char const *`), so `value` should only be initialized with a string literal. ''' -[[error]] -=== `error` +[[e_file_name]] +==== `e_file_name` + +[source,c++] +---- +namespace boost { namespace leaf { + + struct e_file_name { std::string value; }; + +} } +---- + +When a file operation fails, you could use `e_file_name` to store the name of the file. + +''' + +[[e_errno]] +==== `e_errno` + +[source,c++] +---- +namespace boost { namespace leaf { + + struct e_errno + { + int value; + friend std::ostream & operator<<( std::ostream & os, e_errno const & err ); + }; + +} } +---- + +To capture `errno`, use `e_errno`. When printed in automatically-generated diagnostic messages, `e_errno` objects use `strerror` to convert the `errno` code to string. + +WARNING: It is a logic error to use `e_errno` with <>; it should be passed to <> directly, or used with <>. + +''' + +[[e_LastError]] +==== `e_LastError` + +[source,c++] +---- +namespace boost { namespace leaf { + + namespace windows + { + struct e_LastError + { + unsigned value; + }; + } + +} } +---- + +`e_LastError` is designed to communicate `GetLastError()` values on Windows. + +''' + +[[e_at_line]] +==== `e_at_line` + +[source,c++] +---- +namespace boost { namespace leaf { + + struct e_at_line { int value; }; + +} } +---- + +`e_at_line` can be used to communicate the line number when communicating errors (for example parse errors) about a text file. + +''' + +[[e_type_info_name]] +==== `e_type_info_name` + +[source,c++] +---- +namespace boost { namespace leaf { + + struct e_type_info_name { char const * value; }; + +} } +---- + +`e_type_info_name` is designed to store the return value of `std::type_info::name`. + +''' + +[[e_source_location]] +==== `e_source_location` + +[source,c++] +---- +namespace boost { namespace leaf { + + struct e_source_location + { + char const * const file; + int const line; + char const * const function; + + friend std::ostream & operator<<( std::ostream & os, e_source_location const & x ); + }; + +} } +---- + +The <>, <> and <> macros capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` into a `e_source_location` object. + +''' + +[[error_id]] +=== `error_id` include::{sourcedir}/synopses/error.adoc[] -Objects of class `error` are values that identify an error across the entire program. They can be copied, moved, assigned to, and compared to other `error` objects. They occupy as much memory, and are as efficient as `unsigned int`. +Objects of class `error_id` are values that identify an error across the entire program. They can be copied, moved, assigned to, and compared to other `error_id` objects. They occupy as much memory, and are as efficient as `unsigned int`. ''' @@ -626,16 +752,16 @@ Objects of class `error` are values that identify an error across the entire pro namespace boost { namespace leaf { template - error new_error( E && ... e ) noexcept; + error_id new_error( E && ... e ) noexcept; } } ---- -Requirements: :: `<>::value` must be `true` for each `E`. +Requirements: :: `<>::value` must be `true` for each `E`. Effects: :: Each of the `e...` objects is <> and uniquely associated with the returned value. -Returns: :: A new `error` value, which is unique across the entire program. +Returns: :: A new `error_id` value, which is unique across the entire program. ''' @@ -647,16 +773,16 @@ Returns: :: A new `error` value, which is unique across the entire program. ---- namespace boost { namespace leaf { - error next_error() noexcept; + error_id next_error() noexcept; } } ---- -Returns: :: The `error` value which will be returned the next time <> is invoked from the calling thread. +Returns: :: The `error_id` value which will be returned the next time <> is invoked from the calling thread. + -This function can be used to associate E-objects with the next `error` value to be reported. Use with caution, only when restricted to reporting errors via specific third-party types, incompatible with LEAF -- for example when reporting an error from a C callback. As soon as control exits this critical path, you should create a <> (which will be equal to the `error` object returned by the earlier call to `next_error`). +This function can be used to associate E-objects with the next `error_id` value to be reported. Use with caution, only when restricted to reporting errors via specific third-party types, incompatible with LEAF -- for example when reporting an error from a C callback. As soon as control exits this critical path, you should create a <> (which will be equal to the `error_id` object returned by the earlier call to `next_error`). -TIP: `error` values are unique across the entire program. +TIP: `error_id` values are unique across the entire program. ''' @@ -668,14 +794,14 @@ TIP: `error` values are unique across the entire program. ---- namespace boost { namespace leaf { - error last_error() noexcept; + error_id last_error() noexcept; } } ---- -Returns: :: The `error` value returned the last time <> was invoked from the calling thread. +Returns: :: The `error_id` value returned the last time <> was invoked from the calling thread. -TIP: `error` values are unique across the entire program. +TIP: `error_id` values are unique across the entire program. ''' @@ -688,7 +814,7 @@ TIP: `error` values are unique across the entire program. namespace boost { namespace leaf { template - error error::propagate( E && ... e ) const noexcept; + error_id error_id::propagate( E && ... e ) const noexcept; } } ---- @@ -699,7 +825,7 @@ Returns: :: `*this`. ''' -[[operator_eq-error]] +[[operator_eq-error_id]] ==== `operator==` .#include @@ -707,7 +833,7 @@ Returns: :: `*this`. ---- namespace boost { namespace leaf { - bool operator==( error const & e1, error const & e2 ) noexcept; + bool operator==( error_id const & e1, error_id const & e2 ) noexcept; } } ---- @@ -716,7 +842,7 @@ Returns: :: `true` if the two values `e1` and `e2` are equal, `false` otherwise. ''' -[[operator_neq-error]] +[[operator_neq-error_id]] ==== `operator!=` .#include @@ -724,7 +850,7 @@ Returns: :: `true` if the two values `e1` and `e2` are equal, `false` otherwise. ---- namespace boost { namespace leaf { - bool operator!=( error const & e1, error const & e2 ) noexcept; + bool operator!=( error_id const & e1, error_id const & e2 ) noexcept; } } ---- @@ -733,7 +859,7 @@ Returns: :: `!(e1==e2)`. ''' -[[operator_shl-error]] +[[operator_shl-error_id]] ==== `operator<<` .#include @@ -741,7 +867,7 @@ Returns: :: `!(e1==e2)`. ---- namespace boost { namespace leaf { - std::ostream & operator<<( std::ostream & os, error const & e ) + std::ostream & operator<<( std::ostream & os, error_id const & e ) } } ---- @@ -750,6 +876,145 @@ Effects: :: Prints an `unsigned int` value that uniquely identifies the value `e ''' +[[preload]] +=== `preload` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + <> preload( E && ... e ) noexcept; + +} } +---- + +Requirements: :: `<>::value` must be `true` for each `E`. + +Effects: :: All `e...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: +* If a new <> was created (in the calling thread) since the object returned by `preload` was created, the stored `e...` objects are <> and become associated with the _last_ such `leaf::error_id`; +* Otherwise, if `std::unhandled_exception()` returns `true`, the stored `e...` objects are propagated and become associated with the _first_ `leaf::error_id` created later on; +* Otherwise, the stored `e...` objects are discarded. + +''' + +[[defer]] +=== `defer` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + <> defer( F && ... f ) noexcept; + +} } +---- + +Requirements: :: Each of the `f~i~` objects must be a function that does not throw exceptions, takes no arguments and returns an object of a no-throw movable type `E~i~` for which `<>::value` is `true`. + +Effects: :: All `f...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: +* If a new <> was created (in the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and each returned object is <> and becomes associated with the _last_ such `leaf::error_id`; +* Otherwise, if `std::unhandled_exception()` returns `true`, each of the stored `f...` is called, and the returned objects are propagated and become associated with the _first_ `leaf::error_id` created later on; +* Otherwise, the stored `f...` objects are discarded. + +''' + +[[error_info]] +=== `error_info` + +[source,c++] +---- +namespace boost { namespace leaf { + + class error_info + { + //Constructors unspecified + + public: + + leaf::error_id const & error() const noexcept; + + bool exception_caught() const noexcept; + + std::exception const * exception() const noexcept; + + friend std::ostream & operator<<( std::ostream & os, error_info const & x ); + }; + +} } +---- + +Handlers passed to <>, <> or <> may take an argument of type `error_info const &` to receive information about the error. + +The `error` member function returns the program-wide unique <> of the error. + +The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` otherwise. + +The `exception` member function returns a pointer to the `std::exception` subobject of the exception caught by `try_`, or `0` if that exception could not be converted to `std::exception`. It is illegal to call `exception` unless `exception_caught()` is `true`. + +The `operator<<` overload prints diagnostic information about all E-objects, associated with the <> returned by `error()`, currently stored in any active <>, <> or <> scope. + +''' + +[[diagnostic_info]] +=== `diagnostic_info` + +[source,c++] +---- +namespace boost { namespace leaf { + + class diagnostic_info + { + //Constructors unspecified + + public: + + friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x ); + }; + +} } +---- + +Handlers passed to <>, <> or <> may take an argument of type `diagnostic_info const &` if they need to print diagnostic information about the error. + +The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. + +The additional information includes the total count, as well as the type of the first such E-object. + +''' + +[[verbose_diagnostic_info]] +=== `verbose_diagnostic_info` + +[source,c++] +---- +namespace boost { namespace leaf { + + class verbose_diagnostic_info + { + //Constructors unspecified + + public: + + friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x ); + }; + +} } +---- + +Handlers passed to <>, <> or <> may take an argument of type `verbose_diagnostic_info const &` if they need to print diagnostic information about the error. + +The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. + +The additional information includes the types and the values of all such E-objects. + +WARNING: Using `verbose_diagnostic_info` may allocate memory dynamically. + +== `noexcept` API + [[result]] === `result` @@ -775,7 +1040,7 @@ namespace boost { namespace leaf { result::result( T && v ) noexcept; template - result::result( leaf::error const & e ) noexcept; + result::result( leaf::error_id const & e ) noexcept; template result::result( leaf::error_capture const & ec ) noexcept; @@ -786,12 +1051,12 @@ namespace boost { namespace leaf { A `result` object is in one of three states: * Value state, in which case it contains an object of type `T`, and `<>`/`<>` can be used to access the contained value. -* Error state, in which case it contains an object of type `<>`, and calling `<>`/`<>` throws `leaf::<>`. -* Error-capture state, which is the same as the Error state, but in addition to the <> object, it contains captured E-objects. +* Error state, in which case it contains an object of type <>, and calling `<>`/`<>` throws `leaf::<>`. +* Error-capture state, which is the same as the Error state, but in addition to the <> object, it contains captured E-objects. To get a `result` in value state, initialize it with an object of type `T` or use the default constructor. -To get a `result` in error state, initialize it with an <> object. +To get a `result` in error state, initialize it with an <> object. To get a `result` in error-capture state, call <>. @@ -854,17 +1119,17 @@ namespace boost { namespace leaf { template template - leaf::error result::error( E && ... e ) noexcept; + error_id result::error( E && ... e ) noexcept; } } ---- -This member function is designed to be used in `return` statements in functions that return `result` (or `leaf::<>`) to return an error to the caller. +This member function is designed to be used in `return` statements in functions that return `result` (or `leaf::<>`) to return an error to the caller. Returns: :: -* If `*this` is in value state, returns `<>(std::forward(e...))`, which begins propagating a new `error` value (as opposed to forwarding an existing `error` value); +* If `*this` is in value state, returns `<>(std::forward(e...))`, which begins propagating a new `error_id` value (as opposed to forwarding an existing `error_id` value); * If `*this` is in error-capture state, all captured E-objects are moved to available storage in active <>, <> or <> scopes, `*this` is converted to error state, and then -* If `*this` is in error state, returns `err.<>(std::forward(e...))`, where `err` is the <> value stored in `*this`. +* If `*this` is in error state, returns `err.<>(std::forward(e...))`, where `err` is the <> stored in `*this`. ''' @@ -883,169 +1148,237 @@ namespace boost { namespace leaf { This exception is thrown by `<>()`/`<>()` if they're invoked for a `result` object that is in not in value state. -''' - -[[exception]] -=== `exception` +[[handle_some]] +=== `handle_some` [source,c++] -.#include ---- namespace boost { namespace leaf { - template - <> exception( Ex && ex, E && ... e ) noexcept; + template + decltype(std::declval()()) + handle_some( TryBlock && try_block, Handler && ... handler ); + +} } +---- + +When errors are communicated by `<>`, the `handle_some` function template can be used to recognize and handle some errors, forwarding any unrecognized errors to the caller. + +Requirements: :: + +* The `try_block` must be a function callable with no arguments that returns `<>`; +* All `handler...` arguments must be functions that return the same type as the `try_block`, or types that convert implicitly to it (note: a `result` object can be initialized by an <> object, regardless of `T`); + +* Each of the `handler...` functions: +** may take arguments of <>, either by value or by `const &`, or as a `const *`; +** may take arguments, either by value or by `const &`, of the predicate type `<>`, where `E` is an E-type; +** may take an <> argument by `const &`; +** may take an <> argument by `const &`; +** may take a <> argument by `const &`; +** may not take any other types of arguments. + +Effects: :: + +When `handle_some` is invoked, LEAF reserves space inside of the `handle_some` scope (using automatic storage duration) to store the E-objects it would pass to the appropriate `handler` in case the `try_block` indicates an error. ++ +The list of E-types that `handle_some` needs to store internally is deduced automatically, using the following steps, in order: ++ +-- +* A type list is deduced by concatenating all argument types of all `handler...` functions; +* References and cv-qualifiers are stripped from each type in the list; +* The <>, <>, and <> types are removed from the list; +* Any duplicate types are removed. +-- ++ +Next, `handle_some` calls the `try_block` function, which generally would call other functions as needed. When any function needs to report an error, it passes one or more E-objects to LEAF. For example, a function that returns a `<>` can report an error with a call to <> in a `return` expression: ++ +[source,c++] +---- +if( file-open-fails ) + return leaf::new_error( error_code::file_open_error, e_file_name{fn} ); +---- ++ +As each E-type object is passed to LEAF, it is immediately moved to the `handle_some` (or <>, or <>) scope highest in the call stack that has available storage for that type, and is uniquely associated with the specific <> returned by `new_error`. Objects of E-types for which none of these scopes have available storage are discarded. + +Return Value: :: +If the `<>` object `r` returned by the `try_block` function indicates success, `handle_some` returns `r`. ++ +Otherwise, `handle_some` considers each of the `handler...` functions, in order, until it finds one that matches the reported error. The first matching handler is invoked and the `result` it returns is forwarded to the caller of `handle_some`; if no match is found, `handle_some` returns `r`. ++ +If the value `handle_some` is returning indicates success, all stored E-objects are discarded. Otherwise, each stored E-object is moved to appropriate storage available in `handle_some` (or <>, or <>) scopes higher up the call stack (each moved object retains its unique association with its specific `error_id` value). E-objects for which such storage is not available are discarded. + +Handler Matching Procedure: :: ++ +A `handler` matches the reported failure iff `handle_some` is able to produce values to pass as its arguments. As soon as it is determined that an argument value can not be produced, the current `handler` is dropped and the matching procedure continues with the next `handler`, if any. ++ +If `e` is the specific <> stored in the `result` returned by the `try_block`, each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced as follows: ++ +-- +* If `a`~i~ is of type `error_info const &` or `diagnostic_info const &` or `verbose_diagnostic_info const &`, `handle_some` is always able to produce it. +* If `a`~i~ is of type `A`~i~ `const &`: +** If an E-object of type `A`~i~, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. +** If `A`~i~ is of the predicate type `<>`, if an object of type `E`, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. The handler is also dropped if the expression `a`~i~`()` evaluates to `false`. +* If `a`~i~ is of type `A`~i~ `const pass:[*]`, `handle_some` is always able to produce it: if an E-object of type `A`~i~, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with the address of the stored object, otherwise it is initialized with `0`. +* It is illegal to pass to `handle_some` a `handler` that takes any other argument types. +-- + +TIP: Because `handle_some` can always produce arguments of type `error_info const &`, `diagnostic_info const &` and `verbose_diagnostic_info const &`, if a handler only takes arguments of these types, it will match any error. + +''' + +[[handle_all]] +=== `handle_all` + +[source,c++] +---- +namespace boost { namespace leaf { + + template + typename std::remove_reference()().value())>::type + handle_all( TryBlock && try_block, Handler && ... handler ); } } ---- Requirements: :: -* `Ex` must derive from `std::exception`. -* For all of E, `<>::value` is `true`. -Returns: :: An object of unspecified type which derives publicly from `Ex` *and* from class `<>` such that: -* its `Ex` subobject is initialized by `std::forward(ex)`; -* its `error` subojbect is initialized by `<>(std::forward(e)...`). - -TIP: If thrown, the returned object can be caught as `Ex &` or as `leaf::<>`. - -NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` with the returned object, use <> instead of `leaf::exception`. - -''' - -[[exception_to_result]] -=== `exception_to_result` - -include::{sourcedir}/synopses/exception_to_result.adoc[] - -This function can be used to catch exceptions from a lower-level library and conevrt them to `<>`. - -Returns: :: If `f` returns `T`, `exception_to_result` returns `result`. - -Effects: :: Invokes `f` in a `try` block, catching each individual exception type `Ex`, by value, proceeding as follows: +`handle_all` has the same requirements as <>, with the following change: + -[source,c++] ----- -catch( Ex ex ) -{ - return leaf::new_error(std::move(ex)); -} ----- -+ -A final catch-all is added, as follows: -+ -[source,c++] ----- -catch(...) -{ - return leaf::new_error(std::current_exception()); -} ----- - -[WARNING] -- -* Catching by value slices each individual exception object. -* The order of the types in the `Ex...` list is significant. -* `std::current_exception()` likely allocates memory dynamically. -* Handlers passed to <> / <> should take the converted-to-result exception objects by `const &` (whereas, in case exceptions are handled directly by <> handlers, you would use <> instead). +* All `handler...` arguments must be functions that return `T` (rather than `result`), or types that convert implicitly to `T`. -- +And the following addition: +* At least one `handler` function (usually the last) must match any error. -Example: +Effects: :: +The `handle_all` function works the same as <>, but because it is guaranteed to be able to match any error communicated by the `try_block` with a `handler`, it doesn't need to return a `result`. + +.Example [source,c++] ---- -int compute_answer(); //throws - -//Call compute_answer, convert exceptions to result -leaf::result catch_exceptions() +int main() { - return leaf::catch_exceptions< - ex_type1, - ex_type2>( compute_answer() ); + return leaf::handle_all( + [ ]() -> leaf::result + { + //do work, bail out returning leaf::error_id for any failure + .... + + std::cout << "Success!"; + return 0; + }, + [ ] + { + std::cerr << "Error!"; + return 1; + } ); } ---- -Later, the `ex_type1` and `ex_type2` exceptions can be processed by <> / <>: - -[source,c++] ----- -return leaf::handle_some( - - [ ] -> leaf::result - { - LEAF_AUTO(answer, catch_exceptions()); - //Use answer - .... - return { }; - }, - - [ ]( ex_tppe1 const & ex1 ) - { - //Handle ex_type1 - .... - return { }; - }, - - [ ]( ex_tppe2 const & ex2 ) - { - //Handle ex_type2 - .... - return { }; - }, - - [ ]( std::exception_ptr const & p ) - { - //Handle any other exception from compute_answer. - .... - return { }; - } ); ----- - ''' -[[preload]] -=== `preload` +[[match]] +=== `match` -.#include [source,c++] ---- namespace boost { namespace leaf { - template - <> preload( E && ... e ) noexcept; + template ::type... V> + struct match + { + using type = typename deduce-type::type; + type const & value; + + explicit match( E const & e ) noexcept; + + bool operator()() const noexcept; + }; } } ---- -Requirements: :: `<>::value` must be `true` for each `E`. +NOTE: The `match` template is useful only as argument to a handler function passed to <> (or `handle_all` or `try_`). -Effects: :: All `e...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: -* If a new <> value was created (in the calling thread) since the object returned by `preload` was created, the stored `e...` objects are <> and become associated with the _last_ such `leaf::error` value; -* Otherwise, if `std::unhandled_exception()` returns `true`, the stored `e...` objects are propagated and become associated with the _first_ `leaf::error` value created later on; -* Otherwise, the stored `e...` objects are discarded. +Effects: :: -''' +* If `E` defines an accessible data member `value`: +** The type of the parameter pack `V...` is deduced as `decltype(std::declval().value)`; +** The `match` constructor initializes the `value` reference with `e.value`. -[[defer]] -=== `defer` +* Otherwise: +** The type of the parameter pack `V...` is deduced as `E`; +** The `match` constructor initializes the `value` reference with `e`. -.#include +The `match` template is a predicate function type: `operator()` returns `true` iff the expression `value == V~i~` is `true` for at least one of `V...` values. + +.Example 1: [source,c++] ---- -namespace boost { namespace leaf { +struct error_code { int value; }; - template - <> defer( F && ... f ) noexcept; +error_code e = {42}; -} } +match m1(e); +assert(!m1()); + +match m2(e); +assert(m2()); + +match m3(e); +assert(m3()); + +match m4(e); +assert(!m4()); ---- -Requirements: :: Each of the `f~i~` objects must be a function that does not throw exceptions, takes no arguments and returns an object of a no-throw movable type `E~i~` for which `<>::value` is `true`. +.Example 2: +[source,c++] +---- +enum error_code { e1=1, e2, e3 }; -Effects: :: All `f...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: -* If a new <> value was created (in the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and each returned object is <> and becomes associated with the _last_ such `leaf::error` value; -* Otherwise, if `std::unhandled_exception()` returns `true`, each of the stored `f...` is called, and the returned objects are propagated and become associated with the _first_ `leaf::error` value created later on; -* Otherwise, the stored `f...` objects are discarded. +error_code e = e2; + +match m1(e); +assert(!m1()); + +match m2(e); +assert(m2()); + +match m3(e); +assert(m3()); + +match m4(e); +assert(!m4()); +---- + +[[capture_result]] +=== `capture_result` + +.#include +include::{sourcedir}/synopses/capture_result.adoc[] + +The `capture_result` function can be used to transport E-objects <> when `f` is invoked in one thread, to a different thread which handles errors through <> or <>. + +Requirements: :: +* `F` must be a function type that returns a `<>`; +* Each of the `E...` types must be an <>. + +Returns: :: A function object `fw` of unspecified type, which acts as a wrapper for `f`, taking the same argument types and returning the same `result` type. ++ +When the caller invokes `fw` (presumably from a worker thread) with arguments `a...` of types `A...`: ++ +-- +* Like <>, `fw` reserves storage in its scope for objects of the `E...` types, then +* invokes `f(std::forward(a...))`, which in general will call other functions as needed (any <> objects of the `E...` types are captured in the reserved storage); +-- ++ +If the returned `result` indicates success, it is forwarded to the caller; any E-objects stored in the `fw` scope are discarded. ++ +Otherwise, all stored E-objects are moved to dynamically-allocated memory, which is used to initialize a `result` object in <>. This object is returned to the caller of `fw`. ++ +The returned `result` can safely cross thread boundaries (presumably sent back to the main thread). When it is returned from the `try_block` function passed to <> or <>, its contents are <> (in that thread), and then error handling proceeds as usual. ''' @@ -1171,161 +1504,33 @@ leaf::result add_values() } ---- -''' +== Exception Handling API -[[LEAF_EXCEPTION]] -=== `LEAF_EXCEPTION` +[[exception]] +=== `exception` [source,c++] .#include ---- -#define LEAF_EXCEPTION(...) <> ----- - -Effects: :: This is a variadic macro which forwards its arguments to the function template <>, in addition capturing `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]`, in a `<>` object. - -''' - -[[LEAF_THROW]] -=== `LEAF_THROW` - -[source,c++] -.#include ----- -#define LEAF_THROW(...) throw LEAF_EXCEPTION(__VA_ARGS__) ----- - -Effects: :: Throws the exception object returned by <>. - -== Error Handling - -[[handle_some]] -=== `handle_some` - -[source,c++] ----- namespace boost { namespace leaf { - template - decltype(std::declval()()) - handle_some( TryBlock && try_block, Handler && ... handler ); - -} } ----- - -When errors are communicated by `<>`, the `handle_some` function template can be used to recognize and handle some errors, forwarding any unrecognized errors to the caller. - -Requirements: :: - -* The `try_block` must be a function callable with no arguments that returns `<>`; -* All `handler...` arguments must be functions that return the same type as the `try_block`, or types that convert implicitly to it (note: a `result` object can be initialized by an <> object, regardless of `T`); - -* Each of the `handler...` functions: -** may take arguments of <>, either by value or by `const &`, or as a `const *`; -** may take arguments, either by value or by `const &`, of the predicate type `<>`, where `E` is an E-type; -** may take an <> argument by `const &`; -** may take an <> argument by `const &`; -** may take a <> argument by `const &`; -** may not take any other types of arguments. - -Effects: :: - -When `handle_some` is invoked, LEAF reserves space inside of the `handle_some` scope (using automatic storage duration) to store the E-objects it would pass to the appropriate `handler` in case the `try_block` indicates an error. -+ -The list of E-types that `handle_some` needs to store internally is deduced automatically, using the following steps, in order: -+ --- -* A type list is deduced by concatenating all argument types of all `handler...` functions; -* References and cv-qualifiers are stripped from each type in the list; -* The <>, <>, and <> types are removed from the list; -* Any duplicate types are removed. --- -+ -Next, `handle_some` calls the `try_block` function, which generally would call other functions as needed. When any function needs to report an error, it passes one or more E-objects to LEAF. For example, a function that returns a `<>` can report an error with a call to <> in a `return` expression: -+ -[source,c++] ----- -if( file-open-fails ) - return leaf::new_error( error_code::file_open_error, e_file_name{fn} ); ----- -+ -As each E-type object is passed to LEAF, it is immediately moved to the `handle_some` (or <>, or <>) scope highest in the call stack that has available storage for that type, and is uniquely associated with the specific <> value returned by `new_error`. Objects of E-types for which none of these scopes have available storage are discarded. - -Return Value: :: -If the `<>` object `r` returned by the `try_block` function indicates success, `handle_some` returns `r`. -+ -Otherwise, `handle_some` considers each of the `handler...` functions, in order, until it finds one that matches the reported error. The first matching handler is invoked and the `result` it returns is forwarded to the caller of `handle_some`; if no match is found, `handle_some` returns `r`. -+ -If the value `handle_some` is returning indicates success, all stored E-objects are discarded. Otherwise, each stored E-object is moved to appropriate storage available in `handle_some` (or <>, or <>) scopes higher up the call stack (each moved object retains its unique association with its specific `error` value). E-objects for which such storage is not available are discarded. - -Handler Matching Procedure: :: -+ -A `handler` matches the reported failure iff `handle_some` is able to produce values to pass as its arguments. As soon as it is determined that an argument value can not be produced, the current `handler` is dropped and the matching procedure continues with the next `handler`, if any. -+ -If `e` is the specific <> value stored in the `result` returned by the `try_block`, each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced as follows: -+ --- -* If `a`~i~ is of type `error_info const &` or `diagnostic_info const &` or `verbose_diagnostic_info const &`, `handle_some` is always able to produce it. -* If `a`~i~ is of type `A`~i~ `const &`: -** If an E-object of type `A`~i~, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. -** If `A`~i~ is of the predicate type `<>`, if an object of type `E`, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. The handler is also dropped if the expression `a`~i~`()` evaluates to `false`. -* If `a`~i~ is of type `A`~i~ `const pass:[*]`, `handle_some` is always able to produce it: if an E-object of type `A`~i~, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with the address of the stored object, otherwise it is initialized with `0`. -* It is illegal to pass to `handle_some` a `handler` that takes any other argument types. --- - -TIP: Because `handle_some` can always produce arguments of type `error_info const &`, `diagnostic_info const &` and `verbose_diagnostic_info const &`, if a handler only takes arguments of these types, it will match any error. - -''' - -[[handle_all]] -=== `handle_all` - -[source,c++] ----- -namespace boost { namespace leaf { - - template - typename std::remove_reference()().value())>::type - handle_all( TryBlock && try_block, Handler && ... handler ); + template + <> exception( Ex && ex, E && ... e ) noexcept; } } ---- Requirements: :: +* `Ex` must derive from `std::exception`. +* For all of E, `<>::value` is `true`. -`handle_all` has the same requirements as <>, with the following change: -+ --- -* All `handler...` arguments must be functions that return `T` (rather than `result`), or types that convert implicitly to `T`. --- -And the following addition: -* At least one `handler` function (usually the last) must match any error. +Returns: :: An object of unspecified type which derives publicly from `Ex` *and* from class <> such that: +* its `Ex` subobject is initialized by `std::forward(ex)`; +* its `error_id` subojbect is initialized by `<>(std::forward(e)...`). -Effects: :: +TIP: If thrown, the returned object can be caught as `Ex &` or as `leaf::<>`. -The `handle_all` function works the same as <>, but because it is guaranteed to be able to match any error communicated by the `try_block` with a `handler`, it doesn't need to return a `result`. - -.Example -[source,c++] ----- -int main() -{ - return leaf::handle_all( - [ ]() -> leaf::result - { - //do work, bail out returning leaf::error for any failure - .... - - std::cout << "Success!"; - return 0; - }, - [ ] - { - std::cerr << "Error!"; - return 1; - } ); -} ----- +NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` with the returned object, use <> instead of `leaf::exception`. ''' @@ -1360,125 +1565,28 @@ Effects: :: Return Value: :: * If the `try_block` succeeds, `try_` forwards the return value to the caller; -* Otherwise, it attempts match the <> communicated with the caught exception with a `handler`. If that succeeds, `try_` forwards the return value from the matched `handler` to the caller; +* Otherwise, it attempts match the <> communicated with the caught exception with a `handler`. If that succeeds, `try_` forwards the return value from the matched `handler` to the caller; * Otherwise, `try_` re-throws the caught exception. Handler Matching Procedure: :: -Because each E-object stored in the `try_` scope (see <>) is uniquely associated with a specific <> value, `try_` needs to extract an `error` value from the caught exception in order to access any currently stored E-object (`handle_some` and `handle_all` do not have this challenge, since the <> value they need is readily available in the `<>` object communicating the failure). +Because each E-object stored in the `try_` scope (see <>) is uniquely associated with a specific <>, `try_` needs to extract an `error_id` value from the caught exception in order to access any currently stored E-object (`handle_some` and `handle_all` do not have this challenge, since the <> they need is readily available in the `<>` object communicating the failure). + -When throwing, users are encouraged to pass the exception object through the <> function template -- and `throw` the object it returns. This guarantees that the thrown exception transports a unique `error` value, just like `result` does. +When throwing, users are encouraged to pass the exception object through the <> function template -- and `throw` the object it returns. This guarantees that the thrown exception transports a unique `error_id` value, just like `result` does. + -However, this isn't possible when we don't control the `throw` site, for example if the exception is thrown by a standard function. In this case, E-objects communicated to LEAF are associated with the `error` value returned by <>, which is a preview of sorts, of the `error` value that would be returned by the next call to <>. +However, this isn't possible when we don't control the `throw` site, for example if the exception is thrown by a standard function. In this case, E-objects communicated to LEAF are associated with the `error_id` value returned by <>, which is a preview of sorts, of the `error_id` value that would be returned by the next call to <>. + -Similarly, if the exception object caught by `try_` does not transports an `error` value, E-objects are looked up using the `error` value returned by <> just before `try_` goes through its `handler`-matching search. If imperfect, this approach provides the needed association. +Similarly, if the exception object caught by `try_` does not transports an `error_id` value, E-objects are looked up using the `error_id` value returned by <> just before `try_` goes through its `handler`-matching search. If imperfect, this approach provides the needed association. + -NOTE: <> implements the procedure described above. +NOTE: <> implements the procedure described above. + -With the `error` value thus obtained, the `handler`-matching procedure works the same as in <>, with the following addition to the way each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced: +With the `error_id` value thus obtained, the `handler`-matching procedure works the same as in <>, with the following addition to the way each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced: * If `a`~i~ is of type `A`~i~ `const &`: ** If `A`~i~ is of the predicate type `<>`, `a`~i~ is initialized with the caught `std::exception const &`. The handler is dropped if the expression `a`~i~`()` evaluates to `false`. ''' -[[get_error]] -=== `get_error` - -[source,c++] ----- -namespace boost { namespace leaf { - - error get_error( std::exception const & ex ) noexcept; - -} } ----- - -Returns: :: If the dynamic type of `ex` derives from <>, returns the `error` slice of `ex`. Otherwise, returns the result of <>. - -NOTE: Please read the documentation for <> to understand why `get_error` is needed. - -TIP: Whenever possible, pass exceptions you throw throught the <> function template (or use <>). This ensures that the thrown exceptions derive from <>, which is always preferable to the <> hack. - -''' - -[[match]] -=== `match` - -[source,c++] ----- -namespace boost { namespace leaf { - - template ::type... V> - struct match - { - using type = typename deduce-type::type; - type const & value; - - explicit match( E const & e ) noexcept; - - bool operator()() const noexcept; - }; - -} } ----- - -NOTE: The `match` template is useful only as argument to a handler function passed to <> (or `handle_all` or `try_`). - -Effects: :: - -* If `E` defines an accessible data member `value`: -** The type of the parameter pack `V...` is deduced as `decltype(std::declval().value)`; -** The `match` constructor initializes the `value` reference with `e.value`. - -* Otherwise: -** The type of the parameter pack `V...` is deduced as `E`; -** The `match` constructor initializes the `value` reference with `e`. - -The `match` template is a predicate function type: `operator()` returns `true` iff the expression `value == V~i~` is `true` for at least one of `V...` values. - -.Example 1: -[source,c++] ----- -struct error_code { int value; }; - -error_code e = {42}; - -match m1(e); -assert(!m1()); - -match m2(e); -assert(m2()); - -match m3(e); -assert(m3()); - -match m4(e); -assert(!m4()); ----- - -.Example 2: -[source,c++] ----- -enum error_code { e1=1, e2, e3 }; - -error_code e = e2; - -match m1(e); -assert(!m1()); - -match m2(e); -assert(m2()); - -match m3(e); -assert(m3()); - -match m4(e); -assert(!m4()); ----- - -''' - [[catch_]] === `catch_` @@ -1531,130 +1639,118 @@ assert(!c4()); ''' -[[error_info]] -=== `error_info` +[[get_error_id]] +=== `get_error_id` [source,c++] ---- namespace boost { namespace leaf { - class error_info - { - //Constructors unspecified - - public: - - leaf::error const & get_error() const noexcept; - - bool exception_caught() const noexcept; - - std::exception const * get_exception() const noexcept; - - friend std::ostream & operator<<( std::ostream & os, error_info const & x ); - }; + error_id get_error_id( std::exception const & ex ) noexcept; } } ---- -Handlers passed to <>, <> or <> may take an argument of type `error_info const &` to receive information about the error. +Returns: :: If the dynamic type of `ex` derives from <>, returns the `error_id` slice of `ex`. Otherwise, returns the result of <>. -The `get_error` member function returns the program-wide unique identifier of the <>. +NOTE: Please read the documentation for <> to understand why `get_error_id` is needed. -The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` otherwise. - -The `get_exception` member function returns a pointer to the `std::exception` subobject of the exception caught by `try_`, or `0` if that exception could not be converted to `std::exception`. It is illegal to call `get_exception` unless `exception_caught()` is `true`. - -The `operator<<` overload prints diagnostic information about all E-objects, associated with the <> value returned by `get_error()`, currently stored in any active <>, <> or <> scope. +TIP: Whenever possible, pass exceptions you throw throught the <> function template (or use <>). This ensures that the thrown exceptions derive from <>, which is always preferable to the <> hack. ''' -[[diagnostic_info]] -=== `diagnostic_info` +[[exception_to_result]] +=== `exception_to_result` + +include::{sourcedir}/synopses/exception_to_result.adoc[] + +This function can be used to catch exceptions from a lower-level library and conevrt them to `<>`. + +Returns: :: If `f` returns `T`, `exception_to_result` returns `result`. + +Effects: :: Invokes `f` in a `try` block, catching each individual exception type `Ex`, by value, proceeding as follows: ++ +[source,c++] +---- +catch( Ex ex ) +{ + return leaf::new_error(std::move(ex)); +} +---- ++ +A final catch-all is added, as follows: ++ +[source,c++] +---- +catch(...) +{ + return leaf::new_error(std::current_exception()); +} +---- + +[WARNING] +-- +* Catching by value slices each individual exception object. +* The order of the types in the `Ex...` list is significant. +* `std::current_exception()` likely allocates memory dynamically. +* Handlers passed to <> / <> should take the converted-to-result exception objects by `const &` (whereas, in case exceptions are handled directly by <> handlers, you would use <> instead). +-- + +Example: [source,c++] ---- -namespace boost { namespace leaf { +int compute_answer(); //throws - class diagnostic_info - { - //Constructors unspecified - - public: - - friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x ); - }; - -} } +//Call compute_answer, convert exceptions to result +leaf::result catch_exceptions() +{ + return leaf::catch_exceptions< + ex_type1, + ex_type2>( compute_answer() ); +} ---- -Handlers passed to <>, <> or <> may take an argument of type `diagnostic_info const &` if they need to print diagnostic information about the error. - -The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. - -The additional information includes the total count, as well as the type of the first such E-object. - -''' - -[[verbose_diagnostic_info]] -=== `verbose_diagnostic_info` +Later, the `ex_type1` and `ex_type2` exceptions can be processed by <> / <>: [source,c++] ---- -namespace boost { namespace leaf { +return leaf::handle_some( - class verbose_diagnostic_info + [ ] -> leaf::result { - //Constructors unspecified + LEAF_AUTO(answer, catch_exceptions()); + //Use answer + .... + return { }; + }, - public: + [ ]( ex_tppe1 const & ex1 ) + { + //Handle ex_type1 + .... + return { }; + }, - friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x ); - }; + [ ]( ex_tppe2 const & ex2 ) + { + //Handle ex_type2 + .... + return { }; + }, -} } + [ ]( std::exception_ptr const & p ) + { + //Handle any other exception from compute_answer. + .... + return { }; + } ); ---- -Handlers passed to <>, <> or <> may take an argument of type `verbose_diagnostic_info const &` if they need to print diagnostic information about the error. - -The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. - -The additional information includes the types and the values of all such E-objects. - -WARNING: Using `verbose_diagnostic_info` may allocate memory dynamically. - -== Multi-Thread Programming - -[[capture_result]] -==== `capture_result` - -.#include -include::{sourcedir}/synopses/capture_result.adoc[] - -The `capture_result` function can be used to transport E-objects <> when `f` is invoked in one thread, to a different thread which handles errors through <> or <>. - -Requirements: :: -* `F` must be a function type that returns a `<>`; -* Each of the `E...` types must be an <>. - -Returns: :: A function object `fw` of unspecified type, which acts as a wrapper for `f`, taking the same argument types and returning the same `result` type. -+ -When the caller invokes `fw` (presumably from a worker thread) with arguments `a...` of types `A...`: -+ --- -* Like <>, `fw` reserves storage in its scope for objects of the `E...` types, then -* invokes `f(std::forward(a...))`, which in general will call other functions as needed (any <> objects of the `E...` types are captured in the reserved storage); --- -+ -If the returned `result` indicates success, it is forwarded to the caller; any E-objects stored in the `fw` scope are discarded. -+ -Otherwise, all stored E-objects are moved to dynamically-allocated memory, which is used to initialize a `result` object in <>. This object is returned to the caller of `fw`. -+ -The returned `result` can safely cross thread boundaries (presumably sent back to the main thread). When it is returned from the `try_block` function passed to <> or <>, its contents are <> (in that thread), and then error handling proceeds as usual. - ''' [[capture_exception]] -==== `capture_exception` +=== `capture_exception` .#include include::{sourcedir}/synopses/capture_exception.adoc[] @@ -1680,145 +1776,31 @@ Otherwise, `fw` throws another exception object (of unspecified type) which hold + If this new exception is thrown by the `try_block` function passed to <> (presumably after it was transported to the main thread), its contents are <> (in that thread), and then error handling proceeds as usual. -[[common]] -== Common E-Types -include::{sourcedir}/synopses/common.adoc[] +''' -This header defines some common error types which can be used directly. +[[LEAF_EXCEPTION]] +=== `LEAF_EXCEPTION` + +[source,c++] +.#include +---- +#define LEAF_EXCEPTION(...) <> +---- + +Effects: :: This is a variadic macro which forwards its arguments to the function template <>, in addition capturing `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]`, in a `<>` object. ''' -[[e_api_function]] -=== `e_api_function` +[[LEAF_THROW]] +=== `LEAF_THROW` [source,c++] +.#include ---- -namespace boost { namespace leaf { - - struct e_api_function { char const * value; }; - -} } +#define LEAF_THROW(...) throw LEAF_EXCEPTION(__VA_ARGS__) ---- - -The `e_api_function` type is designed to capture the name of the API function which returned an error. For example, if you're reporting an error from `fread`, you could use `leaf::e_api_function { "fread" }`. - -WARNING: The passed value is stored as a C string (`char const *`), so `value` should only be initialized with a string literal. - -''' - -[[e_file_name]] -=== `e_file_name` - -[source,c++] ----- -namespace boost { namespace leaf { - - struct e_file_name { std::string value; }; - -} } ----- - -When a file operation fails, you could use `e_file_name` to store the name of the file. - -''' - -[[e_errno]] -=== `e_errno` - -[source,c++] ----- -namespace boost { namespace leaf { - - struct e_errno - { - int value; - friend std::ostream & operator<<( std::ostream & os, e_errno const & err ); - }; - -} } ----- - -To capture `errno`, use `e_errno`. When printed in automatically-generated diagnostic messages, `e_errno` objects use `strerror` to convert the `errno` code to string. - -WARNING: It is a logic error to use `e_errno` with <>; it should be passed to <> directly, or used with <>. - -''' - -[[e_LastError]] -=== `e_LastError` - -[source,c++] ----- -namespace boost { namespace leaf { - - namespace windows - { - struct e_LastError - { - unsigned value; - }; - } - -} } ----- - -`e_LastError` is designed to communicate `GetLastError()` values on Windows. - -''' - -[[e_at_line]] -=== `e_at_line` - -[source,c++] ----- -namespace boost { namespace leaf { - - struct e_at_line { int value; }; - -} } ----- - -`e_at_line` can be used to communicate the line number when communicating errors (for example parse errors) about a text file. - -''' - -[[e_type_info_name]] -=== `e_type_info_name` - -[source,c++] ----- -namespace boost { namespace leaf { - - struct e_type_info_name { char const * value; }; - -} } ----- - -`e_type_info_name` is designed to store the return value of `std::type_info::name`. - -''' - -[[e_source_location]] -=== `e_source_location` - -[source,c++] ----- -namespace boost { namespace leaf { - - struct e_source_location - { - char const * const file; - int const line; - char const * const function; - - friend std::ostream & operator<<( std::ostream & os, e_source_location const & x ); - }; - -} } ----- - -The <>, <> and <> macros capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` into a `e_source_location` object. +Effects: :: Throws the exception object returned by <>. [[techniques]] == Programming Techniques @@ -2069,7 +2051,7 @@ leaf::result f() ''' [[technique_exception_to_result]] -=== Converting exceptions to `result` +=== Converting Exceptions to `result` It is sometimes necessary to catch exceptions thrown by lower-level library fuction, and report the error through different means, to a higher-level library which may not use exception handling. @@ -2174,17 +2156,17 @@ try catch( std::exception const & e ) { if( condition ) - leaf::get_error(e).propagate( e_this{....}, e_that{....} ); + leaf::get_error_id(e).propagate( e_this{....}, e_that{....} ); throw; } ---- [.text-right] -<> | <> +<> | <> -The reason we need to use <> is that not all exception types derive from `leaf::<>`. If the caught exception has a `leaf::error` subobject, `get_error` will return that `leaf::error` value. Also, such exceptions can be intercepted by `catch( error e )` if needed. +The reason we need to use <> is that not all exception types derive from `leaf::<>`. If the caught exception has a `leaf::error_id` subobject, `get_error_id` will return that `leaf::error_id` slice. Also, such exceptions can be intercepted by `catch( error_id e )` if needed. -But if the caught exception doesn't have a `leaf::error` subobject, `get_error` returns an unspecified `leaf::error` value, which is temporarily associated with any and all current exceptions, until successfully handled by <>. While this association is imperfect (because it does not pertain to a specific exception object) it is the best that can be done in this case. +But if the caught exception doesn't have a `leaf::error_id` subobject, `get_error_id` returns an unspecified `leaf::error_id`, which is temporarily associated with any and all current exceptions, until successfully handled by <>. While this association is imperfect (because it does not pertain to a specific exception object) it is the best that can be done in this case. ''' @@ -2238,12 +2220,12 @@ enum do_work_error_code namespace boost { namespace leaf { - template<> struct is_e_type: std::true_type { }; + template<> struct is_error_type: std::true_type { }; } } ---- [.text-right] -<> +<> We're now ready to define the `do_work` callback function: @@ -2269,7 +2251,7 @@ int do_work( lua_State * L ) noexcept <1> "Sometimes" `do_work` fails. <2> In case of success, push the result on the Lua stack, return back to Lua. -<3> Associate an `do_work_error_code` object with the *next* `leaf::error` object we will definitely return from the `call_lua` function (below)... +<3> Associate an `do_work_error_code` object with the *next* `leaf::error_id` object we will definitely return from the `call_lua` function (below)... <4> ...once control reaches it, after we tell the Lua interpreter to abort the program. Now we'll write the function that calls the Lua interpreter to execute the Lua function `call_do_work`, which in turn calls `do_work`. We'll return `<>`, so that our caller can get the answer in case of success, or an error: @@ -2297,7 +2279,7 @@ leaf::result call_lua( lua_State * L ) <> | <> | <> <1> Ask the Lua interpreter to call the global Lua function `call_do_work`. -<2> Something went wrong with the call, so we'll return a `leaf::<>`. If this is a `do_work` failure, the `do_work_error_code` object prepared in `do_work` will become associated with this `leaf::error` value. If not, we will still need to communicate that the `lua_pcall` failed with an error code and an error message. +<2> Something went wrong with the call, so we'll return a `leaf::<>`. If this is a `do_work` failure, the `do_work_error_code` object prepared in `do_work` will become associated with this `leaf::error_id`. If not, we will still need to communicate that the `lua_pcall` failed with an error code and an error message. <3> Success! Just return the int answer. Finally, here is the `main` function which handles all failures: @@ -3039,4 +3021,6 @@ meson test == Acknowledgements -Thanks to Peter Dimov for the countless library design discussions. Ivo Belchev, Sean Palmer, Jason King, Vinnie Falco, Glen Fernandes, Nir Friedman -- thanks for your feedback. \ No newline at end of file +Special thanks to Peter Dimov. + +Ivo Belchev, Sean Palmer, Jason King, Vinnie Falco, Glen Fernandes, Nir Friedman -- thanks for the valuable feedback. \ No newline at end of file diff --git a/doc/synopses/error.adoc b/doc/synopses/error.adoc index 80fa405..23db7b5 100644 --- a/doc/synopses/error.adoc +++ b/doc/synopses/error.adoc @@ -3,30 +3,30 @@ ---- namespace boost { namespace leaf { - class error; + class error_id; template - error new_error( E && ... e ) noexcept; + error_id new_error( E && ... e ) noexcept; - error next_error() noexcept; + error_id next_error() noexcept; - error last_error() noexcept; + error_id last_error() noexcept; - class error + class error_id { public: template - error propagate( E && ... e ) const noexcept; + error_id propagate( E && ... e ) const noexcept; - friend bool operator==( error const & e1, error const & e2 ) noexcept; - friend bool operator!=( error const & e1, error const & e2 ) noexcept; + friend bool operator==( error_id const & e1, error_id const & e2 ) noexcept; + friend bool operator!=( error_id const & e1, error_id const & e2 ) noexcept; - friend std::ostream & operator<<( std::ostream & os, error const & e ) + friend std::ostream & operator<<( std::ostream & os, error_id const & e ) }; template - struct is_e_type + struct is_error_type { static constexpr bool value = <>; }; @@ -35,4 +35,4 @@ namespace boost { namespace leaf { ---- [.text-right] -<> | <> | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/result.adoc b/doc/synopses/result.adoc index ae0e6a5..b0b371a 100644 --- a/doc/synopses/result.adoc +++ b/doc/synopses/result.adoc @@ -11,8 +11,8 @@ namespace boost { namespace leaf { result() noexcept; result( T const & v ); result( T && v ) noexcept; - result( leaf::error const & e ) noexcept; - result( leaf::error_capture const & ec ) noexcept; + result( error_id const & e ) noexcept; + result( error_capture const & ec ) noexcept; explicit operator bool() const noexcept; @@ -22,7 +22,7 @@ namespace boost { namespace leaf { T & operator*(); template - leaf::error error( E && ... e ) noexcept; + error_id error( E && ... e ) noexcept; }; struct bad_result: std::exception { }; diff --git a/doc/synopses/try.adoc b/doc/synopses/try.adoc index 9754f3e..a414c22 100644 --- a/doc/synopses/try.adoc +++ b/doc/synopses/try.adoc @@ -6,10 +6,10 @@ namespace boost { namespace leaf { template decltype(std::declval()()) try_( TryBlock && try_block, Handler && ... handler ); - error get_error( std::exception const & ex ) noexcept; + error_id get_error_id( std::exception const & ex ) noexcept; } } ---- [.text-right] -<> | <> +<> | <> diff --git a/example/lua_callback_eh.cpp b/example/lua_callback_eh.cpp index f71972b..8d11d28 100644 --- a/example/lua_callback_eh.cpp +++ b/example/lua_callback_eh.cpp @@ -24,7 +24,7 @@ enum do_work_error_code ec2 }; namespace boost { namespace leaf { - template<> struct is_e_type: std::true_type { }; + template<> struct is_error_type: std::true_type { }; } } struct e_lua_pcall_error { int value; }; diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index d001b69..485441c 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -24,7 +24,7 @@ enum do_work_error_code ec2 }; namespace boost { namespace leaf { - template<> struct is_e_type: std::true_type { }; + template<> struct is_error_type: std::true_type { }; } } struct e_lua_pcall_error { int value; }; diff --git a/example/print_file_result.cpp b/example/print_file_result.cpp index 805b40d..39f7f0b 100644 --- a/example/print_file_result.cpp +++ b/example/print_file_result.cpp @@ -26,9 +26,9 @@ enum error_code cout_error }; -// To enable LEAF to work with our error_code enum, we need to specialize the is_e_type template: +// To enable LEAF to work with our error_code enum, we need to specialize the is_error_type template: namespace boost { namespace leaf { - template<> struct is_e_type: std::true_type { }; + template<> struct is_error_type: std::true_type { }; } } diff --git a/example/print_half.cpp b/example/print_half.cpp index 0cf62cf..7d122d4 100644 --- a/example/print_half.cpp +++ b/example/print_half.cpp @@ -23,7 +23,7 @@ enum class ConversionErrc }; namespace boost { namespace leaf { - template <> struct is_e_type: public std::true_type { }; + template <> struct is_error_type: public std::true_type { }; } } leaf::result convert(const std::string& str) noexcept diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp index ef6d671..717e843 100644 --- a/include/boost/leaf/capture_exception.hpp +++ b/include/boost/leaf/capture_exception.hpp @@ -44,9 +44,9 @@ namespace boost { namespace leaf { assert(ds); if( had_error_ ) { - error_id err = ds->unload(); - assert(err==*this); - (void) err; + error_id id = ds->unload(); + assert(id==*this); + (void) id; } else ds->unload(next_error()); @@ -109,9 +109,9 @@ namespace boost { namespace leaf { { throw; } - catch( error_id const & e ) + catch( error_id const & id ) { - throw captured_exception_impl( std::current_exception(), std::make_shared>(e,std::move(ss)), true, &print_types::print ); + throw captured_exception_impl( std::current_exception(), std::make_shared>(id,std::move(ss)), true, &print_types::print ); } catch(...) { diff --git a/include/boost/leaf/detail/dynamic_store_impl.hpp b/include/boost/leaf/detail/dynamic_store_impl.hpp index 74d743f..86bf29f 100644 --- a/include/boost/leaf/detail/dynamic_store_impl.hpp +++ b/include/boost/leaf/detail/dynamic_store_impl.hpp @@ -19,12 +19,12 @@ namespace boost { namespace leaf { template struct tuple_for_each { - static void unload( error_id const & e, Tuple && tup ) noexcept + static void unload( error_id const & id, Tuple && tup ) noexcept { - tuple_for_each::unload(e,std::move(tup)); + tuple_for_each::unload(id,std::move(tup)); auto && opt = std::get(std::move(tup)); if( opt.has_value() ) - (void) e.propagate(std::move(opt).value()); + (void) id.propagate(std::move(opt).value()); } static void print( std::ostream & os, Tuple const & tup ) @@ -49,30 +49,30 @@ namespace boost { namespace leaf { class dynamic_store_impl: public dynamic_store { - error_id e_; + error_id id_; std::tuple...> s_; error_id const & error() const noexcept { - return e_; + return id_; } error_id unload() noexcept { - return unload(e_); + return unload(id_); } - error_id unload( error_id const & e ) noexcept + error_id unload( error_id const & id ) noexcept { - dynamic_store_internal::tuple_for_each::unload(e,std::move(s_)); - return e; + dynamic_store_internal::tuple_for_each::unload(id,std::move(s_)); + return id; } public: - dynamic_store_impl( error_id const & e, static_store && ss ) noexcept: - e_(e), - s_(std::make_tuple( std::get,decltype(ss.s_)>::value>(std::move(ss.s_)).extract_optional(e)... )) + dynamic_store_impl( error_id const & id, static_store && ss ) noexcept: + id_(id), + s_(std::make_tuple( std::get,decltype(ss.s_)>::value>(std::move(ss.s_)).extract_optional(id)... )) { } }; diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp index 6823356..bdfc748 100644 --- a/include/boost/leaf/detail/static_store.hpp +++ b/include/boost/leaf/detail/static_store.hpp @@ -141,11 +141,11 @@ namespace boost { namespace leaf { public slot { public: - optional extract_optional( error_id const & e ) && noexcept + optional extract_optional( error_id const & id ) && noexcept { slot const & s = *this; - if( s.has_value() && s.value().e==e ) - return optional(std::move(*this).value().v); + if( s.has_value() && s.value().id==id ) + return optional(std::move(*this).value().e); else return optional(); } @@ -199,7 +199,7 @@ namespace boost { namespace leaf { static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept { auto & sl = std::get,SlotsTuple>::value>(tup); - return sl.has_value() && sl.value().e==ei.error(); + return sl.has_value() && sl.value().id==ei.error(); } }; @@ -221,7 +221,7 @@ namespace boost { namespace leaf { if( sl.has_value() ) { auto const & v = sl.value(); - return v.e==ei.error() && match(v.v)(); + return v.id==ei.error() && match(v.e)(); } else return false; @@ -357,7 +357,7 @@ namespace boost { namespace leaf { //////////////////////////////////////// template struct argument_matches_any_error: std::false_type { }; - template struct argument_matches_any_error: is_e_type { }; + template struct argument_matches_any_error: is_error_type { }; template <> struct argument_matches_any_error: std::true_type { }; template <> struct argument_matches_any_error: std::true_type { }; template <> struct argument_matches_any_error: std::true_type { }; @@ -427,14 +427,14 @@ namespace boost { namespace leaf { } template - P const * peek( error_id const & e ) const noexcept + P const * peek( error_id const & id ) const noexcept { auto & opt = std::get::value>(s_); if( opt.has_value() ) { - auto & x = opt.value(); - if( x.e==e ) - return &x.v; + auto & v = opt.value(); + if( v.id==id ) + return &v.e; } return 0; } diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 7ffdaf6..202e511 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -104,9 +104,9 @@ namespace boost { namespace leaf { return *this; } - friend std::ostream & operator<<( std::ostream & os, error_id const & e ) + friend std::ostream & operator<<( std::ostream & os, error_id const & id ) { - os << e.id_; + os << id.id_; return os; } @@ -139,14 +139,14 @@ namespace boost { namespace leaf { slot_base( slot_base const & ) = delete; slot_base & operator=( slot_base const & ) = delete; - virtual bool slot_print( std::ostream &, error_id const & e ) const = 0; + virtual bool slot_print( std::ostream &, error_id const & ) const = 0; public: - static void print( std::ostream & os, error_id const & e ) + static void print( std::ostream & os, error_id const & id ) { for( slot_base const * p = first(); p; p=p->next_ ) - if( p->slot_print(os,e) ) + if( p->slot_print(os,id) ) os << std::endl; } @@ -184,23 +184,23 @@ namespace boost { namespace leaf { error_info( error_info const & ) = delete; error_info & operator=( error_info const & ) = delete; - error_id const e_; + error_id const id_; std::exception const * const ex_; leaf_detail::captured_exception const * const cap_; void (* const print_ex_)( std::ostream &, std::exception const *, leaf_detail::captured_exception const * ); public: - explicit error_info( error_id const & e ) noexcept: - e_(e), + explicit error_info( error_id const & id ) noexcept: + id_(id), ex_(0), cap_(0), print_ex_(0) { } - error_info( error_id const & e, std::exception const * ex, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: - e_(e), + error_info( error_id const & id, std::exception const * ex, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: + id_(id), ex_(ex), cap_(0), print_ex_(print_ex) @@ -208,8 +208,8 @@ namespace boost { namespace leaf { assert(print_ex_!=0); } - error_info( error_id const & e, std::exception const * ex, leaf_detail::captured_exception const * cap, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: - e_(e), + error_info( error_id const & id, std::exception const * ex, leaf_detail::captured_exception const * cap, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: + id_(id), ex_(ex), cap_(cap), print_ex_(print_ex) @@ -219,7 +219,7 @@ namespace boost { namespace leaf { error_id const & error() const noexcept { - return e_; + return id_; } bool exception_caught() const noexcept @@ -235,10 +235,10 @@ namespace boost { namespace leaf { friend std::ostream & operator<<( std::ostream & os, error_info const & x ) { - os << "leaf::error_id: " << x.e_ << std::endl; + os << "leaf::error_id: " << x.id_ << std::endl; if( x.print_ex_ ) x.print_ex_(os,x.ex_,x.cap_); - leaf_detail::slot_base::print(os,x.e_); + leaf_detail::slot_base::print(os,x.id_); return os; } }; @@ -271,7 +271,7 @@ namespace boost { namespace leaf { } template - struct is_e_type: leaf_detail::is_error_type_default + struct is_error_type: leaf_detail::is_error_type_default { }; @@ -432,25 +432,25 @@ namespace boost { namespace leaf { namespace leaf_detail { template - struct ev_type + struct id_e_pair { - error_id e; - E v; + error_id id; + E e; - explicit ev_type( error_id const & e ) noexcept: + explicit id_e_pair( error_id const & id ) noexcept: + id(id) + { + } + + id_e_pair( error_id const & id, E const & e ): + id(id), e(e) { } - ev_type( error_id const & e, E const & v ): - e(e), - v(v) - { - } - - ev_type( error_id const & e, E && v ) noexcept: - e(e), - v(std::forward(v)) + id_e_pair( error_id const & id, E && e ) noexcept: + id(id), + e(std::forward(e)) { } }; @@ -469,13 +469,13 @@ namespace boost { namespace leaf { template class slot: slot_base, - optional> + optional> { slot( slot const & ) = delete; slot & operator=( slot const & ) = delete; - typedef optional> base; + typedef optional> base; slot * prev_; - static_assert(is_e_type::value,"Not an error type"); + static_assert(is_error_type::value,"Not an error type"); bool slot_print( std::ostream &, error_id const & ) const; @@ -501,61 +501,61 @@ namespace boost { namespace leaf { } template - void put_unexpected( ev_type const & ev ) noexcept + void put_unexpected( id_e_pair const & id_e ) noexcept { if( slot * p = tl_slot_ptr() ) { if( p->has_value() ) { - auto & p_ev = p->value(); - if( p_ev.e==ev.e ) + auto & p_id_e = p->value(); + if( p_id_e.id==id_e.id ) { - ++p_ev.v.count; + ++p_id_e.e.count; return; } } - (void) p->put( ev_type(ev.e,diagnostic_info(&type)) ); + (void) p->put( id_e_pair(id_e.id,diagnostic_info(&type)) ); } } template - void put_verbose_diagnostic_info( ev_type const & ev ) noexcept + void put_verbose_diagnostic_info( id_e_pair const & id_e ) noexcept { if( slot * sl = tl_slot_ptr() ) { if( auto * pv = sl->has_value() ) { - if( pv->e!=ev.e ) + if( pv->id!=id_e.id ) { - pv->e = ev.e; - pv->v.reset(); + pv->id = id_e.id; + pv->e.reset(); } - pv->v.add(ev.v); + pv->e.add(id_e.e); } else - sl->emplace(ev.e).v.add(ev.v); + sl->emplace(id_e.id).e.add(id_e.e); } } template - void no_expect_slot( ev_type const & ev ) noexcept + void no_expect_slot( id_e_pair const & id_e ) noexcept { - put_unexpected(ev); - put_verbose_diagnostic_info(ev); + put_unexpected(id_e); + put_verbose_diagnostic_info(id_e); } template - int put_slot( E && v, error_id const & e ) noexcept + int put_slot( E && e, error_id const & id ) noexcept { using T = typename std::remove_cv::type>::type; if( slot * p = tl_slot_ptr() ) - (void) p->put( ev_type(e,std::forward(v)) ); + (void) p->put( id_e_pair(id,std::forward(e)) ); else { int c = tl_unexpected_enabled_counter(); assert(c>=0); if( c ) - no_expect_slot( ev_type(e,std::forward(v)) ); + no_expect_slot( id_e_pair(id,std::forward(e)) ); } return 0; } @@ -573,7 +573,7 @@ namespace boost { namespace leaf { { if( prev_ ) { - optional> & p = *prev_; + optional> & p = *prev_; p = std::move(*this); } else @@ -587,12 +587,12 @@ namespace boost { namespace leaf { } template - bool slot::slot_print( std::ostream & os, error_id const & e ) const + bool slot::slot_print( std::ostream & os, error_id const & id ) const { if( tl_slot_ptr()==this ) - if( ev_type const * ev = has_value() ) - if( ev->e==e ) - return diagnosticv)>::print(os,ev->v); + if( id_e_pair const * id_e = has_value() ) + if( id_e->id==id ) + return diagnostice)>::print(os,id_e->e); return false; } diff --git a/include/boost/leaf/preload.hpp b/include/boost/leaf/preload.hpp index 0003e23..a3977e6 100644 --- a/include/boost/leaf/preload.hpp +++ b/include/boost/leaf/preload.hpp @@ -17,10 +17,10 @@ namespace boost { namespace leaf { template struct tuple_for_each_preload { - static void trigger( Tuple & tup, error_id const & e ) noexcept + static void trigger( Tuple & tup, error_id const & id ) noexcept { - tuple_for_each_preload::trigger(tup,e); - std::get(tup).trigger(e); + tuple_for_each_preload::trigger(tup,id); + std::get(tup).trigger(id); } }; @@ -39,22 +39,22 @@ namespace boost { namespace leaf { class preloaded_item { slot * s_; - E v_; + E e_; public: - explicit preloaded_item( E && v ) noexcept: + explicit preloaded_item( E && e ) noexcept: s_(tl_slot_ptr()), - v_(std::forward(v)) + e_(std::forward(e)) { } - void trigger( error_id const & e ) noexcept + void trigger( error_id const & id ) noexcept { if( s_ ) { - if( !s_->has_value() || s_->value().e!=e ) - s_->put( leaf_detail::ev_type(e,std::move(v_)) ); + if( !s_->has_value() || s_->value().id!=id ) + s_->put( leaf_detail::id_e_pair(id,std::move(e_)) ); } else { @@ -62,7 +62,7 @@ namespace boost { namespace leaf { int c = tl_unexpected_enabled_counter(); assert(c>=0); if( c ) - no_expect_slot( ev_type(e,std::forward(v_)) ); + no_expect_slot( id_e_pair(id,std::forward(e_)) ); } } }; @@ -73,21 +73,21 @@ namespace boost { namespace leaf { preloaded & operator=( preloaded const & ) = delete; std::tuple...> p_; - error_id e_; + error_id id_; bool moved_; public: explicit preloaded( E && ... e ) noexcept: p_(preloaded_item(std::forward(e))...), - e_(last_error()), + id_(last_error()), moved_(false) { } preloaded( preloaded && x ) noexcept: p_(std::move(x.p_)), - e_(std::move(x.e_)), + id_(std::move(x.id_)), moved_(false) { x.moved_ = true; @@ -97,14 +97,14 @@ namespace boost { namespace leaf { { if( moved_ ) return; - error_id const e = last_error(); - if( e==e_ ) + error_id const id = last_error(); + if( id==id_ ) { if( std::uncaught_exception() ) leaf_detail::tuple_for_each_preload::trigger(p_,next_error()); } else - leaf_detail::tuple_for_each_preload::trigger(p_,e); + leaf_detail::tuple_for_each_preload::trigger(p_,id); } }; } // leaf_detail @@ -134,12 +134,12 @@ namespace boost { namespace leaf { { } - void trigger( error_id const & e ) noexcept + void trigger( error_id const & id ) noexcept { if( s_ ) { - if( !s_->has_value() || s_->value().e!=e ) - s_->put( leaf_detail::ev_type(e,f_()) ); + if( !s_->has_value() || s_->value().id!=id ) + s_->put( leaf_detail::id_e_pair(id,f_()) ); } else { @@ -147,7 +147,7 @@ namespace boost { namespace leaf { int c = tl_unexpected_enabled_counter(); assert(c>=0); if( c ) - no_expect_slot( ev_type(e,std::forward(f_())) ); + no_expect_slot( id_e_pair(id,std::forward(f_())) ); } } }; @@ -157,21 +157,21 @@ namespace boost { namespace leaf { { deferred & operator=( deferred const & ) = delete; std::tuple...> d_; - error_id e_; + error_id id_; bool moved_; public: explicit deferred( F && ... f ) noexcept: d_(deferred_item(std::forward(f))...), - e_(last_error()), + id_(last_error()), moved_(false) { } deferred( deferred && x ) noexcept: d_(std::move(x.d_)), - e_(std::move(x.e_)), + id_(std::move(x.id_)), moved_(false) { x.moved_ = true; @@ -181,14 +181,14 @@ namespace boost { namespace leaf { { if( moved_ ) return; - error_id const e = last_error(); - if( e==e_ ) + error_id const id = last_error(); + if( id==id_ ) { if( std::uncaught_exception() ) leaf_detail::tuple_for_each_preload::trigger(d_,next_error()); } else - leaf_detail::tuple_for_each_preload::trigger(d_,e); + leaf_detail::tuple_for_each_preload::trigger(d_,id); } }; } // leaf_detail diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index 79aa39b..2533984 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -149,8 +149,8 @@ namespace boost { namespace leaf { { } - result( error_id const & e ) noexcept: - err_(e), + result( error_id const & id ) noexcept: + err_(id), which_(leaf_detail::result_variant::err) { } @@ -249,8 +249,8 @@ namespace boost { namespace leaf { result() = default; - result( error_id const & e ) noexcept: - base(e) + result( error_id const & id ) noexcept: + base(id) { } diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp index 1c3169e..9ed77e8 100644 --- a/include/boost/leaf/try.hpp +++ b/include/boost/leaf/try.hpp @@ -15,8 +15,8 @@ namespace boost { namespace leaf { inline error_id get_error_id( std::exception const & ex ) noexcept { - if( auto e = dynamic_cast(&ex) ) - return *e; + if( auto id = dynamic_cast(&ex) ) + return *id; else return next_error(); } diff --git a/test/capture_exception_state_test.cpp b/test/capture_exception_state_test.cpp index 189c1a8..1fd31b9 100644 --- a/test/capture_exception_state_test.cpp +++ b/test/capture_exception_state_test.cpp @@ -33,7 +33,7 @@ struct info }; namespace boost { namespace leaf { - template struct is_e_type>: public std::true_type { }; + template struct is_error_type>: public std::true_type { }; } } int main() diff --git a/test/capture_result_state_test.cpp b/test/capture_result_state_test.cpp index 726dbb8..c01277e 100644 --- a/test/capture_result_state_test.cpp +++ b/test/capture_result_state_test.cpp @@ -33,7 +33,7 @@ struct info }; namespace boost { namespace leaf { - template struct is_e_type>: public std::true_type { }; + template struct is_error_type>: public std::true_type { }; } } int main() diff --git a/test/defer_nested_error_exception_test.cpp b/test/defer_nested_error_exception_test.cpp index e994494..8ba07ac 100644 --- a/test/defer_nested_error_exception_test.cpp +++ b/test/defer_nested_error_exception_test.cpp @@ -35,9 +35,9 @@ void f2() { f1(); } - catch( leaf::error_id e ) + catch( leaf::error_id id ) { - e.propagate( info<3>{3} ); + id.propagate( info<3>{3} ); throw; } } diff --git a/test/defer_nested_new_error_exception_test.cpp b/test/defer_nested_new_error_exception_test.cpp index bdfb596..2a3d118 100644 --- a/test/defer_nested_new_error_exception_test.cpp +++ b/test/defer_nested_new_error_exception_test.cpp @@ -37,9 +37,9 @@ leaf::error_id f2() f1(); BOOST_TEST(false); } - catch( leaf::error_id e ) + catch( leaf::error_id id ) { - e.propagate( info<3>{3} ); + id.propagate( info<3>{3} ); throw; } catch(...) diff --git a/test/handle_all_test.cpp b/test/handle_all_test.cpp index 17b0767..aa0d3a0 100644 --- a/test/handle_all_test.cpp +++ b/test/handle_all_test.cpp @@ -19,7 +19,7 @@ enum class error_code error3 }; namespace boost { namespace leaf { - template <> struct is_e_type: std::true_type { }; + template <> struct is_error_type: std::true_type { }; } } struct error_codes_ { error_code value; }; diff --git a/test/handle_some_basic_test.cpp b/test/handle_some_basic_test.cpp index 7f40047..f3a3439 100644 --- a/test/handle_some_basic_test.cpp +++ b/test/handle_some_basic_test.cpp @@ -17,7 +17,7 @@ enum class error_code }; namespace boost { namespace leaf { - template <> struct is_e_type: public std::true_type { }; + template <> struct is_error_type: public std::true_type { }; } } leaf::result compute_answer( int what_to_do ) noexcept diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp index 83972df..3ccea70 100644 --- a/test/handle_some_test.cpp +++ b/test/handle_some_test.cpp @@ -19,7 +19,7 @@ enum class error_code error3 }; namespace boost { namespace leaf { - template <> struct is_e_type: std::true_type { }; + template <> struct is_error_type: std::true_type { }; } } struct error_codes_ { error_code value; }; diff --git a/test/is_error_type_test.cpp b/test/is_error_type_test.cpp index a779cdc..424f458 100644 --- a/test/is_error_type_test.cpp +++ b/test/is_error_type_test.cpp @@ -12,16 +12,16 @@ struct t2 { }; struct t3 { }; namespace boost { namespace leaf { - template <> struct is_e_type { static constexpr bool value = true; }; + template <> struct is_error_type { static constexpr bool value = true; }; } } namespace leaf = boost::leaf; int main() { - static_assert(leaf::is_e_type::value,"t0"); - static_assert(!leaf::is_e_type::value,"t1"); - static_assert(!leaf::is_e_type::value,"t2"); - static_assert(leaf::is_e_type::value,"t3"); + static_assert(leaf::is_error_type::value,"t0"); + static_assert(!leaf::is_error_type::value,"t1"); + static_assert(!leaf::is_error_type::value,"t2"); + static_assert(leaf::is_error_type::value,"t3"); return 0; } diff --git a/test/preload_nested_error_exception_test.cpp b/test/preload_nested_error_exception_test.cpp index 3992eba..4ed2f24 100644 --- a/test/preload_nested_error_exception_test.cpp +++ b/test/preload_nested_error_exception_test.cpp @@ -35,9 +35,9 @@ void f2() { f1(); } - catch( leaf::error_id e ) + catch( leaf::error_id id ) { - e.propagate( info<3>{3} ); + id.propagate( info<3>{3} ); throw; } } diff --git a/test/preload_nested_new_error_exception_test.cpp b/test/preload_nested_new_error_exception_test.cpp index b06979a..ce733b1 100644 --- a/test/preload_nested_new_error_exception_test.cpp +++ b/test/preload_nested_new_error_exception_test.cpp @@ -37,9 +37,9 @@ leaf::error_id f2() f1(); BOOST_TEST(false); } - catch( leaf::error_id e ) + catch( leaf::error_id id ) { - e.propagate( info<3>{3} ); + id.propagate( info<3>{3} ); throw; } catch(...) diff --git a/test/result_state_test.cpp b/test/result_state_test.cpp index 0d572d7..488b849 100644 --- a/test/result_state_test.cpp +++ b/test/result_state_test.cpp @@ -422,8 +422,8 @@ int main() { // error copy -> copy static_store exp; exp.set_reset(true); - leaf::error_id e = leaf::new_error( e_err{ } ); - leaf::result r1 = e; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -449,8 +449,8 @@ int main() BOOST_TEST(val::count==0); { // error copy -> move static_store exp; - leaf::error_id e = leaf::new_error( e_err{ } ); - leaf::result r1 = e; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -475,8 +475,8 @@ int main() BOOST_TEST(val::count==0); { // error copy -> assign copy static_store exp; - leaf::error_id e = leaf::new_error( e_err{ } ); - leaf::result r1 = e; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -495,8 +495,8 @@ int main() BOOST_TEST(val::count==0); leaf::result r2; r2=std::move(r1); BOOST_TEST(!r2); - leaf::error_id const e = r2.error(); - exp.handle_error(leaf::error_info(e),[ ]{ }); + leaf::error_id const id = r2.error(); + exp.handle_error(leaf::error_info(id),[ ]{ }); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); } @@ -504,8 +504,8 @@ int main() BOOST_TEST(val::count==0); { // error copy -> assign move static_store exp; - leaf::error_id e = leaf::new_error( e_err{ } ); - leaf::result r1 = e; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -528,7 +528,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { // error copy -> capture -> copy - leaf::result r1 = capture_result( [ ] { leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ] { leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -551,7 +551,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { // error copy -> capture -> move - leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -574,7 +574,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { // error copy -> capture -> assign-copy - leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -597,7 +597,7 @@ int main() BOOST_TEST(err::count==0); BOOST_TEST(val::count==0); { // error copy -> capture -> assign-move - leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); BOOST_TEST(val::count==0); @@ -687,8 +687,8 @@ int main() { // void error copy -> copy static_store exp; exp.set_reset(true); - leaf::error_id e = leaf::new_error( e_err{ } ); - leaf::result r1 = e; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = r1; @@ -710,8 +710,8 @@ int main() BOOST_TEST(err::count==0); { // void error copy -> move static_store exp; - leaf::error_id e = leaf::new_error( e_err{ } ); - leaf::result r1 = e; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = std::move(r1); @@ -732,8 +732,8 @@ int main() BOOST_TEST(err::count==0); { // void error copy -> assign copy static_store exp; - leaf::error_id e = leaf::new_error( e_err{ } ); - leaf::result r1 = e; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=r1; @@ -749,15 +749,15 @@ int main() BOOST_TEST(err::count==1); leaf::result r2; r2=std::move(r1); BOOST_TEST(!r2); - leaf::error_id const e = r2.error(); - exp.handle_error(leaf::error_info(e),[ ]{ }); + leaf::error_id const id = r2.error(); + exp.handle_error(leaf::error_info(id),[ ]{ }); BOOST_TEST(err::count==1); } BOOST_TEST(err::count==0); { // void error copy -> assign move static_store exp; - leaf::error_id e = leaf::new_error( e_err{ } ); - leaf::result r1 = e; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=std::move(r1); @@ -776,7 +776,7 @@ int main() } BOOST_TEST(err::count==0); { // void error copy -> capture -> copy - leaf::result r1 = capture_result( [ ] { leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ] { leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = r1; @@ -795,7 +795,7 @@ int main() } BOOST_TEST(err::count==0); { // void error copy -> capture -> move - leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2 = std::move(r1); @@ -814,7 +814,7 @@ int main() } BOOST_TEST(err::count==0); { // void error copy -> capture -> assign-copy - leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=r1; @@ -833,7 +833,7 @@ int main() } BOOST_TEST(err::count==0); { // void error copy -> capture -> assign-move - leaf::result r1 = capture_result( [ ]{ leaf::error_id e = leaf::new_error( e_err{ } ); return leaf::result(e); } )(); + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); BOOST_TEST(!r1); BOOST_TEST(err::count==1); leaf::result r2; r2=std::move(r1); diff --git a/test/static_store_deduction_test.cpp b/test/static_store_deduction_test.cpp index 4f4e202..3fcfb67 100644 --- a/test/static_store_deduction_test.cpp +++ b/test/static_store_deduction_test.cpp @@ -30,7 +30,7 @@ enum class my_error_code error3 }; namespace boost { namespace leaf { - template <> struct is_e_type: std::true_type { }; + template <> struct is_error_type: std::true_type { }; } } void not_called_on_purpose() From dcb578761acd6d93437a07a8e89cc2c4eb6d6f1f Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Tue, 15 Jan 2019 18:10:04 -0800 Subject: [PATCH 34/34] added move assignment to result --- doc/leaf.adoc | 407 +++++++++++++++++++------------- doc/synopses/common.adoc | 33 +-- doc/synopses/error.adoc | 2 +- doc/synopses/handle.adoc | 5 +- doc/synopses/result.adoc | 34 ++- doc/synopses/throw.adoc | 4 +- doc/synopses/try.adoc | 8 +- example/lua_callback_result.cpp | 2 +- example/print_file_eh.cpp | 2 +- include/boost/leaf/result.hpp | 40 +++- 10 files changed, 317 insertions(+), 220 deletions(-) diff --git a/doc/leaf.adoc b/doc/leaf.adoc index 98bbcb0..487316a 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -85,7 +85,7 @@ leaf::result file_read( FILE & f, void * buf, int size ); What's this `result` template, you ask? It's a simple variant: it holds a value of type `T` or else it represents an error condition. This way, functions can communicate failures -- and we don't even need an error code to represent success. Which makes sense if you think about it. -Now on to the `main` function: it brings everything together and handles all the errors that may occur. Did I say *all* the errors? I did, so we'll use `leaf::handle_all`. It has the following signature: +Now on to the `main` function: it will call functions as needed and handle all the errors that occur. Did I say *all* the errors? I did, so we'll use `leaf::handle_all`. It has the following signature: [source,c++] ---- @@ -140,9 +140,9 @@ int main( int argc, char const * argv[ ] ) <1> Our `TryBlock` returns a `result`. In case of success, it will hold `0`, which will be returned from `main` to the OS. <2> If `parse_command_line` returns an error, we return that error to `handle_all` (which invoked us). Otherwise, the returned `file_name` stores a value of type `char const *`, which we can access by "dereferencing" it: `*file_name`. -<3> From now on, all errors escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name { std::string value; };` ). It's as if every time one of the following functions reports an error, `preload` says "wait, put this `e_file_name` thing with the error, it's important!" +<3> From now on, all errors escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name {std::string value;}`). It's as if every time one of the following functions reports an error, `preload` says "wait, put this `e_file_name` thing with the error, it's important!" <4> Call more functions, forward each failure to the caller... -<5> ...but this is slightly different: we didn't get a failure via `result` from another function, this is our own error we've detected! We return a `new_error`, passing the `cout_error` error code and the system `errno` (LEAF defines `struct e_errno { int value; };` ). +<5> ...but this is slightly different: we didn't get a failure via `result` from another function, this is our own error we've detected! We return a `new_error`, passing the `cout_error` error code and the system `errno` (LEAF defines `struct e_errno {int value;}`). <6> List of error handler goes here. We'll see that later. <7> This concludes the `handle_all` arguments -- as well as our program! @@ -248,9 +248,9 @@ leaf::result> file_open( char const * file_name ) } ---- -If `fopen` succeeds, we return a `shared_ptr` which will automatically call `fclose` as needed. If `fopen` fails, we report an error by calling `new_error`, which takes any number of error objects to send with the error. In this case we pass the error code (`input_file_open_error`), as well as the system `errno` (LEAF defines `struct e_errno { int value; }`). +If `fopen` succeeds, we return a `shared_ptr` which will automatically call `fclose` as needed. If `fopen` fails, we report an error by calling `new_error`, which takes any number of error objects to send with the error. In this case we pass the error code (`input_file_open_error`), as well as the system `errno` (LEAF defines `struct e_errno {int value;}`). -NOTE: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[here]. The https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[other] version of the same program uses exception handling to report errors (see <>). +TIP: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[here]. The https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[other] version of the same program uses exception handling to report errors (see <>). ''' @@ -276,7 +276,7 @@ struct input_file_read_error : virtual input_file_error { }; struct input_eof_error : virtual input_file_error { }; ---- -NOTE: To avoid ambiguities in the dynamic type conversion which occurs when catching a base type, it is generally recommended to use virtual inheritance in exception type hierarchies. +NOTE: To avoid ambiguities in the dynamic type conversion which occur when catching a base type, it is generally recommended to use virtual inheritance in exception type hierarchies. We'll split the job into several functions, each communicating failures by throwing exceptions: @@ -303,7 +303,7 @@ template <> try_( TryBlock && try_block, Handler && ... handler ); ---- -`TryBlock` is a function type, almost always a lambda; `try_` simply returns the value returned by the `try_block`, catching any exception it throws. In that case `try_` calls a suitable error handling function from the `handler...` list. +`TryBlock` is a function type, almost always a lambda; `try_` simply returns the value returned by the `try_block`, catching any exception it throws, in which case `try_` calls a suitable error handling function from the `handler...` list. Let's look at the `TryBlock` our `main` function passes to `try_`: @@ -311,7 +311,7 @@ Let's look at the `TryBlock` our `main` function passes to `try_`: ---- int main( int argc, char const * argv[ ] ) { - std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); <1> + std::cout.exceptions(std::ostream::failbit | std::ostream::badbit); <1> return leaf::try_( @@ -342,8 +342,8 @@ int main( int argc, char const * argv[ ] ) <1> Configure `std::cout` to throw on error. <2> Except if it throws, our `TryBlock` returns `0`, which will be returned from `main` to the OS. <3> If any of the functions we call throws, `try_` will find an appropriate handler to invoke. We'll look at that later. -<4> From now on, all exceptions escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name { std::string value; };` ). It's as if every time one of the following functions throws an exception, `preload` says "wait, put this `e_file_name` thing with the exception, it's important!" -<5> `defer` is similar to `preload`, but instead of an error object, it takes a function that returns it. From this point on, when an exception escapes this scope, `defer` will call the passed function in order to include the relevant `e_errno` with the exception (LEAF defines `struct e_errno { int value; };` ). +<4> From now on, all exceptions escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name {std::string value;}`). It's as if every time one of the following functions throws an exception, `preload` says "wait, put this `e_file_name` thing with the exception, it's important!" +<5> `defer` is similar to `preload`, but instead of the error object, it takes a function that returns it. From this point on, when an exception escapes this scope, `defer` will call the passed function and include the returned `e_errno` with the exception (LEAF defines `struct e_errno {int value;}`). <6> List of error handler goes here. We'll see that later. <7> This concludes the `try_` arguments -- as well as our program! @@ -355,7 +355,7 @@ Now let's look at the second part of the call to `try_`, which lists the error h ---- int main( int argc, char const * argv[ ] ) { - std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); <1> + std::cout.exceptions(std::ostream::failbit | std::ostream::badbit); <1> return leaf::try_( [&] @@ -451,9 +451,11 @@ std::shared_ptr file_open( char const * file_name ) } ---- -If `fopen` succeeds, it returns a `shared_ptr` which will automatically call `fclose` as needed. If `fopen` fails, we throw the exception object returned by `leaf::exception`, which takes as its first argument an exception object, followed by any number of error objects to send with it. In this case we pass the system `errno` (LEAF defines `struct e_errno { int value; }`). The returned object can be caught as `input_file_open_error`. +If `fopen` succeeds, it returns a `shared_ptr` which will automatically call `fclose` as needed. If `fopen` fails, we throw the exception object returned by `leaf::exception`, which takes as its first argument an exception object, followed by any number of error objects to send with it. In this case we pass the system `errno` (LEAF defines `struct e_errno {int value;}`). The returned object can be caught as `input_file_open_error`. -NOTE: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[here]. The https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[other] version of the same program does not use exception handling to report errors (see the <>). +NOTE: `try_` can work with any exception, not only exceptions thrown using `leaf::exception`. + +TIP: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[here]. The https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[other] version of the same program does not use exception handling to report errors (see the <>). [[synopsis]] == Synopsis @@ -532,7 +534,7 @@ include::{sourcedir}/synopses/capture_exception.adoc[] With LEAF, users can efficiently associate with errors or with exceptions any number of values that pertain to a failure. These values may be of any no-throw movable type `E` for which `<>::value` is `true`. The expectation is that this template will be specialized as needed for e.g. all user-defined error enums. -Throughout this text, types for which `is_error_type` is `true` are called E-types. Objects of those types are called E-objects. +Throughout this text, types for which `is_error_type` is `true` are called E-types. Objects of those types are called error objects or E-objects. The main `is_error_type` template is defined so that `is_error_type::value` is `true` when `E` is: @@ -560,11 +562,11 @@ By convention, the enclosing C-`struct` names use the `e_` prefix. [[propagation]] ==== Propagation -"To propagate" an E-object is to associate it with a particular error (identified by an <>), making it available to functions that handle that error. +"To propagate" an E-object is to associate it with a particular <>, making it available to functions that handle that error. More formally, when an E-object is propagated, it is immediately moved to available storage in a `handle_some`, a `handle_all` or a `try_` scope currently active in the calling thread, where it becomes uniquely associated with a specific <> -- or discarded if storage is not available; see <>. -Various LEAF functions take a list of E-objects to propagate. As an example, if a `copy_file` function that takes the name of the input file and the name of the output file as its arguments detects a failure, it could communicate an error code `ec`, plus the two relevant file names using <>: +Various LEAF functions take a list of E-objects to propagate. As an example, if a function `copy_file` that takes the name of the input file and the name of the output file as its arguments detects a failure, it could communicate an error code `ec`, plus the two relevant file names using <>: [source,c++] ---- @@ -573,11 +575,12 @@ return leaf::new_error( ec, e_input_name{n1}, e_output_name{n2} ); ''' +[[diagnostic_information]] ==== Diagnostic Information -LEAF is able to automatically generate diagnostic messages that include information about all E-objects available in an error-handling scope. For this purpose, it needs to be able to print objects of user-defined E-types. +LEAF is able to automatically generate diagnostic messages that include information about all E-objects available to error handlers. For this purpose, it needs to be able to print objects of user-defined E-types. -First, LEAF attempts to bind an unqualified call to `operator<<`, passing a `std::ostream` and the E-object. If that fails, it will also attempt to bind `operator<<` that takes the `.value` of the E-object. If that also doesn't compile, the E-object will not appear in diagnostic messages, though LEAF will still print its type. +To do this, LEAF attempts to bind an unqualified call to `operator<<`, passing a `std::ostream` and the E-object. If that fails, it will also attempt to bind `operator<<` that takes the `.value` of the E-object. If that also doesn't compile, the E-object will not appear in diagnostic messages, though LEAF will still print its type. Even with E-types that define a printable `.value`, the user may still want to overload `operator<<` for the enclosing `struct`, e.g.: @@ -596,7 +599,7 @@ struct e_errno The `e_errno` type above is designed to hold `errno` values. The defined `operator<<` overload will automatically include the output from `strerror` when `e_errno` values are printed (LEAF defines `e_errno` in ``, together with other commonly-used error types). -TIP: This automatically-generated diagnostic messages are developer-friendly, but not user-friendly. Therefore, `operator<<` overloads for E-types should only print technical information in English, and should not attempt to localize strings or to format a user-friendly message; this should be done in error-handling functions specifically designed for that purpose. +TIP: These automatically-generated diagnostic messages are developer-friendly, but not user-friendly. Therefore, `operator<<` overloads for E-types should only print technical information in English, and should not attempt to localize strings or to format a user-friendly message; this should be done in error-handling functions specifically designed for that purpose. ''' @@ -607,13 +610,13 @@ TIP: This automatically-generated diagnostic messages are developer-friendly, bu ---- namespace boost { namespace leaf { - struct e_api_function { char const * value; }; + struct e_api_function {char const * value;}; } } ---- -The `e_api_function` type is designed to capture the name of the API function which returned an error. For example, if you're reporting an error from `fread`, you could use `leaf::e_api_function { "fread" }`. +The `e_api_function` type is designed to capture the name of the API function that failed. For example, if you're reporting an error from `fread`, you could use `leaf::e_api_function {"fread"}`. WARNING: The passed value is stored as a C string (`char const *`), so `value` should only be initialized with a string literal. @@ -626,7 +629,7 @@ WARNING: The passed value is stored as a C string (`char const *`), so `value` s ---- namespace boost { namespace leaf { - struct e_file_name { std::string value; }; + struct e_file_name {std::string value;}; } } ---- @@ -653,8 +656,6 @@ namespace boost { namespace leaf { To capture `errno`, use `e_errno`. When printed in automatically-generated diagnostic messages, `e_errno` objects use `strerror` to convert the `errno` code to string. -WARNING: It is a logic error to use `e_errno` with <>; it should be passed to <> directly, or used with <>. - ''' [[e_LastError]] @@ -669,6 +670,7 @@ namespace boost { namespace leaf { struct e_LastError { unsigned value; + friend std::ostream & operator<<( std::ostream & os, e_LastError const & err ); }; } @@ -691,7 +693,7 @@ namespace boost { namespace leaf { } } ---- -`e_at_line` can be used to communicate the line number when communicating errors (for example parse errors) about a text file. +`e_at_line` can be used to communicate the line number when reporting errors (for example parse errors) about a text file. ''' @@ -739,7 +741,7 @@ The <>, <> and <> macros capture `pass:[ include::{sourcedir}/synopses/error.adoc[] -Objects of class `error_id` are values that identify an error across the entire program. They can be copied, moved, assigned to, and compared to other `error_id` objects. They occupy as much memory, and are as efficient as `unsigned int`. +Values of type `error_id` identify an error across the entire program. They can be copied, moved, assigned to, and compared to other `error_id` objects. They occupy as much memory, and are as efficient as `unsigned int`. ''' @@ -779,10 +781,8 @@ namespace boost { namespace leaf { ---- Returns: :: The `error_id` value which will be returned the next time <> is invoked from the calling thread. -+ -This function can be used to associate E-objects with the next `error_id` value to be reported. Use with caution, only when restricted to reporting errors via specific third-party types, incompatible with LEAF -- for example when reporting an error from a C callback. As soon as control exits this critical path, you should create a <> (which will be equal to the `error_id` object returned by the earlier call to `next_error`). -TIP: `error_id` values are unique across the entire program. +This function can be used to associate E-objects with the next `error_id` value to be reported. Use with caution, only when restricted to reporting errors via specific third-party types, incompatible with LEAF -- for example when reporting an error from a C callback. As soon as control exits this critical path, you should create a <> (which will be equal to the `error_id` object returned by the earlier call to `next_error`). ''' @@ -801,8 +801,6 @@ namespace boost { namespace leaf { Returns: :: The `error_id` value returned the last time <> was invoked from the calling thread. -TIP: `error_id` values are unique across the entire program. - ''' [[propagate]] @@ -867,12 +865,12 @@ Returns: :: `!(e1==e2)`. ---- namespace boost { namespace leaf { - std::ostream & operator<<( std::ostream & os, error_id const & e ) + std::ostream & operator<<( std::ostream & os, error_id const & id ) } } ---- -Effects: :: Prints an `unsigned int` value that uniquely identifies the value `e`. +Effects: :: Prints an `unsigned int` value that uniquely identifies the value `id`. ''' @@ -893,8 +891,8 @@ namespace boost { namespace leaf { Requirements: :: `<>::value` must be `true` for each `E`. Effects: :: All `e...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: -* If a new <> was created (in the calling thread) since the object returned by `preload` was created, the stored `e...` objects are <> and become associated with the _last_ such `leaf::error_id`; -* Otherwise, if `std::unhandled_exception()` returns `true`, the stored `e...` objects are propagated and become associated with the _first_ `leaf::error_id` created later on; +* If <> was invoked (by the calling thread) since the object returned by `preload` was created, the stored `e...` objects are <> and become uniquely associated with the _last_ such `leaf::error_id`; +* Otherwise, if `std::unhandled_exception()` returns `true`, the stored `e...` objects are propagated and will become uniquely associated with the _first_ `leaf::error_id` created later on; * Otherwise, the stored `e...` objects are discarded. ''' @@ -916,9 +914,13 @@ namespace boost { namespace leaf { Requirements: :: Each of the `f~i~` objects must be a function that does not throw exceptions, takes no arguments and returns an object of a no-throw movable type `E~i~` for which `<>::value` is `true`. Effects: :: All `f...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: -* If a new <> was created (in the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and each returned object is <> and becomes associated with the _last_ such `leaf::error_id`; -* Otherwise, if `std::unhandled_exception()` returns `true`, each of the stored `f...` is called, and the returned objects are propagated and become associated with the _first_ `leaf::error_id` created later on; -* Otherwise, the stored `f...` objects are discarded. ++ +-- +* If <> was invoked (by the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and each returned object is <> and becomes uniquely associated with the _last_ such `leaf::error_id`; +* Otherwise, if `std::unhandled_exception()` returns `true`, each of the stored `f...` is called, and each returned object is propagated and will become uniquely associated with the _first_ `leaf::error_id` created later on; +-- ++ +The stored `f...` objects are discarded. ''' @@ -951,11 +953,11 @@ Handlers passed to <>, <> or <> may take an argum The `error` member function returns the program-wide unique <> of the error. -The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` otherwise. +The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` if invoked by <> or <>. The `exception` member function returns a pointer to the `std::exception` subobject of the exception caught by `try_`, or `0` if that exception could not be converted to `std::exception`. It is illegal to call `exception` unless `exception_caught()` is `true`. -The `operator<<` overload prints diagnostic information about all E-objects, associated with the <> returned by `error()`, currently stored in any active <>, <> or <> scope. +The `operator<<` overload prints diagnostic information about each E-object currently stored in any active <>, <> or <> scope, but only if it is associated with the <> returned by `error()`. ''' @@ -980,9 +982,9 @@ namespace boost { namespace leaf { Handlers passed to <>, <> or <> may take an argument of type `diagnostic_info const &` if they need to print diagnostic information about the error. -The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. +The message printed by `operator<<` includes the message printed by `error_info`, followed by basic information about E-objects that were communicated to LEAF (to be associated with the error) for which there was no storage available in any active <>, <> or <> scope (these E-objects were discarded by LEAF, because no handler needed them). -The additional information includes the total count, as well as the type of the first such E-object. +The additional information is limited the type name of the first such E-object, as well as their total count. ''' @@ -1007,11 +1009,11 @@ namespace boost { namespace leaf { Handlers passed to <>, <> or <> may take an argument of type `verbose_diagnostic_info const &` if they need to print diagnostic information about the error. -The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF for which there was no storage available in any active <>, <> or <> scope. +The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF (to be associated with the error) for which there was no storage available in any active <>, <> or <> scope (these E-objects were discarded by LEAF, because no handler needed them). The additional information includes the types and the values of all such E-objects. -WARNING: Using `verbose_diagnostic_info` may allocate memory dynamically. +WARNING: Using `verbose_diagnostic_info` will likely allocate memory dynamically. == `noexcept` API @@ -1020,6 +1022,15 @@ WARNING: Using `verbose_diagnostic_info` may allocate memory dynamically. include::{sourcedir}/synopses/result.adoc[] +The `result` type can be returned by functions which produce a value of type `T` but may fail doing so. + +Requirements: :: `T` must be movable, and its move constructor may not throw. + +Invariant: :: A `result` object is in one of three states: +* Value state, in which case it contains an object of type `T`, and `<>`/`<>` can be used to access the contained value. +* Error state, in which case it contains an object of type <>, and calling `<>`/`<>` throws `leaf::<>`. +* Error-capture state, which is the same as the Error state, but in addition to the <> object, it holds captured E-objects. + ''' [[result::result]] @@ -1033,37 +1044,79 @@ namespace boost { namespace leaf { template result::result() noexcept; - template - result::result( T const & v ); - template result::result( T && v ) noexcept; template - result::result( leaf::error_id const & e ) noexcept; + result::result( T const & v ); template - result::result( leaf::error_capture const & ec ) noexcept; + result::result( leaf::error_id const & err ) noexcept; + + template + result::result( result && ) noexcept; + + template + result::result( result const & ); + + template + template + result::result( result && ) noexcept; + + template + template + result::result( result const & ); } } ---- -A `result` object is in one of three states: +Requirements: :: `T` must be movable, and its move constructor may not throw. -* Value state, in which case it contains an object of type `T`, and `<>`/`<>` can be used to access the contained value. -* Error state, in which case it contains an object of type <>, and calling `<>`/`<>` throws `leaf::<>`. -* Error-capture state, which is the same as the Error state, but in addition to the <> object, it contains captured E-objects. +Effects: :: -To get a `result` in value state, initialize it with an object of type `T` or use the default constructor. +Establishes the `result` invariant: +* To get a `result` in <>, initialize it with an object of type `T` or use the default constructor. +* To get a `result` in <>, initialize it with an <> object. +* To get a `result` in <>, call <>. -To get a `result` in error state, initialize it with an <> object. - -To get a `result` in error-capture state, call <>. +Throws: :: +* Initializing the `result` in Value state may throw, depending on which constructor of `T` is invoked; +* Copying a `result` in Value state throws any exceptions thrown by the `T` copy constructor; +* Other constructors do not throw. TIP: A `result` that is in value state converts to `true` in boolean contexts. A `result` that is not in value state converts to `false` in boolean contexts. ''' +[[result::operator_eq]] +==== `operator=` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + result & result::operator=( result && ) noexcept; + + template + result & result::operator=( result const & ); + + template + template + result & result::operator=( result && ) noexcept; + + template + template + result & result::operator=( result const & ); + +} } +---- + +Effects: :: Destroys `*this`, then re-initializes it using the appropriate `result` constructor. Basic exception-safety guarantee. + +''' + [[result::operator_bool]] ==== Conversion to `bool` @@ -1078,7 +1131,7 @@ namespace boost { namespace leaf { } } ---- -Returns: :: If `*this` was initialized in value state, returns `true`, otherwise returns `false`. See `<>`. +Returns: :: If `*this` is in <>, returns `true`, otherwise returns `false`. . ''' @@ -1105,7 +1158,7 @@ namespace boost { namespace leaf { } } ---- -Effects: :: If `*this` was initialized in value state, returns a reference to the stored value, otherwise throws `leaf::<>`. See `<>`. +Effects: :: If `*this` is in <>, returns a reference to the stored value, otherwise throws `leaf::<>`. ''' @@ -1124,12 +1177,12 @@ namespace boost { namespace leaf { } } ---- -This member function is designed to be used in `return` statements in functions that return `result` (or `leaf::<>`) to return an error to the caller. +This member function is designed for use in `return` statements in functions that return `result` (or `leaf::<>`) to return an error to the caller. Returns: :: -* If `*this` is in value state, returns `<>(std::forward(e...))`, which begins propagating a new `error_id` value (as opposed to forwarding an existing `error_id` value); -* If `*this` is in error-capture state, all captured E-objects are moved to available storage in active <>, <> or <> scopes, `*this` is converted to error state, and then -* If `*this` is in error state, returns `err.<>(std::forward(e...))`, where `err` is the <> stored in `*this`. +* If `*this` is in <>, returns `<>(std::forward(e...))`, which creates a new error (as opposed to forwarding an existing error); +* If `*this` is in <>, all captured E-objects are <>, `*this` is converted to error state, and then +* If `*this` is in <>, returns `err.<>(std::forward(e...))`, where `err` is the <> stored in `*this`. ''' @@ -1146,7 +1199,9 @@ namespace boost { namespace leaf { } } ---- -This exception is thrown by `<>()`/`<>()` if they're invoked for a `result` object that is in not in value state. +This exception is thrown by `<>()`/`<>()` if they're invoked for a `result` object that is in not in <>. + +''' [[handle_some]] === `handle_some` @@ -1173,7 +1228,7 @@ Requirements: :: ** may take arguments of <>, either by value or by `const &`, or as a `const *`; ** may take arguments, either by value or by `const &`, of the predicate type `<>`, where `E` is an E-type; ** may take an <> argument by `const &`; -** may take an <> argument by `const &`; +** may take a <> argument by `const &`; ** may take a <> argument by `const &`; ** may not take any other types of arguments. @@ -1198,7 +1253,7 @@ if( file-open-fails ) return leaf::new_error( error_code::file_open_error, e_file_name{fn} ); ---- + -As each E-type object is passed to LEAF, it is immediately moved to the `handle_some` (or <>, or <>) scope highest in the call stack that has available storage for that type, and is uniquely associated with the specific <> returned by `new_error`. Objects of E-types for which none of these scopes have available storage are discarded. +As each E-type object is passed to LEAF, it is immediately moved to the `handle_some` (or <>, or <>) scope highest in the call stack that has available storage for that type, where it is uniquely associated with a specific <> (in this case, the value returned by `new_error`). Objects of E-types for which none of these scopes have available storage are discarded. Return Value: :: If the `<>` object `r` returned by the `try_block` function indicates success, `handle_some` returns `r`. @@ -1211,18 +1266,18 @@ Handler Matching Procedure: :: + A `handler` matches the reported failure iff `handle_some` is able to produce values to pass as its arguments. As soon as it is determined that an argument value can not be produced, the current `handler` is dropped and the matching procedure continues with the next `handler`, if any. + -If `e` is the specific <> stored in the `result` returned by the `try_block`, each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced as follows: +If `err` is the specific <> stored in the `result` returned by the `try_block`, each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced as follows: + -- +* If `a`~i~ is taken as `A`~i~ `const &` or by value: +** If an E-object of type `A`~i~, associated with `err`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. +** If `A`~i~ is of the predicate type `<>`, if an object of type `E`, associated with `err`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. The handler is also dropped if the expression `a`~i~`()` evaluates to `false`. +* If `a`~i~ is of type `A`~i~ `const pass:[*]`, `handle_some` is always able to produce it: if an E-object of type `A`~i~, associated with `err`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with the address of the stored object, otherwise it is initialized with `0`. * If `a`~i~ is of type `error_info const &` or `diagnostic_info const &` or `verbose_diagnostic_info const &`, `handle_some` is always able to produce it. -* If `a`~i~ is of type `A`~i~ `const &`: -** If an E-object of type `A`~i~, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. -** If `A`~i~ is of the predicate type `<>`, if an object of type `E`, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. The handler is also dropped if the expression `a`~i~`()` evaluates to `false`. -* If `a`~i~ is of type `A`~i~ `const pass:[*]`, `handle_some` is always able to produce it: if an E-object of type `A`~i~, associated with `e`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with the address of the stored object, otherwise it is initialized with `0`. * It is illegal to pass to `handle_some` a `handler` that takes any other argument types. -- -TIP: Because `handle_some` can always produce arguments of type `error_info const &`, `diagnostic_info const &` and `verbose_diagnostic_info const &`, if a handler only takes arguments of these types, it will match any error. +TIP: Because `handle_some` can always produce arguments of type `error_info const &`, `diagnostic_info const &` and `verbose_diagnostic_info const &`, a handler that only takes arguments of these types will match any error. ''' @@ -1245,10 +1300,10 @@ Requirements: :: `handle_all` has the same requirements as <>, with the following change: + -- -* All `handler...` arguments must be functions that return `T` (rather than `result`), or types that convert implicitly to `T`. +* If `T` is the type returned by the `TryBlock`, all `handler...` arguments must be functions that return `T` or a type that converts implicitly to `T` (note: `handle_some` handlers are required to return `result`). -- And the following addition: -* At least one `handler` function (usually the last) must match any error. +* At least one `handler` function (usually the last) must match any error. This is enforced at compile-time. Effects: :: @@ -1285,10 +1340,10 @@ int main() ---- namespace boost { namespace leaf { - template ::type... V> + template ::type... V> struct match { - using type = typename deduce-type::type; + using type = typename deduced-type::type; type const & value; explicit match( E const & e ) noexcept; @@ -1353,6 +1408,8 @@ match m4(e); assert(!m4()); ---- +''' + [[capture_result]] === `capture_result` @@ -1371,7 +1428,7 @@ When the caller invokes `fw` (presumably from a worker thread) with arguments `a + -- * Like <>, `fw` reserves storage in its scope for objects of the `E...` types, then -* invokes `f(std::forward(a...))`, which in general will call other functions as needed (any <> objects of the `E...` types are captured in the reserved storage); +* invokes `f(std::forward(a...))`, which in general will call other functions as needed (any <> objects of the `E...` types are captured in the reserved storage). -- + If the returned `result` indicates success, it is forwarded to the caller; any E-objects stored in the `fw` scope are discarded. @@ -1391,7 +1448,7 @@ The returned `result` can safely cross thread boundaries (presumably sent bac #define LEAF_ERROR(...) <> ---- -Effects: :: `LEAF_ERROR(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically passed to `new_error`, in addition to all E-objects `e...`, in a `<>` object. +Effects: :: `LEAF_ERROR(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically passed to `new_error` in a `<>` object (in addition to all `e...` objects). ''' @@ -1491,19 +1548,6 @@ leaf::result add_values() } ---- -Alternatively: - -.Try to send a message, then compute a value, report errors without LEAF_CHECK: ----- -leaf::result add_values() -{ - if( auto r = send_message("Hello!") ) - return compute_value(); - else - return r.error(); -} ----- - == Exception Handling API [[exception]] @@ -1522,13 +1566,13 @@ namespace boost { namespace leaf { Requirements: :: * `Ex` must derive from `std::exception`. -* For all of E, `<>::value` is `true`. +* For each `E`~i~ int `E...`, `<>::value` is `true`. Returns: :: An object of unspecified type which derives publicly from `Ex` *and* from class <> such that: * its `Ex` subobject is initialized by `std::forward(ex)`; * its `error_id` subojbect is initialized by `<>(std::forward(e)...`). -TIP: If thrown, the returned object can be caught as `Ex &` or as `leaf::<>`. +TIP: If thrown, the returned object can be caught as `Ex &` or as `leaf::<> &`. NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` with the returned object, use <> instead of `leaf::exception`. @@ -1565,7 +1609,7 @@ Effects: :: Return Value: :: * If the `try_block` succeeds, `try_` forwards the return value to the caller; -* Otherwise, it attempts match the <> communicated with the caught exception with a `handler`. If that succeeds, `try_` forwards the return value from the matched `handler` to the caller; +* Otherwise, it attempts to match the <> communicated with the caught exception with a `handler`. If that succeeds, `try_` forwards the return value from the matched `handler` to the caller; * Otherwise, `try_` re-throws the caught exception. Handler Matching Procedure: :: @@ -1582,7 +1626,7 @@ NOTE: <> implements the procedure described above. + With the `error_id` value thus obtained, the `handler`-matching procedure works the same as in <>, with the following addition to the way each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced: -* If `a`~i~ is of type `A`~i~ `const &`: +* If `a`~i~ is taken as `A`~i~ `const &` or by value: ** If `A`~i~ is of the predicate type `<>`, `a`~i~ is initialized with the caught `std::exception const &`. The handler is dropped if the expression `a`~i~`()` evaluates to `false`. ''' @@ -1611,9 +1655,9 @@ NOTE: The `catch_` template is useful only as argument to a handler function pas Effects: :: -The `catch_` constructor initializes `value` with `ex`. +The `catch_` constructor initializes the `value` reference with `ex`. -The `catch_` template is a predicate function type: `operator()` returns `true` iff the expression `dynamic_cast(&value) != 0` is `true` for at least one of the `Ex...` types. +The `catch_` template is a predicate function type: `operator()` returns `true` iff for at least one of `Ex`~i~ in `Ex...`, the expression `dynamic_cast(&value) != 0` is `true`. .Example: [source,c++] @@ -1624,14 +1668,14 @@ struct exception3: std::exception { }; exception2 x; -catch_ c1(x); +catch_ c1(x); assert(!c1()); catch_ c2(x); assert(c2()); catch_ c3(x); -assert(c2()); +assert(c3()); catch_ c4(x); assert(!c4()); @@ -1700,14 +1744,14 @@ Example: [source,c++] ---- -int compute_answer(); //throws +int compute_answer_throws(); //Call compute_answer, convert exceptions to result -leaf::result catch_exceptions() +leaf::result compute_answer() { return leaf::catch_exceptions< ex_type1, - ex_type2>( compute_answer() ); + ex_type2>( compute_answer_throws() ); } ---- @@ -1719,20 +1763,20 @@ return leaf::handle_some( [ ] -> leaf::result { - LEAF_AUTO(answer, catch_exceptions()); + LEAF_AUTO(answer, compute_answer()); //Use answer .... return { }; }, - [ ]( ex_tppe1 const & ex1 ) + [ ]( ex_type1 const & ex1 ) { //Handle ex_type1 .... return { }; }, - [ ]( ex_tppe2 const & ex2 ) + [ ]( ex_type2 const & ex2 ) { //Handle ex_type2 .... @@ -1806,7 +1850,7 @@ Effects: :: Throws the exception object returned by <>. == Programming Techniques [[technique_preload]] -=== Preloading Errors +=== Preloading Error Objects Consider the following exception type: @@ -1892,10 +1936,12 @@ Now, exceptions thrown by `process_file` may be handled like this: [source,c++] ---- leaf::try_( + [ ] { process_file("example.txt"); }, + [ ]( leaf::catch_, e_file_name const & fn, e_errno const & errn ) { std::cerr << @@ -1961,7 +2007,7 @@ The solution is to use `<>`, so we don't have to remember to includ ---- void read_file(FILE * f) { - auto propagate = leaf::defer( [ ] { return e_errno{errno} } ); + auto propagate = leaf::defer([ ]{ return e_errno{errno}; }); .... size_t nr1=fread(buf1,1,count1,f); @@ -1982,16 +2028,79 @@ void read_file(FILE * f) { [.text-right] <> | `<>` -This works similarly to `preload`, except that capturing of the `errno` is deferred until the destructor of the `propagate` object is called, which calls the passed lambda function to obtain the `errno`. +This works similarly to `preload`, except that the capturing of the `errno` is deferred until the destructor of the `propagate` object is called, which calls the passed lambda function to obtain the `errno`. + +NOTE: This technique works exactly the same way when errors are reported using `leaf::<>` rather than by throwing exceptions. + +''' + +[[technique_augment_in_catch]] +=== Augmenting Exceptions in a `catch` + +What makes `<>` and `<>` useful (see <> and <>) is that they automatically include E-objects with any exception or error reported by a function. + +But what if we need to include some E-object conditionally? When using exception handling, it would be nice to be able to do this in a `catch` statement which selectively augments passing exceptions. + +LEAF supports the following approach, assuming all exceptions derive from `std::exception`: + +[source,c++] +---- +try +{ + .... + function_that_throws(); + .... +} +catch( std::exception const & e ) +{ + if( condition ) + leaf::get_error_id(e).propagate( e_this{....}, e_that{....} ); + throw; +} +---- +[.text-right] +<> | <> + +The reason we need to use <> is that not all exception types derive from `leaf::<>`. If the caught exception has a `leaf::error_id` subobject, `get_error_id` will return that `leaf::error_id` slice. + +But if the caught exception doesn't have a `leaf::error_id` subobject, `get_error_id` returns an unspecified `leaf::error_id`, which is temporarily associated with any and all current exceptions, until successfully handled by <>. While this association is imperfect (because it does not pertain to a specific exception object) it is the best that can be done in this case. + +[NOTE] +-- +The following approach is roughly equivalent: +[source,c++] +---- +try +{ + .... + function_that_throws(); + .... +} +catch( leaf::error_id err ) +{ + if( condition ) + err.propagate( e_this{....}, e_that{....} ); + throw; +} +catch( std::exception const & e ) +{ + if( condition ) + leaf::next_error().propagate( e_this{....}, e_that{....} ); + throw; +} +---- +[.text-right] +<> | <> +-- ''' [[technique_disparate_error_types]] -=== Working with Disparate Error Codes +=== Working with Disparate Error Types Because most libraries define their own mechanism for reporting errors, programmers often need to use multiple incompatible error-reporting interfaces in the same program. This led to the introduction of `boost::system::error_code` which later became `std::error_code`. Each `std::error_code` object is assigned an `error_category`. Libraries that communicate errors in terms of `std::error_code` define their own `error_category`. For libraries that do not, the user can "easily" define a custom `error_category` and still translate domain-specific error codes to `std::error_code`. -But let's take a step back and consider _why_ do we have to express every error in terms of the same static type, `std::error_code`? We need this translation because the {CPP} static type-checking system makes it difficult to write functions that may return error objects of the disparate static types used by different libraries. Outside of this limitation, it would be preferable to be able to write functions that can communicate errors in terms of arbitrary {CPP} types, as needed. +But let's take a step back and consider _why_ did we want to express every error in terms of the same static type, `std::error_code` in the first place? We need this translation because the {CPP} static type-checking system makes it difficult to write functions that may return error objects of the disparate static types used by different libraries. Outside of this limitation, it would be preferable to be able to write functions that can communicate errors in terms of arbitrary {CPP} types, as needed. To drive this point further, consider the real world problem of mixing `boost::system::error_code` and `std::error_code` in the same program. In theory, both systems are designed to be able to express one error code in terms of the other. In practice, describing a _generic_ system for error categorization in terms of another _generic_ system for error categorization is not trivial. @@ -2076,7 +2185,7 @@ int compute_answer_throws() } ---- -We can write a simple wrapper using <>, which calls `compute_answer_throws and switches to `result` for error handling: +We can write a simple wrapper using <>, which calls `compute_answer_throws` and switches to `result` for error handling: [source,c++] ---- @@ -2090,7 +2199,7 @@ leaf::result compute_answer() noexcept } ---- -NOTE: As a demonstration, `compute_answer` specifically converts exceptions of type `error_a` or `error_b`, while it leaves `error_c` to be captured by `std::exception_ptr`. +(As a demonstration, `compute_answer` specifically converts exceptions of type `error_a` or `error_b`, while it leaves `error_c` to be captured by `std::exception_ptr`). Here is a simple function which prints successfully computed answers, forwarding any error (originally reported by throwing an exception) to its caller: @@ -2098,7 +2207,7 @@ Here is a simple function which prints successfully computed answers, forwarding ---- leaf::result print_answer() noexcept { - LEAF_AUTO( answer, compute_answer()); + LEAF_AUTO(answer, compute_answer()); std::cout << "Answer: " << answer << std::endl; return { }; } @@ -2136,48 +2245,14 @@ NOTE: The complete program illustrating this technique is available https://gith ''' -[[technique_augment_in_catch]] -=== Augmenting Exceptions in a `catch` - -What makes `<>` and `<>` useful (see <> and <>) is that they automatically include E-objects with any exception or error reported by a function. - -But what if we need to include some E-object conditionally? When using exception handling, it would be nice to be able to do this in a `catch` statement which selectively augments passing exceptions. - -LEAF supports the following approach, assuming all exceptions derive from `std::exception`: - -[source,c++] ----- -try -{ - .... - function_that_throws(); - .... -} -catch( std::exception const & e ) -{ - if( condition ) - leaf::get_error_id(e).propagate( e_this{....}, e_that{....} ); - throw; -} ----- - -[.text-right] -<> | <> - -The reason we need to use <> is that not all exception types derive from `leaf::<>`. If the caught exception has a `leaf::error_id` subobject, `get_error_id` will return that `leaf::error_id` slice. Also, such exceptions can be intercepted by `catch( error_id e )` if needed. - -But if the caught exception doesn't have a `leaf::error_id` subobject, `get_error_id` returns an unspecified `leaf::error_id`, which is temporarily associated with any and all current exceptions, until successfully handled by <>. While this association is imperfect (because it does not pertain to a specific exception object) it is the best that can be done in this case. - -''' - [[technique_preload_in_c_callbacks]] -=== Using `next_error` in C-callbacks +=== Using `next_error` in (Lua) C-callbacks Communicating information pertaining to a failure detected in a C callback is tricky, because C callbacks are limited to a specific static signature, which may not use {CPP} types. LEAF makes this easy. As an example, we'll write a program that uses Lua and reports a failure from a {CPP} function registered as a C callback, called from a Lua program. The failure will be propagated from {CPP}, through the Lua interpreter (written in C), back to the {CPP} function which called it. -C/{CPP} functions designed to be called from a Lua program must use the following signature: +C/{CPP} functions designed to be invoked from a Lua program must use the following signature: [source,c] ---- @@ -2251,7 +2326,7 @@ int do_work( lua_State * L ) noexcept <1> "Sometimes" `do_work` fails. <2> In case of success, push the result on the Lua stack, return back to Lua. -<3> Associate an `do_work_error_code` object with the *next* `leaf::error_id` object we will definitely return from the `call_lua` function (below)... +<3> Associate an `do_work_error_code` object with the *next* `leaf::error_id` object we will definitely return from the `call_lua` function... <4> ...once control reaches it, after we tell the Lua interpreter to abort the program. Now we'll write the function that calls the Lua interpreter to execute the Lua function `call_do_work`, which in turn calls `do_work`. We'll return `<>`, so that our caller can get the answer in case of success, or an error: @@ -2279,10 +2354,10 @@ leaf::result call_lua( lua_State * L ) <> | <> | <> <1> Ask the Lua interpreter to call the global Lua function `call_do_work`. -<2> Something went wrong with the call, so we'll return a `leaf::<>`. If this is a `do_work` failure, the `do_work_error_code` object prepared in `do_work` will become associated with this `leaf::error_id`. If not, we will still need to communicate that the `lua_pcall` failed with an error code and an error message. +<2> Something went wrong with the call, so we'll return a <>. If this is a `do_work` failure, the `do_work_error_code` object prepared in `do_work` will become associated with this `leaf::error_id`. If not, we will still need to communicate that the `lua_pcall` failed with an error code and an error message. <3> Success! Just return the int answer. -Finally, here is the `main` function which handles all failures: +Finally, here is the `main` function which exercises `call_lua`, each time handling any failure: [source,c++] ---- @@ -2337,7 +2412,7 @@ Remarkably, the Lua interpreter is {CPP} exception-safe, even though it is writt ''' [[technique_transport]] -=== Transporting E-Objects Between Threads +=== Transporting Error Objects Between Threads With LEAF, <> use automatic storage duration, stored inside the scope of <>, <> or <> functions. When using concurrency, we need a mechanism to detach E-objects from a worker thread and transport them to another thread where errors are handled. @@ -2353,7 +2428,7 @@ Let's assume we have a `task` which produces a result but could also fail: leaf::result task(); ---- -To prepare the returned `result` to be sent across the thread boundary, when we launch the asynchronous task, we wrap it in call to <>, specifying which E-types we want captured: +To prepare the returned `result` to be sent across the thread boundary, when we launch the asynchronous task, we wrap it in а call to <>, specifying which E-types we want captured: [source,c++] ---- @@ -2366,7 +2441,7 @@ std::future> launch_task() ---- [.text-right] -<> +<> | <> That's it! Later when we `get` the `std::future`, we can process the returned `result` as if it was generated locally: @@ -2416,7 +2491,7 @@ Let's assume we have a `task` which produces a result but could also throw: task_result task(); ---- -To prepare exceptions thrown by `task` to be sent across the thread boundary, when we launch the asynchronous task, we wrap it in call to <>, specifying which E-types we want captured: +To prepare exceptions thrown by the `task` function to be sent across the thread boundary, when we launch the asynchronous task, we wrap it in а call to <>, specifying which E-types we want captured: [source,c++] ---- @@ -2484,13 +2559,13 @@ Stronger: authors of _error-initiating_ functions may not even reason about what The same reasoning applies to _error-neutral_ functions, but in this case there is the additional problem that the errors they need to communicate, in general, are initiated by functions multiple levels removed from them in the call chain, functions which usually are -- and should be treated as -- implementation details. The _error-neutral_ function should not be coupled with any error object type used by _error-initiating_ functions, for the same reason it should not be coupled with any other aspect of their interface. -Finally, _error-handling_ functions, by definition, have the full context they need to deal with at least some, if not all, failures. In this scope it is an absolute necessity that the author knows exactly what information must be communicated by lower level functions in order to recover from each error condition. Specifically, none of this necessary information can be treated as implementation details; the coupling which is to be avoided by _error-neutral_ functions is unavoidable and even desirable here. +Finally, _error-handling_ functions, by definition, have the full context they need to deal with at least some, if not all, failures. In this scope it is an absolute necessity that the author knows exactly what information must be communicated by lower level functions in order to recover from each error condition. Specifically, none of this necessary information can be treated as implementation details; here, the coupling which is to be avoided by _error-neutral_ functions is unavoidable, in fact desirable. We're now ready to define our Design goals: :: * *Error-initiating* functions should be able to communicate all information available to them that is relevant to the failure being reported. -* *Error-neutral* functions should not interfere or be coupled with error-related information that passes through them. They should be able to augment it with any additional information available to them, which may be relevant to any error they forward to the caller. +* *Error-neutral* functions should not interfere or be coupled with error-related information that passes through them. They should be able to augment it with any additional information available to them that may be relevant to any error they forward to the caller. * *Error-handling* functions should be able to access all the information communicated by _error-initiating_ or _error-neutral_ functions that is needed in their domain in order to deal with failures. The difficulty in reaching these design goals is in that they seem to require that all error objects be allocated dynamically (the Boost Exception library meets these design goals at the cost of dynamic memory allocation). @@ -2498,16 +2573,16 @@ The difficulty in reaching these design goals is in that they seem to require th As it turns out, dynamic memory allocation is not necessary due to the following Fact: :: -* *Error-handling* functions already tell us which of the information _error-initiating_ and _error-neutral_ functions are [.underline]#able# to communicate is [.underline]#actually needed# in order to deal with failures in a particular program domain. Ideally, no resources should be [.line-through]#used# wasted storing or communicating information which is not currently needed to handle errors, even if it is relevant to the failure. +* *Error-handling* functions already "know" which of the information _error-initiating_ and _error-neutral_ functions are [.underline]#able# to communicate is [.underline]#actually needed# in order to deal with failures in a particular program domain. Ideally, no resources should be [.line-through]#used# wasted storing or communicating information which is not currently needed to handle errors, even if it is relevant to the failure. -The error-handling LEAF functions <>, <> and <> implement this idea. Users provide error-handling lambda funcitons which, naturally, take the arguments they need. LEAF examines these arguments and reserves the space needed to hold the objects it needs to pass to a handler. +The LEAF functions <>, <> and <> implement this idea. Users provide error-handling lambda funcitons which, naturally, take the arguments types they need. LEAF simply provides space needed to store these types, using automatic storage duration, before they are passed to a handler. -When this space is created, `thread_local` pointers of the needed E-types are set to point to the corresponding storage within the scope of <>, <> or <>. Later on, _error-initiating_ or _error-neutral_ functions wanting to communicate an error object of type `E` access the corresponding `thread_local` pointer to see if there is currently storage available for this type: +When this space is created, `thread_local` pointers of the required error types are set to point to the corresponding storage within the scope of <>, <> or <>. Later on, _error-initiating_ or _error-neutral_ functions wanting to communicate an error object of type `E` access the corresponding `thread_local` pointer to see if there is currently storage available for this type: -* If the pointer is not null, storage is available and the object is moved into that storage, exactly once -- regardless of how many levels of function calls must unwind before an appropriate _error-handling_ function. +* If the pointer is not null, storage is available and the object is moved into that storage, exactly once -- regardless of how many levels of function calls must unwind before an appropriate _error-handling_ function is reached. * If the pointer is null, storage is not available and the error object is discarded, since no error-handling function makes any use of it -- saving resources. -This almost works, except we need to make sure that _error-handling_ functions are protected from accessing stale error objects stored in an error-handling scope, left there from previous failures, which would be a serious logic error. To this end, each failure is assigned a unique serial number, which is transported inside `leaf::<>` objects that communicate failures. Each of the `E...` objects stored in error-handling scopes is assigned the same unique identifier, permanently associating it with a particular error. +This almost works, except we need to make sure that _error-handling_ functions are protected from being passed stale error objects stored in an error-handling scope, left there from previous failures, which would be a serious logic error. To this end, each failure is assigned a unique <>, which is transported inside `leaf::<>` objects that communicate failures. Each of the `E...` objects stored in error-handling scopes is assigned the same unique identifier, permanently associating it with a particular error. Lastly, in _error-handling_ functions it makes sense to be able to not only recognize individual error conditions, but match specific error-handling code with the complete set of error objects that is required in each case. @@ -2754,9 +2829,9 @@ struct config_error result read_config(); ---- -This looks nice, until we realize what the `config_error` type means for the automatic mapping API we wanted to define: an enum can not represent a struct. It is a fact that we can not assume that all error conditions can be fully specified by an enum; an error handling library must be able to transport arbitrary static types efficiently. +This looks nice, until we realize what the `config_error` type means for the automatic mapping API we wanted to define: an `enum` can not represent a `struct`. It is a fact that we can not assume that all error conditions can be fully specified by an `enum`; an error handling library must be able to transport arbitrary static types efficiently. -Conclusion: :: Transporting error objects statically in return values works great if the failure is handled by the immediate caller of the function that reports it, but most error objects must be communicated across multiple layers of function calls and APIs, which leads to excessive physical coupling between these interfaces. +Conclusion: :: Transporting error objects in return values works great if the failure is handled by the immediate caller of the function that reports it, but most error objects must be communicated across multiple layers of function calls and APIs, which leads to excessive physical coupling between these interfaces. NOTE: While the `leaf::<>` class template does have value-or-error semantics, it does not carry the actual error objects. Instead, they are forwarded directly to the appropriate error-handling scope and their types do not participate in function signatures. @@ -2939,7 +3014,7 @@ The idea is to use `result<>` to communicate failures which can be fully specifi Another way to describe this design is that `result<>` is used when it suffices to return an error object of some static type `EC`, while `outcome<>` can also transport a polymorphic error object, using the pointer type `EP`. -NOTE: In the default configuration of `outcome` the additional information -- or the additional polymorphic object -- is an exception object held by `std::exception_ptr`. This targets the use case when an exception thrown by a lower-level library function needs to be transported through some intermediate contexts that are not exception-safe, to a higher-level context able to handle it. +NOTE: In the default configuration of `outcome` the additional information -- or the additional polymorphic object -- is an exception object held by `std::exception_ptr`. This targets the use case when an exception thrown by a lower-level library function needs to be transported through some intermediate contexts that are not exception-safe, to a higher-level context able to handle it. LEAF supports this case more efficiently, see <>. Similar reasoning drives the design of LEAF as well. The difference is that while both libraries recognize the need to transport "something else" in addition to an "error code", LEAF provides an efficient solution to this problem, while Outcome shifts this burden to the user. diff --git a/doc/synopses/common.adoc b/doc/synopses/common.adoc index 11d468a..53df5eb 100644 --- a/doc/synopses/common.adoc +++ b/doc/synopses/common.adoc @@ -3,37 +3,18 @@ ---- namespace boost { namespace leaf { - struct e_api_function { char const * value; }; - - struct e_file_name { std::string value; }; - - struct e_errno - { - int value; - friend std::ostream & operator<<( std::ostream & os, e_errno const & err ); - }; + struct e_api_function { .... }; + struct e_file_name { .... }; + struct e_errno { .... }; + struct e_at_line { .... }; + struct e_type_info_name { .... }; + struct e_source_location { .... }; namespace windows { - struct e_LastError - { - unsigned value; - }; + struct e_LastError { .... }; } - struct e_at_line { int value; }; - - struct e_type_info_name { char const * value; }; - - struct e_source_location - { - char const * const file; - int const line; - char const * const function; - - friend std::ostream & operator<<( std::ostream & os, e_source_location const & x ); - }; - } } ---- diff --git a/doc/synopses/error.adoc b/doc/synopses/error.adoc index 23db7b5..3daf9b2 100644 --- a/doc/synopses/error.adoc +++ b/doc/synopses/error.adoc @@ -35,4 +35,4 @@ namespace boost { namespace leaf { ---- [.text-right] -<> | <> | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/handle.adoc b/doc/synopses/handle.adoc index e3fca17..5f90629 100644 --- a/doc/synopses/handle.adoc +++ b/doc/synopses/handle.adoc @@ -11,8 +11,11 @@ namespace boost { namespace leaf { decltype(std::declval()()) handle_some( TryBlock && try_block, Handler && ... handler ); + template ::type... V> + struct match; + } } ---- [.text-right] -<> | <> \ No newline at end of file +<> | <> | <> \ No newline at end of file diff --git a/doc/synopses/result.adoc b/doc/synopses/result.adoc index b0b371a..2402785 100644 --- a/doc/synopses/result.adoc +++ b/doc/synopses/result.adoc @@ -9,15 +9,33 @@ namespace boost { namespace leaf { public: result() noexcept; - result( T const & v ); result( T && v ) noexcept; - result( error_id const & e ) noexcept; - result( error_capture const & ec ) noexcept; + result( T const & v ); + result( error_id const & err ) noexcept; + + result( result && r ) noexcept; + result( result const & r ); + + template + result( result && r ) noexcept + + template + result( result const & r ) + + result & operator=( result && r ) noexcept; + result & operator=( result const & r ); + + template + result & operator=( result && r ) noexcept + + template + result & operator=( result const & r ) explicit operator bool() const noexcept; T const & value() const; T & value(); + T const & operator*() const; T & operator*(); @@ -29,12 +47,10 @@ namespace boost { namespace leaf { } } -#define LEAF_ERROR(...) <> - -#define LEAF_AUTO(v,r) auto _r_##v = r; if( !_r_##v ) return _r_##v.error(); auto & v = *_r_##v - -#define LEAF_CHECK(r) {auto _r = r; if( !_r ) return _r.error();} +#define LEAF_ERROR(...) .... +#define LEAF_AUTO(v,r) .... +#define LEAF_CHECK(r) .... ---- [.text-right] -<> | <> | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/throw.adoc b/doc/synopses/throw.adoc index 6c93e93..4e96d32 100644 --- a/doc/synopses/throw.adoc +++ b/doc/synopses/throw.adoc @@ -8,9 +8,9 @@ namespace boost { namespace leaf { } } -#define LEAF_EXCEPTION(...) <> +#define LEAF_EXCEPTION(...) .... -#define LEAF_THROW(...) throw <> +#define LEAF_THROW(...) .... ---- [.text-right] diff --git a/doc/synopses/try.adoc b/doc/synopses/try.adoc index a414c22..9943451 100644 --- a/doc/synopses/try.adoc +++ b/doc/synopses/try.adoc @@ -6,10 +6,16 @@ namespace boost { namespace leaf { template decltype(std::declval()()) try_( TryBlock && try_block, Handler && ... handler ); + template ::type... V> + struct match; + + template + struct catch_; + error_id get_error_id( std::exception const & ex ) noexcept; } } ---- [.text-right] -<> | <> +<> | <> | <> | <> diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index 485441c..e776740 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -90,7 +90,7 @@ leaf::result call_lua( lua_State * L ) lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); if( int err=lua_pcall(L,0,1,0) ) { - // Something went wrong with the call, so we'll return a leaf::new_error. + // Something went wrong with the call, so we'll return a new_error. // If this is a do_work failure, the do_work_error_code object prepared in // do_work will become associated with this leaf::error_id value. If not, // we will still need to communicate that the lua_pcall failed with an diff --git a/example/print_file_eh.cpp b/example/print_file_eh.cpp index 56b5882..f08d09f 100644 --- a/example/print_file_eh.cpp +++ b/example/print_file_eh.cpp @@ -48,7 +48,7 @@ void file_read( FILE & f, void * buf, int size ); int main( int argc, char const * argv[ ] ) { // Configure std::cout to throw on error. - std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); + std::cout.exceptions(std::ostream::failbit | std::ostream::badbit); return leaf::try_( [&] diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index 2533984..99882b4 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -109,18 +109,12 @@ namespace boost { namespace leaf { destroy(); } - result( result const & x ) - { - copy_from(x); - } - result( result && x ) noexcept { move_from(std::move(x)); } - template - result( result const & x ) + result( result const & x ) { copy_from(x); } @@ -131,20 +125,26 @@ namespace boost { namespace leaf { move_from(std::move(x)); } + template + result( result const & x ) + { + copy_from(x); + } + result(): value_(T()), which_(leaf_detail::result_variant::value) { } - result( T const & v ): - value_(v), + result( T && v ) noexcept: + value_(std::move(v)), which_(leaf_detail::result_variant::value) { } - result( T && v ) noexcept: - value_(std::move(v)), + result( T const & v ): + value_(v), which_(leaf_detail::result_variant::value) { } @@ -157,6 +157,13 @@ namespace boost { namespace leaf { result( std::shared_ptr && ) noexcept; + result & operator=( result && x ) noexcept + { + destroy(); + move_from(std::move(x)); + return *this; + } + result & operator=( result const & x ) { destroy(); @@ -164,13 +171,22 @@ namespace boost { namespace leaf { return *this; } - result & operator=( result && x ) noexcept + template + result & operator=( result && x ) noexcept { destroy(); move_from(std::move(x)); return *this; } + template + result & operator=( result const & x ) + { + destroy(); + copy_from(x); + return *this; + } + explicit operator bool() const noexcept { return which_==leaf_detail::result_variant::value;