2
0
mirror of https://github.com/boostorg/leaf.git synced 2026-02-22 03:22:25 +00:00

Improved diagnostic_details, able to access error objects from parent error handling scopes

This commit is contained in:
Emil Dotchevski
2026-02-10 15:07:46 -05:00
parent f3ac5509d4
commit 36a28472f3
9 changed files with 303 additions and 87 deletions

View File

@@ -50,10 +50,10 @@ namespace detail
template <class Tup>
BOOST_LEAF_CONSTEXPR static error_type * check( Tup &, error_info const & ) noexcept;
template <class Tup>
BOOST_LEAF_CONSTEXPR static E get( Tup & tup, error_info const & ei ) noexcept
template <class Context>
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<error_type>::value, "Handlers must take predicate arguments by value");
@@ -75,10 +75,10 @@ namespace detail
return e && Pred::evaluate(*e);
}
template <class Tup>
BOOST_LEAF_CONSTEXPR static Pred get( Tup const & tup, error_info const & ei ) noexcept
template <class Context>
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 <class E>
struct handler_argument_traits<E *>: handler_argument_always_available<typename std::remove_const<E>::type>
{
template <class Tup>
BOOST_LEAF_CONSTEXPR static E * get( Tup & tup, error_info const & ei) noexcept
template <class Context>
BOOST_LEAF_CONSTEXPR static E * get( Context & ctx, error_info const & ei) noexcept
{
return handler_argument_traits_defaults<E>::check(tup, ei);
return handler_argument_traits_defaults<E>::check(ctx.tup(), ei);
}
};
@@ -204,7 +204,8 @@ namespace detail
tuple_for_each<I-1,Tup>::unload(tup, err_id);
}
static void serialize_to(encoder & e, void const * tup, error_id id)
template <class Encoder>
static void serialize_to(Encoder & e, void const * tup, error_id id)
{
BOOST_LEAF_ASSERT(tup != nullptr);
tuple_for_each<I-1,Tup>::serialize_to(e, tup, id);
@@ -221,10 +222,12 @@ namespace detail
BOOST_LEAF_CONSTEXPR static void serialize_to(encoder &, void const *, error_id) { }
};
template <class Tup>
void serialize_tuple_contents_to(encoder & e, void const * tup, error_id id)
class context_base;
template <class Context>
void serialize_context_to(encoder & e, context_base const & ctx, error_id id)
{
tuple_for_each<std::tuple_size<Tup>::value, Tup>::serialize_to(e, tup, id);
static_cast<Context const &>(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( context_base && ) noexcept = default;
~context_base() noexcept = default;
void link() noexcept
{
parent_ = tls::read_ptr<context_base>();
tls::write_ptr<context_base>(this);
}
void unlink() noexcept
{
BOOST_LEAF_ASSERT(tls::read_ptr<context_base>() == this);
tls::write_ptr<context_base>(parent_);
}
public:
virtual void serialize_to_( encoder &, error_id ) const = 0;
#endif
}; // class context_base
} // namespace detail
template <class... E>
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<std::tuple_size<Tup>::value, Tup>::serialize_to(e, &tup_, id);
if( parent_ )
parent_->serialize_to_(e, id);
}
#endif
public:
template <class Encoder>
void serialize_to( Encoder & e, error_id id ) const
{
detail::tuple_for_each<std::tuple_size<Tup>::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<std::tuple_size<Tup>::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<std::tuple_size<Tup>::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<std::tuple_size<Tup>::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)

View File

@@ -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);
} );
}
}

View File

@@ -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 <class Tup>
BOOST_LEAF_CONSTEXPR diagnostic_info( error_info const & ei, Tup const & tup ) noexcept:
template <class Context>
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<Tup>)
ctx_(ctx),
serialize_ctx_to_(&detail::serialize_context_to<Context>)
{
}
@@ -31,7 +31,7 @@ protected:
void serialize_to_(Encoder & e) const
{
static_assert(std::is_base_of<detail::encoder, Encoder>::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 <class Tup>
BOOST_LEAF_CONSTEXPR diagnostic_info_( error_info const & ei, Tup const & tup ) noexcept:
diagnostic_info(ei, tup)
template <class Context>
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<diagnostic_info const &>: handler_argument_always_available<e_source_location>
{
template <class Tup>
BOOST_LEAF_CONSTEXPR static diagnostic_info_ get( Tup const & tup, error_info const & ei ) noexcept
template <class Context>
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 <class Tup>
BOOST_LEAF_CONSTEXPR diagnostic_details( error_info const & ei, Tup const & tup, detail::dynamic_allocator const * da ) noexcept:
diagnostic_info(ei, tup),
template <class Context>
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 <class Tup>
BOOST_LEAF_CONSTEXPR diagnostic_details_( error_info const & ei, Tup const & tup, dynamic_allocator const * da ) noexcept:
diagnostic_details(ei, tup, da)
template <class Context>
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<diagnostic_details const &>: handler_argument_always_available<e_source_location, dynamic_allocator>
{
template <class Tup>
BOOST_LEAF_CONSTEXPR static diagnostic_details_ get( Tup const & tup, error_info const & ei ) noexcept
template <class Context>
BOOST_LEAF_CONSTEXPR static diagnostic_details_ get( Context const & ctx, error_info const & ei ) noexcept
{
slot<dynamic_allocator> const * da = find_in_tuple<slot<dynamic_allocator>>(tup);
return diagnostic_details_(ei, tup, da ? &da->get() : nullptr );
slot<dynamic_allocator> const * da = find_in_tuple<slot<dynamic_allocator>>(ctx.tup());
return diagnostic_details_(ei, ctx, da ? &da->get() : nullptr );
}
};
}
@@ -163,9 +163,9 @@ protected:
diagnostic_details( diagnostic_details const & ) noexcept = default;
template <class Tup>
BOOST_LEAF_CONSTEXPR diagnostic_details( error_info const & ei, Tup const & tup ) noexcept:
diagnostic_info(ei, tup)
template <class Context>
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 <class Tup>
BOOST_LEAF_CONSTEXPR diagnostic_details_( error_info const & ei, Tup const & tup ) noexcept:
diagnostic_details(ei, tup)
template <class Context>
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<diagnostic_details const &>: handler_argument_always_available<e_source_location>
{
template <class Tup>
BOOST_LEAF_CONSTEXPR static diagnostic_details_ get( Tup const & tup, error_info const & ei ) noexcept
template <class Context>
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);
}
};
}

View File

@@ -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_;

View File

@@ -377,10 +377,10 @@ namespace detail
template <class R, class F, bool IsResult = is_result_type<R>::value, class FReturnType = fn_return_type<F>>
struct handler_caller
{
template <class Tup, class... A>
BOOST_LEAF_CONSTEXPR static R call( Tup & tup, error_info const & ei, F && f, leaf_detail_mp11::mp_list<A...> )
template <class Context, class... A>
BOOST_LEAF_CONSTEXPR static R call( Context & ctx, error_info const & ei, F && f, leaf_detail_mp11::mp_list<A...> )
{
return std::forward<F>(f)( handler_argument_traits<A>::get(tup, ei)... );
return std::forward<F>(f)( handler_argument_traits<A>::get(ctx, ei)... );
}
};
@@ -389,10 +389,10 @@ namespace detail
{
using R = Result<void, E...>;
template <class Tup, class... A>
BOOST_LEAF_CONSTEXPR static R call( Tup & tup, error_info const & ei, F && f, leaf_detail_mp11::mp_list<A...> )
template <class Context, class... A>
BOOST_LEAF_CONSTEXPR static R call( Context & ctx, error_info const & ei, F && f, leaf_detail_mp11::mp_list<A...> )
{
std::forward<F>(f)( handler_argument_traits<A>::get(tup, ei)... );
std::forward<F>(f)( handler_argument_traits<A>::get(ctx, ei)... );
return { };
}
};
@@ -406,61 +406,61 @@ namespace detail
template <class... T>
struct is_tuple<std::tuple<T...> &>: std::true_type { };
template <class R, class Tup, class H>
template <class R, class Context, class H>
BOOST_LEAF_CONSTEXPR inline
typename std::enable_if<!is_tuple<typename std::decay<H>::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<fn_mp_args<H>>::value, "The last handler passed to handle_all must match any error." );
return handler_caller<R, H>::call( tup, ei, std::forward<H>(h), fn_mp_args<H>{ } );
return handler_caller<R, H>::call( ctx, ei, std::forward<H>(h), fn_mp_args<H>{ } );
}
template <class R, class Tup, class Car, class... Cdr>
template <class R, class Context, class Car, class... Cdr>
BOOST_LEAF_CONSTEXPR inline
typename std::enable_if<!is_tuple<typename std::decay<Car>::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<fn_mp_args<Car>>::value || check_handler_( tup, ei, fn_mp_args<Car>{ } ) )
return handler_caller<R, Car>::call( tup, ei, std::forward<Car>(car), fn_mp_args<Car>{ } );
if( handler_matches_any_error<fn_mp_args<Car>>::value || check_handler_( ctx.tup(), ei, fn_mp_args<Car>{ } ) )
return handler_caller<R, Car>::call( ctx, ei, std::forward<Car>(car), fn_mp_args<Car>{ } );
else
return handle_error_<R>( tup, ei, std::forward<Cdr>(cdr)...);
return handle_error_<R>( ctx, ei, std::forward<Cdr>(cdr)...);
}
template <class R, class Tup, class HTup, size_t ... I>
template <class R, class Context, class HTup, size_t ... I>
BOOST_LEAF_CONSTEXPR inline
R
handle_error_tuple_( Tup & tup, error_info const & ei, leaf_detail_mp11::index_sequence<I...>, HTup && htup )
handle_error_tuple_( Context & ctx, error_info const & ei, leaf_detail_mp11::index_sequence<I...>, HTup && htup )
{
return handle_error_<R>(tup, ei, std::get<I>(std::forward<HTup>(htup))...);
return handle_error_<R>(ctx, ei, std::get<I>(std::forward<HTup>(htup))...);
}
template <class R, class Tup, class HTup, class... Cdr, size_t ... I>
template <class R, class Context, class HTup, class... Cdr, size_t ... I>
BOOST_LEAF_CONSTEXPR inline
R
handle_error_tuple_( Tup & tup, error_info const & ei, leaf_detail_mp11::index_sequence<I...>, HTup && htup, Cdr && ... cdr )
handle_error_tuple_( Context & ctx, error_info const & ei, leaf_detail_mp11::index_sequence<I...>, HTup && htup, Cdr && ... cdr )
{
return handle_error_<R>(tup, ei, std::get<I>(std::forward<HTup>(htup))..., std::forward<Cdr>(cdr)...);
return handle_error_<R>(ctx, ei, std::get<I>(std::forward<HTup>(htup))..., std::forward<Cdr>(cdr)...);
}
template <class R, class Tup, class H>
template <class R, class Context, class H>
BOOST_LEAF_CONSTEXPR inline
typename std::enable_if<is_tuple<typename std::decay<H>::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_<R>(
tup,
ctx,
ei,
leaf_detail_mp11::make_index_sequence<std::tuple_size<typename std::decay<H>::type>::value>(),
std::forward<H>(h));
}
template <class R, class Tup, class Car, class... Cdr>
template <class R, class Context, class Car, class... Cdr>
BOOST_LEAF_CONSTEXPR inline
typename std::enable_if<is_tuple<typename std::decay<Car>::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_<R>(
tup,
ctx,
ei,
leaf_detail_mp11::make_index_sequence<std::tuple_size<typename std::decay<Car>::type>::value>(),
std::forward<Car>(car),
@@ -478,7 +478,7 @@ context<E...>::
handle_error( error_id id, H && ... h ) const
{
BOOST_LEAF_ASSERT(!is_active());
return detail::handle_error_<R>(tup(), error_info(id, nullptr, this->get<e_source_location>(id)), std::forward<H>(h)...);
return detail::handle_error_<R>(*this, error_info(id, nullptr, this->get<e_source_location>(id)), std::forward<H>(h)...);
}
template <class... E>
@@ -489,7 +489,7 @@ context<E...>::
handle_error( error_id id, H && ... h )
{
BOOST_LEAF_ASSERT(!is_active());
return detail::handle_error_<R>(tup(), error_info(id, nullptr, this->get<e_source_location>(id)), std::forward<H>(h)...);
return detail::handle_error_<R>(*this, error_info(id, nullptr, this->get<e_source_location>(id)), std::forward<H>(h)...);
}
////////////////////////////////////////
@@ -585,7 +585,7 @@ namespace detail
{
ctx.deactivate();
error_id id = detail::unpack_error_id(ex);
return handle_error_<R>(ctx.tup(), error_info(id, &ex, ctx.template get<e_source_location>(id)), std::forward<H>(h)...,
return handle_error_<R>(ctx, error_info(id, &ex, ctx.template get<e_source_location>(id)), std::forward<H>(h)...,
[&]() -> R
{
ctx.unload(id);
@@ -596,7 +596,7 @@ namespace detail
{
ctx.deactivate();
error_id id = current_error();
return handle_error_<R>(ctx.tup(), error_info(id, nullptr, ctx.template get<e_source_location>(id)), std::forward<H>(h)...,
return handle_error_<R>(ctx, error_info(id, nullptr, ctx.template get<e_source_location>(id)), std::forward<H>(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_<R>(ctx.tup(), error_info(id, &ex, ctx.template get<e_source_location>(id)), std::forward<H>(h)...,
return detail::handle_error_<R>(ctx, error_info(id, &ex, ctx.template get<e_source_location>(id)), std::forward<H>(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_<R>(ctx.tup(), error_info(id, nullptr, ctx.template get<e_source_location>(id)), std::forward<H>(h)...,
return detail::handle_error_<R>(ctx, error_info(id, nullptr, ctx.template get<e_source_location>(id)), std::forward<H>(h)...,
[&]() -> R
{
ctx.unload(id);
@@ -863,10 +863,10 @@ namespace detail
return nullptr;
}
template <class Tup>
BOOST_LEAF_CONSTEXPR static boost::error_info<Tag, T> get( Tup const & tup, error_info const & ei ) noexcept
template <class Context>
BOOST_LEAF_CONSTEXPR static boost::error_info<Tag, T> get( Context const & ctx, error_info const & ei ) noexcept
{
return boost::error_info<Tag, T>(*check(tup, ei));
return boost::error_info<Tag, T>(*check(ctx.tup(), ei));
}
};

View File

@@ -427,6 +427,47 @@ int main()
}
#endif
{
int r = leaf::try_handle_all(
[]() -> leaf::result<int>
{
return leaf::try_handle_some(
[]() -> leaf::result<int>
{
return leaf::new_error(my_error<1>{1, "error one"});
},
[](leaf::diagnostic_details const & dd) -> leaf::result<int>
{
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<int>(e1j.at("code")), 1);
BOOST_TEST_EQ(boost::json::value_to<std::string>(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();
}

View File

@@ -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<int>(e), get_my_error_code_category());
}
namespace std
{
template <> struct is_error_code_enum<my_error_code>: std::true_type { };

View File

@@ -128,5 +128,43 @@ int main()
BOOST_TEST_EQ(counter, 0);
} );
BOOST_TEST_EQ(counter, 0);
{
int r = leaf::try_handle_all(
[]() -> leaf::result<int>
{
return leaf::try_handle_some(
[]() -> leaf::result<int>
{
return leaf::new_error(info<1>{});
},
[]( leaf::diagnostic_details const & di ) -> leaf::result<int>
{
#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();
}

View File

@@ -428,6 +428,47 @@ int main()
}
#endif
{
int r = leaf::try_handle_all(
[]() -> leaf::result<int>
{
return leaf::try_handle_some(
[]() -> leaf::result<int>
{
return leaf::new_error(my_error<1>{1, "error one"});
},
[](leaf::diagnostic_details const & dd) -> leaf::result<int>
{
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<int>(), 1);
BOOST_TEST_EQ(e1j["message"].get<std::string>(), "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();
}