diff --git a/include/boost/leaf/context.hpp b/include/boost/leaf/context.hpp index 5ae3bba..a6095a1 100644 --- a/include/boost/leaf/context.hpp +++ b/include/boost/leaf/context.hpp @@ -50,10 +50,10 @@ namespace detail template BOOST_LEAF_CONSTEXPR static error_type * check( Tup &, error_info const & ) noexcept; - template - BOOST_LEAF_CONSTEXPR static E get( Tup & tup, error_info const & ei ) noexcept + template + BOOST_LEAF_CONSTEXPR static E get( Context & ctx, error_info const & ei ) noexcept { - return *check(tup, ei); + return *check(ctx.tup(), ei); } static_assert(!is_predicate::value, "Handlers must take predicate arguments by value"); @@ -75,10 +75,10 @@ namespace detail return e && Pred::evaluate(*e); } - template - BOOST_LEAF_CONSTEXPR static Pred get( Tup const & tup, error_info const & ei ) noexcept + template + BOOST_LEAF_CONSTEXPR static Pred get( Context const & ctx, error_info const & ei ) noexcept { - return Pred{*base::check(tup, ei)}; + return Pred{*base::check(ctx.tup(), ei)}; } }; @@ -119,10 +119,10 @@ namespace detail template struct handler_argument_traits: handler_argument_always_available::type> { - template - BOOST_LEAF_CONSTEXPR static E * get( Tup & tup, error_info const & ei) noexcept + template + BOOST_LEAF_CONSTEXPR static E * get( Context & ctx, error_info const & ei) noexcept { - return handler_argument_traits_defaults::check(tup, ei); + return handler_argument_traits_defaults::check(ctx.tup(), ei); } }; @@ -204,7 +204,8 @@ namespace detail tuple_for_each::unload(tup, err_id); } - static void serialize_to(encoder & e, void const * tup, error_id id) + template + static void serialize_to(Encoder & e, void const * tup, error_id id) { BOOST_LEAF_ASSERT(tup != nullptr); tuple_for_each::serialize_to(e, tup, id); @@ -221,10 +222,12 @@ namespace detail BOOST_LEAF_CONSTEXPR static void serialize_to(encoder &, void const *, error_id) { } }; - template - void serialize_tuple_contents_to(encoder & e, void const * tup, error_id id) + class context_base; + + template + void serialize_context_to(encoder & e, context_base const & ctx, error_id id) { - tuple_for_each::value, Tup>::serialize_to(e, tup, id); + static_cast(ctx).serialize_to(e, id); } } // namespace detail @@ -265,8 +268,57 @@ namespace detail //////////////////////////////////////// +namespace detail +{ + // The context_base type is able to walk up the parent error handling + // scopes to collect relevant error objects, so they can be serialized or + // printed by diagnostic_details. + // + // This introduces a bit of overhead which is semantically similar to the + // rest of the overhead introduced by BOOST_LEAF_CFG_CAPTURE=1, namely + // allocating unhandled error objects dynamically if diagnostic_details is + // handled. + class context_base + { +#if BOOST_LEAF_CFG_CAPTURE + context_base( context_base const & ) = delete; + context_base & operator=( context_base const & ) = delete; + + protected: + + context_base * parent_ = nullptr; + + context_base() + { + tls::reserve_ptr(); + } + + context_base( context_base && ) noexcept = default; + ~context_base() noexcept = default; + + void link() noexcept + { + parent_ = tls::read_ptr(); + tls::write_ptr(this); + } + + void unlink() noexcept + { + BOOST_LEAF_ASSERT(tls::read_ptr() == this); + tls::write_ptr(parent_); + } + + public: + + virtual void serialize_to_( encoder &, error_id ) const = 0; +#endif + }; // class context_base +} // namespace detail + + template -class context +class context final: + public detail::context_base { context( context const & ) = delete; context & operator=( context const & ) = delete; @@ -305,9 +357,29 @@ class context } }; +#if BOOST_LEAF_CFG_CAPTURE + void serialize_to_( detail::encoder & e, error_id id ) const override + { + detail::tuple_for_each::value, Tup>::serialize_to(e, &tup_, id); + if( parent_ ) + parent_->serialize_to_(e, id); + } +#endif + public: + template + void serialize_to( Encoder & e, error_id id ) const + { + detail::tuple_for_each::value, Tup>::serialize_to(e, &tup_, id); +#if BOOST_LEAF_CFG_CAPTURE + if( parent_ ) + parent_->serialize_to_(e, id); +#endif + } + BOOST_LEAF_CONSTEXPR context( context && x ) noexcept: + context_base(std::move(x)), tup_(std::move(x.tup_)), is_active_(false) { @@ -338,10 +410,13 @@ public: { using namespace detail; BOOST_LEAF_ASSERT(!is_active()); - tuple_for_each::value,Tup>::activate(tup_); #if !defined(BOOST_LEAF_NO_THREADS) && !defined(NDEBUG) thread_id_ = std::this_thread::get_id(); #endif +#if BOOST_LEAF_CFG_CAPTURE + context_base::link(); +#endif + tuple_for_each::value,Tup>::activate(tup_); is_active_ = true; } @@ -349,12 +424,15 @@ public: { using namespace detail; BOOST_LEAF_ASSERT(is_active()); - is_active_ = false; #if !defined(BOOST_LEAF_NO_THREADS) && !defined(NDEBUG) BOOST_LEAF_ASSERT(std::this_thread::get_id() == thread_id_); thread_id_ = std::thread::id(); #endif + is_active_ = false; tuple_for_each::value,Tup>::deactivate(tup_); +#if BOOST_LEAF_CFG_CAPTURE + context_base::unlink(); +#endif } BOOST_LEAF_CONSTEXPR void unload(error_id id) noexcept(!BOOST_LEAF_CFG_CAPTURE) diff --git a/include/boost/leaf/detail/capture_list.hpp b/include/boost/leaf/detail/capture_list.hpp index 3c42418..0447a68 100644 --- a/include/boost/leaf/detail/capture_list.hpp +++ b/include/boost/leaf/detail/capture_list.hpp @@ -29,7 +29,7 @@ namespace detail friend class capture_list; virtual void unload( int err_id ) = 0; - virtual void serialize_to(encoder &, error_id const &) const = 0; + virtual void serialize_to_(encoder &, error_id const &) const = 0; protected: @@ -97,7 +97,7 @@ namespace detail for_each( [&e, &id]( node const & n ) { - n.serialize_to(e, id); + n.serialize_to_(e, id); } ); } } diff --git a/include/boost/leaf/diagnostics.hpp b/include/boost/leaf/diagnostics.hpp index 04758f5..a0f6ada 100644 --- a/include/boost/leaf/diagnostics.hpp +++ b/include/boost/leaf/diagnostics.hpp @@ -12,18 +12,18 @@ namespace boost { namespace leaf { class diagnostic_info: public error_info { - void const * tup_; - void (*serialize_tuple_contents_to_)(detail::encoder &, void const *, error_id); + detail::context_base const & ctx_; + void (*serialize_ctx_to_)(detail::encoder &, detail::context_base const &, error_id); protected: diagnostic_info( diagnostic_info const & ) noexcept = default; - template - BOOST_LEAF_CONSTEXPR diagnostic_info( error_info const & ei, Tup const & tup ) noexcept: + template + BOOST_LEAF_CONSTEXPR diagnostic_info( error_info const & ei, Context const & ctx ) noexcept: error_info(ei), - tup_(&tup), - serialize_tuple_contents_to_(&detail::serialize_tuple_contents_to) + ctx_(ctx), + serialize_ctx_to_(&detail::serialize_context_to) { } @@ -31,7 +31,7 @@ protected: void serialize_to_(Encoder & e) const { static_assert(std::is_base_of::value, "Encoder must derive from detail::encoder"); - serialize_tuple_contents_to_(e, tup_, error()); + serialize_ctx_to_(e, ctx_, error()); } public: @@ -61,9 +61,9 @@ namespace detail { struct diagnostic_info_: diagnostic_info { - template - BOOST_LEAF_CONSTEXPR diagnostic_info_( error_info const & ei, Tup const & tup ) noexcept: - diagnostic_info(ei, tup) + template + BOOST_LEAF_CONSTEXPR diagnostic_info_( error_info const & ei, Context const & ctx ) noexcept: + diagnostic_info(ei, ctx) { } }; @@ -71,10 +71,10 @@ namespace detail template <> struct handler_argument_traits: handler_argument_always_available { - template - BOOST_LEAF_CONSTEXPR static diagnostic_info_ get( Tup const & tup, error_info const & ei ) noexcept + template + BOOST_LEAF_CONSTEXPR static diagnostic_info_ get( Context const & ctx, error_info const & ei ) noexcept { - return diagnostic_info_(ei, tup); + return diagnostic_info_(ei, ctx); } }; } @@ -91,9 +91,9 @@ protected: diagnostic_details( diagnostic_details const & ) noexcept = default; - template - BOOST_LEAF_CONSTEXPR diagnostic_details( error_info const & ei, Tup const & tup, detail::dynamic_allocator const * da ) noexcept: - diagnostic_info(ei, tup), + template + BOOST_LEAF_CONSTEXPR diagnostic_details( error_info const & ei, Context const & ctx, detail::dynamic_allocator const * da ) noexcept: + diagnostic_info(ei, ctx), da_(da) { } @@ -136,9 +136,9 @@ namespace detail { struct diagnostic_details_: diagnostic_details { - template - BOOST_LEAF_CONSTEXPR diagnostic_details_( error_info const & ei, Tup const & tup, dynamic_allocator const * da ) noexcept: - diagnostic_details(ei, tup, da) + template + BOOST_LEAF_CONSTEXPR diagnostic_details_( error_info const & ei, Context const & ctx, dynamic_allocator const * da ) noexcept: + diagnostic_details(ei, ctx, da) { } }; @@ -146,11 +146,11 @@ namespace detail template <> struct handler_argument_traits: handler_argument_always_available { - template - BOOST_LEAF_CONSTEXPR static diagnostic_details_ get( Tup const & tup, error_info const & ei ) noexcept + template + BOOST_LEAF_CONSTEXPR static diagnostic_details_ get( Context const & ctx, error_info const & ei ) noexcept { - slot const * da = find_in_tuple>(tup); - return diagnostic_details_(ei, tup, da ? &da->get() : nullptr ); + slot const * da = find_in_tuple>(ctx.tup()); + return diagnostic_details_(ei, ctx, da ? &da->get() : nullptr ); } }; } @@ -163,9 +163,9 @@ protected: diagnostic_details( diagnostic_details const & ) noexcept = default; - template - BOOST_LEAF_CONSTEXPR diagnostic_details( error_info const & ei, Tup const & tup ) noexcept: - diagnostic_info(ei, tup) + template + BOOST_LEAF_CONSTEXPR diagnostic_details( error_info const & ei, Context const & ctx ) noexcept: + diagnostic_info(ei, ctx) { } @@ -197,9 +197,9 @@ namespace detail { struct diagnostic_details_: diagnostic_details { - template - BOOST_LEAF_CONSTEXPR diagnostic_details_( error_info const & ei, Tup const & tup ) noexcept: - diagnostic_details(ei, tup) + template + BOOST_LEAF_CONSTEXPR diagnostic_details_( error_info const & ei, Context const & ctx ) noexcept: + diagnostic_details(ei, ctx) { } }; @@ -207,10 +207,10 @@ namespace detail template <> struct handler_argument_traits: handler_argument_always_available { - template - BOOST_LEAF_CONSTEXPR static diagnostic_details_ get( Tup const & tup, error_info const & ei ) noexcept + template + BOOST_LEAF_CONSTEXPR static diagnostic_details_ get( Context const & ctx, error_info const & ei ) noexcept { - return diagnostic_details_(ei, tup); + return diagnostic_details_(ei, ctx); } }; } diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 360bc9b..e0348f3 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -270,7 +270,7 @@ namespace detail { impl::unload(err_id); } - void serialize_to(encoder & e, error_id const & id) const override + void serialize_to_(encoder & e, error_id const & id) const override { impl::serialize_to(e, id); } @@ -305,7 +305,7 @@ namespace detail { std::rethrow_exception(ex_); } - void serialize_to(encoder &, error_id const &) const override + void serialize_to_(encoder &, error_id const &) const override { } std::exception_ptr const ex_; diff --git a/include/boost/leaf/handle_errors.hpp b/include/boost/leaf/handle_errors.hpp index b4dad74..3938786 100644 --- a/include/boost/leaf/handle_errors.hpp +++ b/include/boost/leaf/handle_errors.hpp @@ -377,10 +377,10 @@ namespace detail template ::value, class FReturnType = fn_return_type> struct handler_caller { - template - BOOST_LEAF_CONSTEXPR static R call( Tup & tup, error_info const & ei, F && f, leaf_detail_mp11::mp_list ) + template + BOOST_LEAF_CONSTEXPR static R call( Context & ctx, error_info const & ei, F && f, leaf_detail_mp11::mp_list ) { - return std::forward(f)( handler_argument_traits::get(tup, ei)... ); + return std::forward(f)( handler_argument_traits::get(ctx, ei)... ); } }; @@ -389,10 +389,10 @@ namespace detail { using R = Result; - template - BOOST_LEAF_CONSTEXPR static R call( Tup & tup, error_info const & ei, F && f, leaf_detail_mp11::mp_list ) + template + BOOST_LEAF_CONSTEXPR static R call( Context & ctx, error_info const & ei, F && f, leaf_detail_mp11::mp_list ) { - std::forward(f)( handler_argument_traits::get(tup, ei)... ); + std::forward(f)( handler_argument_traits::get(ctx, ei)... ); return { }; } }; @@ -406,61 +406,61 @@ namespace detail template struct is_tuple &>: std::true_type { }; - template + template BOOST_LEAF_CONSTEXPR inline typename std::enable_if::type>::value, R>::type - handle_error_( Tup & tup, error_info const & ei, H && h ) + handle_error_( Context & ctx, error_info const & ei, H && h ) { static_assert( handler_matches_any_error>::value, "The last handler passed to handle_all must match any error." ); - return handler_caller::call( tup, ei, std::forward(h), fn_mp_args{ } ); + return handler_caller::call( ctx, ei, std::forward(h), fn_mp_args{ } ); } - template + template BOOST_LEAF_CONSTEXPR inline typename std::enable_if::type>::value, R>::type - handle_error_( Tup & tup, error_info const & ei, Car && car, Cdr && ... cdr ) + handle_error_( Context & ctx, error_info const & ei, Car && car, Cdr && ... cdr ) { - if( handler_matches_any_error>::value || check_handler_( tup, ei, fn_mp_args{ } ) ) - return handler_caller::call( tup, ei, std::forward(car), fn_mp_args{ } ); + if( handler_matches_any_error>::value || check_handler_( ctx.tup(), ei, fn_mp_args{ } ) ) + return handler_caller::call( ctx, ei, std::forward(car), fn_mp_args{ } ); else - return handle_error_( tup, ei, std::forward(cdr)...); + return handle_error_( ctx, ei, std::forward(cdr)...); } - template + template BOOST_LEAF_CONSTEXPR inline R - handle_error_tuple_( Tup & tup, error_info const & ei, leaf_detail_mp11::index_sequence, HTup && htup ) + handle_error_tuple_( Context & ctx, error_info const & ei, leaf_detail_mp11::index_sequence, HTup && htup ) { - return handle_error_(tup, ei, std::get(std::forward(htup))...); + return handle_error_(ctx, ei, std::get(std::forward(htup))...); } - template + template BOOST_LEAF_CONSTEXPR inline R - handle_error_tuple_( Tup & tup, error_info const & ei, leaf_detail_mp11::index_sequence, HTup && htup, Cdr && ... cdr ) + handle_error_tuple_( Context & ctx, error_info const & ei, leaf_detail_mp11::index_sequence, HTup && htup, Cdr && ... cdr ) { - return handle_error_(tup, ei, std::get(std::forward(htup))..., std::forward(cdr)...); + return handle_error_(ctx, ei, std::get(std::forward(htup))..., std::forward(cdr)...); } - template + template BOOST_LEAF_CONSTEXPR inline typename std::enable_if::type>::value, R>::type - handle_error_( Tup & tup, error_info const & ei, H && h ) + handle_error_( Context & ctx, error_info const & ei, H && h ) { return handle_error_tuple_( - tup, + ctx, ei, leaf_detail_mp11::make_index_sequence::type>::value>(), std::forward(h)); } - template + template BOOST_LEAF_CONSTEXPR inline typename std::enable_if::type>::value, R>::type - handle_error_( Tup & tup, error_info const & ei, Car && car, Cdr && ... cdr ) + handle_error_( Context & ctx, error_info const & ei, Car && car, Cdr && ... cdr ) { return handle_error_tuple_( - tup, + ctx, ei, leaf_detail_mp11::make_index_sequence::type>::value>(), std::forward(car), @@ -478,7 +478,7 @@ context:: handle_error( error_id id, H && ... h ) const { BOOST_LEAF_ASSERT(!is_active()); - return detail::handle_error_(tup(), error_info(id, nullptr, this->get(id)), std::forward(h)...); + return detail::handle_error_(*this, error_info(id, nullptr, this->get(id)), std::forward(h)...); } template @@ -489,7 +489,7 @@ context:: handle_error( error_id id, H && ... h ) { BOOST_LEAF_ASSERT(!is_active()); - return detail::handle_error_(tup(), error_info(id, nullptr, this->get(id)), std::forward(h)...); + return detail::handle_error_(*this, error_info(id, nullptr, this->get(id)), std::forward(h)...); } //////////////////////////////////////// @@ -585,7 +585,7 @@ namespace detail { ctx.deactivate(); error_id id = detail::unpack_error_id(ex); - return handle_error_(ctx.tup(), error_info(id, &ex, ctx.template get(id)), std::forward(h)..., + return handle_error_(ctx, error_info(id, &ex, ctx.template get(id)), std::forward(h)..., [&]() -> R { ctx.unload(id); @@ -596,7 +596,7 @@ namespace detail { ctx.deactivate(); error_id id = current_error(); - return handle_error_(ctx.tup(), error_info(id, nullptr, ctx.template get(id)), std::forward(h)..., + return handle_error_(ctx, error_info(id, nullptr, ctx.template get(id)), std::forward(h)..., [&]() -> R { ctx.unload(id); @@ -675,7 +675,7 @@ try_catch( TryBlock && try_block, H && ... h ) { ctx.deactivate(); error_id id = detail::unpack_error_id(ex); - return detail::handle_error_(ctx.tup(), error_info(id, &ex, ctx.template get(id)), std::forward(h)..., + return detail::handle_error_(ctx, error_info(id, &ex, ctx.template get(id)), std::forward(h)..., [&]() -> R { ctx.unload(id); @@ -686,7 +686,7 @@ try_catch( TryBlock && try_block, H && ... h ) { ctx.deactivate(); error_id id = current_error(); - return detail::handle_error_(ctx.tup(), error_info(id, nullptr, ctx.template get(id)), std::forward(h)..., + return detail::handle_error_(ctx, error_info(id, nullptr, ctx.template get(id)), std::forward(h)..., [&]() -> R { ctx.unload(id); @@ -863,10 +863,10 @@ namespace detail return nullptr; } - template - BOOST_LEAF_CONSTEXPR static boost::error_info get( Tup const & tup, error_info const & ei ) noexcept + template + BOOST_LEAF_CONSTEXPR static boost::error_info get( Context const & ctx, error_info const & ei ) noexcept { - return boost::error_info(*check(tup, ei)); + return boost::error_info(*check(ctx.tup(), ei)); } }; diff --git a/test/boost_json_encoder_test.cpp b/test/boost_json_encoder_test.cpp index 7fb4e13..757b1b8 100644 --- a/test/boost_json_encoder_test.cpp +++ b/test/boost_json_encoder_test.cpp @@ -427,6 +427,47 @@ int main() } #endif + { + int r = leaf::try_handle_all( + []() -> leaf::result + { + return leaf::try_handle_some( + []() -> leaf::result + { + return leaf::new_error(my_error<1>{1, "error one"}); + }, + [](leaf::diagnostic_details const & dd) -> leaf::result + { + boost::json::value j; + output_encoder e{j}; + dd.serialize_to(e); + std::cout << __LINE__ << " nested diagnostic_details JSON output:\n" << boost::json::serialize(j) << std::endl; + if( BOOST_LEAF_CFG_CAPTURE ) + { + BOOST_TEST(j.as_object().contains("my_error<1>")); + if( j.as_object().contains("my_error<1>") ) + { + auto const & e1j = j.at("my_error<1>"); + BOOST_TEST_EQ(boost::json::value_to(e1j.at("code")), 1); + BOOST_TEST_EQ(boost::json::value_to(e1j.at("message")), "error one"); + } + } + else + BOOST_TEST(j.as_object().find("my_error<1>") == j.as_object().end()); + return dd.error(); + } ); + }, + [](my_error<1> const &) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + return boost::report_errors(); } diff --git a/test/context_deduction_test.cpp b/test/context_deduction_test.cpp index 693286e..f9949c0 100644 --- a/test/context_deduction_test.cpp +++ b/test/context_deduction_test.cpp @@ -52,6 +52,24 @@ enum class my_error_code error3 }; +class my_error_code_category: public std::error_category +{ +public: + char const * name() const noexcept override { return "my_error_code"; } + std::string message(int) const override { return "my_error_code"; } +}; + +my_error_code_category const & get_my_error_code_category() +{ + static my_error_code_category cat; + return cat; +} + +std::error_code make_error_code(my_error_code e) +{ + return std::error_code(static_cast(e), get_my_error_code_category()); +} + namespace std { template <> struct is_error_code_enum: std::true_type { }; diff --git a/test/diagnostics_test5.cpp b/test/diagnostics_test5.cpp index 1326a64..7905e9f 100644 --- a/test/diagnostics_test5.cpp +++ b/test/diagnostics_test5.cpp @@ -128,5 +128,43 @@ int main() BOOST_TEST_EQ(counter, 0); } ); + BOOST_TEST_EQ(counter, 0); + { + int r = leaf::try_handle_all( + []() -> leaf::result + { + return leaf::try_handle_some( + []() -> leaf::result + { + return leaf::new_error(info<1>{}); + }, + []( leaf::diagnostic_details const & di ) -> leaf::result + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream st; + st << di; + std::string s = st.str(); + std::cout << s << std::endl; + if( BOOST_LEAF_CFG_DIAGNOSTICS ) + if( BOOST_LEAF_CFG_CAPTURE ) + BOOST_TEST_NE(s.find("info<1>"), s.npos); + else + BOOST_TEST_EQ(s.find("info<1>"), s.npos); +#endif + return di.error(); + } ); + }, + []( info<1> const & ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + BOOST_TEST_EQ(counter, 0); + return boost::report_errors(); } diff --git a/test/nlohmann_json_encoder_test.cpp b/test/nlohmann_json_encoder_test.cpp index acbe510..7a98bbf 100644 --- a/test/nlohmann_json_encoder_test.cpp +++ b/test/nlohmann_json_encoder_test.cpp @@ -428,6 +428,47 @@ int main() } #endif + { + int r = leaf::try_handle_all( + []() -> leaf::result + { + return leaf::try_handle_some( + []() -> leaf::result + { + return leaf::new_error(my_error<1>{1, "error one"}); + }, + [](leaf::diagnostic_details const & dd) -> leaf::result + { + nlohmann::ordered_json j; + output_encoder e{j}; + dd.serialize_to(e); + std::cout << __LINE__ << " nested diagnostic_details JSON output:\n" << std::setw(2) << j << std::endl; + if( BOOST_LEAF_CFG_CAPTURE ) + { + BOOST_TEST(j.contains("my_error<1>")); + if( j.contains("my_error<1>") ) + { + auto const & e1j = j["my_error<1>"]; + BOOST_TEST_EQ(e1j["code"].get(), 1); + BOOST_TEST_EQ(e1j["message"].get(), "error one"); + } + } + else + BOOST_TEST(!j.contains("my_error<1>")); + return dd.error(); + } ); + }, + [](my_error<1> const &) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + return boost::report_errors(); }