diff --git a/example/capture_in_exception.cpp b/example/dynamic_capture_eh.cpp similarity index 61% rename from example/capture_in_exception.cpp rename to example/dynamic_capture_eh.cpp index a73d30d9..7b34a833 100644 --- a/example/capture_in_exception.cpp +++ b/example/dynamic_capture_eh.cpp @@ -44,30 +44,12 @@ int main() { int const task_count = 42; - // The error_handlers are used in this thread (see leaf::try_catch below). - // The arguments passed to individual lambdas are transported from the - // worker thread to the main thread automatically. - auto error_handlers = std::make_tuple( - []( 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::diagnostic_info const & unmatched ) - { - std::cerr << - "Unknown failure detected" << std::endl << - "Cryptic diagnostic information follows" << std::endl << - unmatched; - } ); - // Container to collect the generated std::future objects. - std::vector> fut; + std::vector>> fut; // Launch the tasks, but rather than launching the task function directly, - // we launch a wrapper function which calls leaf::capture, passing a context - // object that will hold the error objects reported from the task in case it - // throws. The error types the context is able to hold statically are - // automatically deduced from the type of the error_handlers tuple. + // we use leaf::try_catch in compbination with leaf::dynamic_capture: + // in case of a failure, the returned leaf::result<> will capture all error objects. std::generate_n( std::back_inserter(fut), task_count, [&] { @@ -75,7 +57,15 @@ int main() std::launch::async, [&] { - return leaf::capture(leaf::make_shared_context(error_handlers), &task); + return leaf::try_catch( + [&]() -> leaf::result + { + return task(); + }, + []( leaf::dynamic_capture const & cap ) -> leaf::result + { + return cap; + } ); } ); } ); @@ -87,12 +77,22 @@ int main() leaf::try_catch( [&] { - task_result r = f.get(); + task_result r = f.get().value(); // Success! Use r to access task_result. std::cout << "Success!" << std::endl; (void) r; // Presumably we'll somehow use the task_result. }, - error_handlers ); + []( 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::diagnostic_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + } ); } } diff --git a/example/capture_in_result.cpp b/example/dynamic_capture_result.cpp similarity index 67% rename from example/capture_in_result.cpp rename to example/dynamic_capture_result.cpp index c8c9ad7f..bf3374cb 100644 --- a/example/capture_in_result.cpp +++ b/example/dynamic_capture_result.cpp @@ -44,30 +44,12 @@ int main() { int const task_count = 42; - // The error_handlers are used in this thread (see leaf::try_handle_all - // below). The arguments passed to individual lambdas are transported from - // the worker thread to the main thread automatically. - auto error_handlers = std::make_tuple( - []( 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::diagnostic_info const & unmatched ) - { - std::cerr << - "Unknown failure detected" << std::endl << - "Cryptic diagnostic information follows" << std::endl << - unmatched; - } ); - // Container to collect the generated std::future objects. std::vector>> fut; // Launch the tasks, but rather than launching the task function directly, - // we launch a wrapper function which calls leaf::capture, passing a context - // object that will hold the error objects reported from the task in case of - // an error. The error types the context is able to hold statically are - // automatically deduced from the type of the error_handlers tuple. + // we use leaf::try_handle_some in compbination with leaf::dynamic_capture: + // in case of a failure, the returned leaf::result<> will capture all error objects. std::generate_n( std::back_inserter(fut), task_count, [&] { @@ -75,7 +57,15 @@ int main() std::launch::async, [&] { - return leaf::capture(leaf::make_shared_context(error_handlers), &task); + return leaf::try_handle_some( + [&]() -> leaf::result + { + return task(); + }, + []( leaf::dynamic_capture const & cap ) -> leaf::result + { + return cap; + } ); } ); } ); @@ -87,14 +77,24 @@ int main() leaf::try_handle_all( [&]() -> leaf::result { - BOOST_LEAF_AUTO(r,f.get()); + BOOST_LEAF_AUTO(r, f.get()); // Success! Use r to access task_result. std::cout << "Success!" << std::endl; (void) r; // Presumably we'll somehow use the task_result. return { }; }, - error_handlers ); + []( 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::diagnostic_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + } ); } } diff --git a/example/print_file/print_file_outcome_result.cpp b/example/print_file/print_file_outcome_result.cpp index 5c642699..bd152c47 100644 --- a/example/print_file/print_file_outcome_result.cpp +++ b/example/print_file/print_file_outcome_result.cpp @@ -79,7 +79,7 @@ int main( int argc, char const * argv[] ) std::cout << buffer; std::cout.flush(); if( std::cout.fail() ) - return leaf::new_error(output_error, leaf::e_errno{errno}).to_error_code(); + return leaf::new_error(output_error, leaf::e_errno{errno}); return 0; }, @@ -164,7 +164,7 @@ result parse_command_line( int argc, char const * argv[] ) if( argc==2 ) return argv[1]; else - return leaf::new_error(bad_command_line).to_error_code(); + return leaf::new_error(bad_command_line); } @@ -174,7 +174,7 @@ 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(open_error, leaf::e_errno{errno}).to_error_code(); + return leaf::new_error(open_error, leaf::e_errno{errno}); } @@ -184,14 +184,14 @@ result file_size( FILE & f ) auto load = leaf::on_error([] { return leaf::e_errno{errno}; }); if( fseek(&f, 0, SEEK_END) ) - return leaf::new_error(size_error).to_error_code(); + return leaf::new_error(size_error); long s = ftell(&f); if( s==-1L ) - return leaf::new_error(size_error).to_error_code(); + return leaf::new_error(size_error); if( fseek(&f,0,SEEK_SET) ) - return leaf::new_error(size_error).to_error_code(); + return leaf::new_error(size_error); return std::size_t(s); } @@ -203,10 +203,10 @@ result file_read( FILE & f, void * buf, std::size_t size ) std::size_t n = fread(buf, 1, size, &f); if( ferror(&f) ) - return leaf::new_error(read_error, leaf::e_errno{errno}).to_error_code(); + return leaf::new_error(read_error, leaf::e_errno{errno}); if( n!=size ) - return leaf::new_error(eof_error).to_error_code(); + return leaf::new_error(eof_error); return outcome::success(); } diff --git a/example/readme.md b/example/readme.md index 3e98584a..323d9e4b 100644 --- a/example/readme.md +++ b/example/readme.md @@ -2,8 +2,8 @@ * [print_file](./print_file): The complete example from the [Five Minute Introduction](https://boostorg.github.io/leaf/#introduction). This directory contains several versions of the same program, each using a different error handling style. -* [capture_in_result.cpp](https://github.com/boostorg/leaf/blob/master/example/capture_in_result.cpp?ts=4): Shows how to transport error objects between threads in a `leaf::result` object. -* [capture_in_exception.cpp](https://github.com/boostorg/leaf/blob/master/example/capture_in_exception.cpp?ts=4): Shows how to transport error objects between threads in an exception object. +* [dynamic_capture_result.cpp](https://github.com/boostorg/leaf/blob/master/example/dynamic_capture_result.cpp?ts=4): Shows how to transport error objects between threads in a `leaf::result` object without using exception handling. +* [dynamic_capture_eh.cpp](https://github.com/boostorg/leaf/blob/master/example/dynamic_capture_eh.cpp?ts=4): Shows how to transport error objects between threads in an a `leaf::result` object using exception handling. * [lua_callback_result.cpp](https://github.com/boostorg/leaf/blob/master/example/lua_callback_result.cpp?ts=4): Transporting arbitrary error objects through an uncooperative C API. * [lua_callback_eh.cpp](https://github.com/boostorg/leaf/blob/master/example/lua_callback_eh.cpp?ts=4): Transporting arbitrary error objects through an uncooperative exception-safe API. * [exception_to_result.cpp](https://github.com/boostorg/leaf/blob/master/example/exception_to_result.cpp?ts=4): Demonstrates how to transport exceptions through a `noexcept` layer in the program. diff --git a/include/boost/leaf/capture.hpp b/include/boost/leaf/capture.hpp deleted file mode 100644 index 9235a824..00000000 --- a/include/boost/leaf/capture.hpp +++ /dev/null @@ -1,239 +0,0 @@ -#ifndef BOOST_LEAF_CAPTURE_HPP_INCLUDED -#define BOOST_LEAF_CAPTURE_HPP_INCLUDED - -// Copyright 2018-2023 Emil Dotchevski and Reverge Studios, Inc. - -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include - -#if BOOST_LEAF_CFG_CAPTURE - -namespace boost { namespace leaf { - -namespace leaf_detail -{ - template ::value> - struct is_result_tag; - - template - struct is_result_tag - { - }; - - template - struct is_result_tag - { - }; -} - -#ifdef BOOST_LEAF_NO_EXCEPTIONS - -namespace leaf_detail -{ - template - inline - decltype(std::declval()(std::forward(std::declval())...)) - capture_impl(is_result_tag, ContextPtr && ctx, F && f, A... a) noexcept - { - auto active_context = activate_context(*ctx); - return std::forward(f)(std::forward(a)...); - } - - template - inline - decltype(std::declval()(std::forward(std::declval())...)) - capture_impl(is_result_tag, ContextPtr && ctx, F && f, A... a) noexcept - { - auto active_context = activate_context(*ctx); - if( auto r = std::forward(f)(std::forward(a)...) ) - return r; - else - { - ctx->captured_id_ = r.error(); - return std::forward(ctx); - } - } - - template - inline - decltype(std::declval().get()) - future_get_impl(is_result_tag, Future & fut) noexcept - { - return fut.get(); - } - - template - inline - decltype(std::declval().get()) - future_get_impl(is_result_tag, Future & fut) noexcept - { - if( auto r = fut.get() ) - return r; - else - return error_id(r.error()); // unloads - } -} - -#else - -namespace leaf_detail -{ - class capturing_exception: - public std::exception - { - std::exception_ptr ex_; - context_ptr ctx_; - - public: - - template - capturing_exception(std::exception_ptr && ex, ContextPtr && ctx) noexcept: - ex_(std::move(ex)), - ctx_(std::forward(ctx)) - { - BOOST_LEAF_ASSERT(ex_); - BOOST_LEAF_ASSERT(ctx_); - BOOST_LEAF_ASSERT(ctx_->captured_id_); - } - - [[noreturn]] void unload_and_rethrow_original_exception() const - { - BOOST_LEAF_ASSERT(ctx_->captured_id_); - tls::write_uint(unsigned(ctx_->captured_id_.value())); - ctx_->propagate(ctx_->captured_id_); - std::rethrow_exception(ex_); - } - }; - - template - inline - decltype(std::declval()(std::forward(std::declval())...)) - capture_impl(is_result_tag, ContextPtr && ctx, F && f, A... a) - { - auto active_context = activate_context(*ctx); - error_monitor cur_err; - try - { - return std::forward(f)(std::forward(a)...); - } - catch( capturing_exception const & ) - { - throw; - } - catch( exception_base const & e ) - { - ctx->captured_id_ = e.get_error_id(); - leaf_detail::throw_exception_impl( capturing_exception(std::current_exception(), std::forward(ctx)) ); - } - catch(...) - { - ctx->captured_id_ = cur_err.assigned_error_id(); - leaf_detail::throw_exception_impl( capturing_exception(std::current_exception(), std::forward(ctx)) ); - } - } - - template - inline - decltype(std::declval()(std::forward(std::declval())...)) - capture_impl(is_result_tag, ContextPtr && ctx, F && f, A... a) - { - auto active_context = activate_context(*ctx); - error_monitor cur_err; - try - { - if( auto && r = std::forward(f)(std::forward(a)...) ) - return std::move(r); - else - { - ctx->captured_id_ = r.error(); - return std::forward(ctx); - } - } - catch( capturing_exception const & ) - { - throw; - } - catch( exception_base const & e ) - { - ctx->captured_id_ = e.get_error_id(); - leaf_detail::throw_exception_impl( capturing_exception(std::current_exception(), std::forward(ctx)) ); - } - catch(...) - { - ctx->captured_id_ = cur_err.assigned_error_id(); - leaf_detail::throw_exception_impl( capturing_exception(std::current_exception(), std::forward(ctx)) ); - } - } - - template - inline - decltype(std::declval().get()) - future_get_impl(is_result_tag, Future & fut ) - { - try - { - return fut.get(); - } - catch( capturing_exception const & cap ) - { - cap.unload_and_rethrow_original_exception(); - } - } - - template - inline - decltype(std::declval().get()) - future_get_impl(is_result_tag, Future & fut ) - { - try - { - if( auto r = fut.get() ) - return r; - else - return error_id(r.error()); // unloads - } - catch( capturing_exception const & cap ) - { - cap.unload_and_rethrow_original_exception(); - } - } -} - -#endif - -template -inline -decltype(std::declval()(std::forward(std::declval())...)) -capture(context_ptr const & ctx, F && f, A... a) -{ - using namespace leaf_detail; - return capture_impl(is_result_tag()(std::forward(std::declval())...))>(), ctx, std::forward(f), std::forward(a)...); -} - -template -inline -decltype(std::declval()(std::forward(std::declval())...)) -capture(context_ptr && ctx, F && f, A... a) -{ - using namespace leaf_detail; - return capture_impl(is_result_tag()(std::forward(std::declval())...))>(), std::move(ctx), std::forward(f), std::forward(a)...); -} - -template -inline -decltype(std::declval().get()) -future_get( Future & fut ) -{ - using namespace leaf_detail; - return future_get_impl(is_result_tag().get())>(), fut); -} - -} } - -#endif - -#endif diff --git a/include/boost/leaf/config/tls_array.hpp b/include/boost/leaf/config/tls_array.hpp index a78addc8..46243f27 100644 --- a/include/boost/leaf/config/tls_array.hpp +++ b/include/boost/leaf/config/tls_array.hpp @@ -65,15 +65,21 @@ namespace tls { static int c_; - public: - - static BOOST_LEAF_CFG_TLS_INDEX_TYPE next() + static BOOST_LEAF_CFG_TLS_INDEX_TYPE next_() noexcept { int idx = ++c_; BOOST_LEAF_ASSERT(idx > (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); BOOST_LEAF_ASSERT(idx < (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); return idx; } + + public: + + template + static BOOST_LEAF_CFG_TLS_INDEX_TYPE next() noexcept + { + return next_(); // Set breakpoint here to monitor TLS intex allocation for T. + } }; template @@ -95,7 +101,7 @@ namespace tls BOOST_LEAF_CFG_TLS_INDEX_TYPE tls_index::idx = BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX; template - BOOST_LEAF_CFG_TLS_INDEX_TYPE const alloc_tls_index::idx = tls_index::idx = index_counter<>::next(); + BOOST_LEAF_CFG_TLS_INDEX_TYPE const alloc_tls_index::idx = tls_index::idx = index_counter<>::next(); //////////////////////////////////////// diff --git a/include/boost/leaf/context.hpp b/include/boost/leaf/context.hpp index 6a213be4..d1704f51 100644 --- a/include/boost/leaf/context.hpp +++ b/include/boost/leaf/context.hpp @@ -17,7 +17,9 @@ namespace boost { namespace leaf { class error_info; class diagnostic_info; +#if BOOST_LEAF_CFG_DIAGNOSTICS class verbose_diagnostic_info; +#endif template struct is_predicate: std::false_type @@ -58,7 +60,9 @@ namespace leaf_detail static_assert(!is_predicate::value, "Handlers must take predicate arguments by value"); static_assert(!std::is_same::value, "Handlers must take leaf::error_info arguments by const &"); static_assert(!std::is_same::value, "Handlers must take leaf::diagnostic_info arguments by const &"); +#if BOOST_LEAF_CFG_DIAGNOSTICS static_assert(!std::is_same::value, "Handlers must take leaf::verbose_diagnostic_info arguments by const &"); +#endif }; template @@ -163,20 +167,21 @@ namespace leaf_detail tuple_for_each::deactivate(tup); } - BOOST_LEAF_CONSTEXPR static void propagate( Tuple & tup, int err_id ) noexcept + BOOST_LEAF_CONSTEXPR static void unload( Tuple & tup, int err_id ) noexcept { static_assert(!std::is_same(tup))>::type>::value, "Bug in LEAF: context type deduction"); + BOOST_LEAF_ASSERT(err_id != 0); auto & sl = std::get(tup); - sl.propagate(err_id); - tuple_for_each::propagate(tup, err_id); + sl.unload(err_id); + tuple_for_each::unload(tup, err_id); } template - static void print( std::basic_ostream & os, void const * tup, int key_to_print ) + static void print( std::basic_ostream & os, void const * tup, int err_id_to_print ) { BOOST_LEAF_ASSERT(tup != nullptr); - tuple_for_each::print(os, tup, key_to_print); - std::get(*static_cast(tup)).print(os, key_to_print); + tuple_for_each::print(os, tup, err_id_to_print); + std::get(*static_cast(tup)).print(os, err_id_to_print); } }; @@ -185,7 +190,7 @@ namespace leaf_detail { BOOST_LEAF_CONSTEXPR static void activate( Tuple & ) noexcept { } BOOST_LEAF_CONSTEXPR static void deactivate( Tuple & ) noexcept { } - BOOST_LEAF_CONSTEXPR static void propagate( Tuple &, int ) noexcept { } + BOOST_LEAF_CONSTEXPR static void unload( Tuple &, int ) noexcept { } template BOOST_LEAF_CONSTEXPR static void print( std::basic_ostream &, void const *, int ) { } }; @@ -193,37 +198,6 @@ namespace leaf_detail //////////////////////////////////////////// -#if BOOST_LEAF_CFG_DIAGNOSTICS - -namespace leaf_detail -{ - template struct requires_unexpected { constexpr static bool value = false; }; - template struct requires_unexpected { constexpr static bool value = requires_unexpected::value; }; - template struct requires_unexpected { constexpr static bool value = requires_unexpected::value; }; - template struct requires_unexpected { constexpr static bool value = requires_unexpected::value; }; - template <> struct requires_unexpected { constexpr static bool value = true; }; - template <> struct requires_unexpected { constexpr static bool value = true; }; - - template - struct unexpected_requested; - - template