2
0
mirror of https://github.com/boostorg/leaf.git synced 2026-01-19 04:22:08 +00:00

minor refactor

This commit is contained in:
Emil Dotchevski
2026-01-11 00:16:00 -05:00
parent d4d8a537d0
commit 7f85a299d6
21 changed files with 355 additions and 502 deletions

View File

@@ -1740,40 +1740,12 @@ LEAF provides a serialization API that enables exporting error information into
* <<diagnostic_details>>
* <<result>>
==== Custom Writers
LEAF serialization is defined in terms of `write` and `write_nested` function calls, found via ADL:
To support different serialization formats, users can define custom writer types that derive from `leaf::serialization::writer`. Writers should provide `write` and `write_nested` friend functions:
* `write(w, x)` serializes `x` directly to writer `w`.
* `write_nested(w, x, name)` serializes `x` to writer `w` as a named field.
[source,c++]
----
#include <boost/leaf/serialization/writer.hpp>
class my_writer: public leaf::serialization::writer
{
public:
my_writer() noexcept:
writer(this)
{
}
template <class E>
friend void write_nested(my_writer & w, E const & e, char const * name)
{
.... // Serialize e as a named field
}
template <class E>
friend void write(my_writer & w, E const & e)
{
.... // Serialize e
}
};
----
The `write` function serializes an error object directly to the writer. The `write_nested` function serializes an error object as a named field, typically calling `write` internally. LEAF finds these friend functions via ADL using the writer type.
LEAF provides suitable `write` overloads for the following types:
LEAF provides generic `write` overloads for the following types:
* `error_id`
* `e_source_location`
@@ -1784,9 +1756,36 @@ LEAF provides suitable `write` overloads for the following types:
* `std::exception_ptr`
* any type with a `.value` member for which a suitable `write` can be found via ADL
Custom writers must also handle types that do not provide ADL `write` overloads, including built-in types like `int` and `std::string` as needed. See <<json_writer>> for an example.
[[custom-writers]]
==== Custom Writers
To enable serialization of error objects to custom writer types, define a `serialize` function template in the `boost::leaf::serialization` namespace:
To support exporting to a specific format, users define a writer class with associated `write` and `write_nested` function templates specific to that writer:
[source,c++]
----
struct my_writer
{
std::ostream & os;
template <class T>
friend void write(my_writer & w, T const & x)
{
// write x to w
}
template <class T>
friend void write_nested(my_writer & w, T const & x, char const * name)
{
// write x to w as a named field
}
};
----
The `write` function outputs a value. The `write_nested` function typically creates a nested scope (e.g. a JSON object or XML element) and then makes an unqualified call to `write` to output the value. This will call any compatible overload found via ADL.
TIP: The `write` function may need to use SFINAE to avoid ambiguities with the generic `write` overloads provided by LEAF. Custom writers must also handle types that do not provide ADL `write` overloads, including built-in types like `int` and `std::string`.
To enable serialization to a custom writer type, define a `serialize` function template in the `boost::leaf::serialization` namespace:
[source,c++]
----
@@ -1794,54 +1793,70 @@ namespace boost { namespace leaf {
namespace serialization {
template <class E>
void serialize(writer & w, E const & e, char const * name)
{
if( my_writer * mw = w.get<my_writer>() )
write_nested(*mw, e, name);
}
template <class Handle, class E>
void serialize(Handle & h, E const & e, char const * name)
{
h.dispatch([&](my_writer & mw) {
write_nested(mw, e, name);
});
}
}
} }
----
The `serialize` function receives a polymorphic `writer &` reference, the object being serialized, and its name. Use the `get<Derived>()` member function to check for a specific writer type. If the cast succeeds (returns non-null), call `write_nested` to serialize the error object with its type name. To support multiple output formats, this user-defined `serialize` function should check for each writer type accordingly.
The `serialize` function template takes a handle reference `h` (of unspecified type) that holds a writer, the error object to be serialized, and its type name. Call `h.dispatch` with a single-argument function F to detect the writer type based on F's argument type; F is called only if the handle contains a writer of that type.
==== JSON Serialization
LEAF provides `json_writer`, a writer class template for JSON serialization. The `json_writer` class provides `write` and `write_nested` friend functions that work with:
* LEAF types that provide their own `write` overloads (e.g. `error_id`, `e_source_location`, `e_errno`, `std::error_code`, `std::error_condition`, `std::exception`, `std::exception_ptr`)
* Types for which `to_json` overloads can be found via ADL
This interface is compatible with https://github.com/nlohmann/json[nlohmann/json]:
To support multiple output formats, pass multiple functions to `h.dispatch`:
[source,c++]
----
#include <boost/leaf/diagnostics.hpp>
#include <boost/leaf/serialization/json_writer.hpp>
h.dispatch(
[&](nlohmann_writer & nw) { write_nested(nw, e, name); },
[&](xml_writer & xw) { write_nested(xw, e, name); }
);
----
==== JSON Serialization
LEAF provides `nlohmann_writer`, a class template for JSON serialization. The `nlohmann_writer` class defines `write` and `write_nested` friend functions (see <<tutorial-serialization>>) that work with:
* Types for which `to_json` overloads can be found via ADL
* Types for which a `write` overload can be found via ADL (e.g. LEAF types)
This interface is compatible with https://github.com/nlohmann/json[nlohmann/json], we just need to define the required `serialize` function template (see <<custom-writers>>):
[source,c++]
----
#include <boost/leaf/serialization/nlohmann_writer.hpp>
#include "nlohmann/json.hpp"
namespace leaf = boost::leaf;
using nlohmann_writer = leaf::serialization::json_writer<nlohmann::json>;
using nlohmann_writer = leaf::serialization::nlohmann_writer<nlohmann::json>;
namespace boost { namespace leaf {
namespace serialization {
template <class E>
void serialize(writer & w, E const & e, char const * name)
{
if( nlohmann_writer * nw = w.get<nlohmann_writer>() )
write_nested(*nw, e, name);
}
template <class Handle, class E>
void serialize(Handle & h, E const & e, char const * name)
{
h.dispatch([&](nlohmann_writer & nw) {
write_nested(nw, e, name);
});
}
}
} }
----
With this in place, we can easily (for example) serialize <<diagnostic_details>> to JSON:
[source,c++]
----
struct e_api_response
{
int status;
@@ -2852,82 +2867,36 @@ Reference: <<to_variant>>
=== Serialization
[[writer.hpp]]
==== `writer.hpp`
[[nlohmann_writer.hpp]]
==== `nlohmann_writer.hpp`
====
.#include <boost/leaf/serialization/writer.hpp>
.#include <boost/leaf/serialization/nlohmann_writer.hpp>
[source,c++]
----
namespace boost { namespace leaf {
namespace serialization
{
class writer
{
protected:
template <class Derived>
explicit writer( Derived * ) noexcept;
~writer() noexcept;
public:
template <class Derived>
Derived * get() noexcept;
};
// Enabled for types with a .value member for which
// a suitable write can be found via ADL.
template <class W, class E>
void write( W &, E const & );
} // namespace serialization
} }
----
[.text-right]
Reference: <<writer>> | <<serialize>>
====
[[json_writer.hpp]]
==== `json_writer.hpp`
====
.#include <boost/leaf/serialization/json_writer.hpp>
[source,c++]
----
namespace boost { namespace leaf {
namespace serialization
{
template <class Json>
class json_writer: public writer
struct nlohmann_writer
{
public:
Json & j_;
explicit json_writer( Json & ) noexcept;
template <class T>
friend auto write( nlohmann_writer &, T const & x )
-> decltype(to_json(std::declval<Json &>(), x));
template <class E>
friend void write_nested( json_writer &, E const &, char const * name );
// Enabled for types for which a suitable to_json overload
// can be found via ADL.
template <class E>
friend void write( json_writer &, E const & );
template <class T>
friend void write_nested( nlohmann_writer &, T const &, char const * name );
};
}
} }
----
[.text-right]
Reference: <<json_writer>>
Reference: <<nlohmann_writer>>
====
[[functions]]
@@ -3227,21 +3196,20 @@ TIP: See <<tutorial-on_error>> from the Tutorial.
[[serialize]]
=== `serialize`
.#include <boost/leaf/serialization/writer.hpp>
[source,c++]
----
namespace boost { namespace leaf {
namespace serialization
{
template <class E>
void serialize( writer &, E const &, char const * name );
template <class Handle, class E>
void serialize( Handle &, E const &, char const * name );
}
} }
----
The `serialize` function template is a user-defined customization point. If defined, it is called by the serialization system to serialize error objects to a writer. See <<tutorial-serialization>>.
The `serialize` function template is a user-defined customization point. If provided, it is called by the serialization system to output error objects; see <<custom-writers>>.
'''
@@ -4474,10 +4442,10 @@ The `write_to` member function is used with the serialization system; see <<tuto
'''
[[json_writer]]
=== `json_writer`
[[nlohmann_writer]]
=== `nlohmann_writer`
.#include <boost/leaf/serialization/json_writer.hpp>
.#include <boost/leaf/serialization/nlohmann_writer.hpp>
[source,c++]
----
namespace boost { namespace leaf {
@@ -4485,32 +4453,25 @@ namespace boost { namespace leaf {
namespace serialization
{
template <class Json>
class json_writer: public writer
struct nlohmann_writer
{
public:
explicit json_writer( Json & ) noexcept;
template <class E>
friend void write_nested( json_writer &, E const &, char const * name );
Json & j_;
// Enabled for types for which a suitable to_json overload
// can be found via ADL.
template <class E>
friend void write( json_writer &, E const & );
// can be found via ADL, but a suitable write overload cannot.
template <class T>
friend auto write( nlohmann_writer &, T const & x )
-> decltype(to_json(std::declval<Json &>(), x));
template <class T>
friend void write_nested( nlohmann_writer &, T const &, char const * name );
};
}
} }
----
The `json_writer` class template is used to serialize error objects to a JSON object. The `Json` template parameter is the type of the JSON object, for example https://github.com/nlohmann/json[nlohmann/json].
The constructor takes a reference to a JSON object, which will be populated with serialized error data.
The `write_nested` friend function creates a named field in the JSON object and calls `write` to serialize the error object into it.
The `write` friend function serializes an error object by calling `to_json(json, e)`, found via ADL. LEAF types provide their own `write` overloads instead of `to_json`. See <<tutorial-serialization>>.
The `nlohmann_writer` class template serializes error objects to JSON format using unqualified calls to `to_json`. This is compatible with https://github.com/nlohmann/json[nlohmann/json]; See <<tutorial-serialization>>.
'''
@@ -5013,48 +4974,6 @@ namespace boost { namespace leaf {
} }
----
'''
[[writer]]
=== `writer`
.#include <boost/leaf/serialization/writer.hpp>
[source,c++]
----
namespace boost { namespace leaf {
namespace serialization
{
class writer
{
protected:
template <class Derived>
explicit writer( Derived * ) noexcept;
~writer() noexcept;
public:
template <class Derived>
Derived * get() noexcept;
};
// Enabled for types with a .value member for which
// a suitable write overload can be found via ADL.
template <class W, class E>
void write( W &, E const & );
}
} }
----
The `writer` class is the base class for custom serialization writers. The constructor takes a pointer to the derived class, which is used by the `get` member function to recognize writer types at runtime.
The `get` member function returns a pointer to the derived class if the type matches, or `nullptr` otherwise. This allows the <<serialize>> function to dispatch to the correct writer type.
Derived writer types typically provide `write` and `write_nested` friend functions for serializing error objects. The `write` function serializes an error object directly, while `write_nested` serializes it as a named field. See <<tutorial-serialization>>.
[[predicates]]
== Reference: Predicates

View File

@@ -204,7 +204,7 @@ namespace detail
tuple_for_each<I-1,Tup>::unload(tup, err_id);
}
static void write_to(serialization::writer & w, void const * tup, error_id id)
static void write_to(writer & w, void const * tup, error_id id)
{
BOOST_LEAF_ASSERT(tup != nullptr);
tuple_for_each<I-1,Tup>::write_to(w, tup, id);
@@ -218,11 +218,11 @@ namespace detail
BOOST_LEAF_CONSTEXPR static void activate( Tup & ) noexcept { }
BOOST_LEAF_CONSTEXPR static void deactivate( Tup & ) noexcept { }
BOOST_LEAF_CONSTEXPR static void unload( Tup &, int ) noexcept { }
BOOST_LEAF_CONSTEXPR static void write_to(serialization::writer &, void const *, error_id) { }
BOOST_LEAF_CONSTEXPR static void write_to(writer &, void const *, error_id) { }
};
template <class Tup>
BOOST_LEAF_CONSTEXPR void serialize_tuple_contents(serialization::writer & w, void const * tup, error_id id)
void serialize_tuple_contents(writer & w, void const * tup, error_id id)
{
tuple_for_each<std::tuple_size<Tup>::value, Tup>::write_to(w, tup, id);
}
@@ -368,7 +368,7 @@ public:
return is_active_;
}
void write_to( serialization::writer & w ) const
void write_to( detail::diagnostics_writer & w ) const
{
detail::serialize_tuple_contents<Tup>(w, &tup_, error_id());
}

View File

@@ -11,6 +11,5 @@
#include <boost/leaf/on_error.hpp>
#include <boost/leaf/pred.hpp>
#include <boost/leaf/result.hpp>
#include <boost/leaf/serialization/json_writer.hpp>
#include <boost/leaf/serialization/writer.hpp>
#include <boost/leaf/serialization/nlohmann_writer.hpp>
#include <boost/leaf/to_variant.hpp>

View File

@@ -12,10 +12,10 @@
namespace boost { namespace leaf {
class error_id;
namespace serialization { class writer; }
namespace detail
{
class writer;
class capture_list
{
@@ -29,7 +29,7 @@ namespace detail
friend class capture_list;
virtual void unload( int err_id ) = 0;
virtual void write_to(serialization::writer &, error_id const &) const = 0;
virtual void write_to(writer &, error_id const &) const = 0;
protected:
@@ -90,7 +90,7 @@ namespace detail
} );
}
void write_to(serialization::writer & w, error_id const & id) const
void write_to(writer & w, error_id const & id) const
{
if( first_ )
{

View File

@@ -6,7 +6,7 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/leaf/config.hpp>
#include <boost/leaf/serialization/writer.hpp>
#include <boost/leaf/detail/writer.hpp>
#include <boost/leaf/detail/exception_base.hpp>
#include <type_traits>
@@ -52,7 +52,7 @@ namespace detail
////////////////////////////////////////
class diagnostics_writer: public serialization::writer
class diagnostics_writer: public writer
{
diagnostics_writer(diagnostics_writer const &) = delete;
diagnostics_writer & operator=(diagnostics_writer const &) = delete;
@@ -161,10 +161,10 @@ namespace detail
delimiter_ = delimiter;
}
template <class E>
void write(E const & e)
template <class T>
void write(T const & x)
{
diagnostic<E>::print(os_, prefix_, delimiter_, e);
diagnostic<T>::print(os_, prefix_, delimiter_, x);
}
}; // class diagnostics_writer

View File

@@ -7,96 +7,51 @@
#include <boost/leaf/detail/demangle.hpp>
#include <functional>
#if BOOST_LEAF_CFG_STD_STRING
# include <string>
#endif
#if __cplusplus >= 201703L
# include <string_view>
#endif
namespace boost { namespace leaf {
namespace detail
{
struct type_name
{
char const * name_not_zero_terminated_at_length;
std::size_t length;
std::size_t hash;
friend bool operator==(type_name const & a, type_name const & b) noexcept
struct type_name
{
BOOST_LEAF_ASSERT((a.hash == b.hash) == (a.length == b.length && std::memcmp(a.name_not_zero_terminated_at_length, b.name_not_zero_terminated_at_length, a.length) == 0));
return a.hash == b.hash;
}
char const * name_not_zero_terminated_at_length;
std::size_t length;
std::size_t hash;
friend bool operator!=(type_name const & a, type_name const & b) noexcept
friend bool operator==(type_name const & a, type_name const & b) noexcept
{
BOOST_LEAF_ASSERT((a.hash == b.hash) == (a.length == b.length && std::memcmp(a.name_not_zero_terminated_at_length, b.name_not_zero_terminated_at_length, a.length) == 0));
return a.hash == b.hash;
}
friend bool operator!=(type_name const & a, type_name const & b) noexcept
{
return !(a == b);
}
template <class CharT, class Traits>
friend std::ostream & operator<<(std::basic_ostream<CharT, Traits> & os, type_name const & x)
{
return os.write(x.name_not_zero_terminated_at_length, x.length);
}
template <std::size_t S>
friend char * to_zstr(char (&zstr)[S], type_name const & x) noexcept
{
std::size_t n = x.length < S - 1 ? x.length : S - 1;
std::memcpy(zstr, x.name_not_zero_terminated_at_length, n);
zstr[n] = 0;
return zstr;
}
};
template <class T>
type_name get_type_name()
{
return !(a == b);
n::r parsed = n::p<T>();
return { parsed.name_not_zero_terminated_at_length, parsed.length, parsed.hash };
}
friend bool operator<(type_name const & a, type_name const & b) noexcept
{
if( int cmp = std::memcmp(a.name_not_zero_terminated_at_length, b.name_not_zero_terminated_at_length, a.length < b.length ? a.length : b.length) )
return cmp < 0;
return a.length < b.length;
}
template <class CharT, class Traits>
friend std::ostream & operator<<(std::basic_ostream<CharT, Traits> & os, type_name const & x)
{
return os.write(x.name_not_zero_terminated_at_length, x.length);
}
#if __cplusplus >= 201703L
friend std::string_view to_string_view(type_name const & x) noexcept
{
return std::string_view(x.name_not_zero_terminated_at_length, x.length);
}
#endif
#if BOOST_LEAF_CFG_STD_STRING
friend std::string to_string(type_name const & x)
{
return std::string(x.name_not_zero_terminated_at_length, x.length);
}
#endif
template <std::size_t S>
friend char * to_zstr(char (&zstr)[S], type_name const & x) noexcept
{
std::size_t n = x.length < S - 1 ? x.length : S - 1;
std::memcpy(zstr, x.name_not_zero_terminated_at_length, n);
zstr[n] = 0;
return zstr;
}
};
template <class T>
type_name get_type_name()
{
n::r parsed = n::p<T>();
return { parsed.name_not_zero_terminated_at_length, parsed.length, parsed.hash };
}
} // namespace detail
} } // namespace boost::leaf
namespace std
{
template <>
struct hash<boost::leaf::detail::type_name>
{
std::size_t operator()(boost::leaf::detail::type_name const & x) const noexcept
{
return x.hash;
}
};
} // namespace std
#endif // #ifndef BOOST_LEAF_DETAIL_TYPE_NAME_HPP_INCLUDED

View File

@@ -0,0 +1,92 @@
#ifndef BOOST_LEAF_DETAIL_WRITER_HPP_INCLUDED
#define BOOST_LEAF_DETAIL_WRITER_HPP_INCLUDED
// Copyright 2018-2026 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 <boost/leaf/config.hpp>
#include <boost/leaf/detail/type_name.hpp>
#include <boost/leaf/detail/function_traits.hpp>
namespace boost { namespace leaf {
namespace serialization
{
struct writer_adl {};
template <class Writer, class E>
auto write(Writer & w, E const & e) -> decltype(write(w, e.value))
{
write(w, e.value);
}
}
namespace detail
{
class writer:
serialization::writer_adl
{
writer(writer const &) = delete;
writer & operator=(writer const &) = delete;
type_name const type_;
void * const w_;
bool dispatch_()
{
return false;
}
template <class F1, class... Fn>
bool dispatch_(F1 && f1, Fn && ... fn)
{
using writer_type = typename std::decay<fn_arg_type<F1, 0>>::type;
if (writer_type * w = get<writer_type>())
{
std::forward<F1>(f1)(*w);
return true;
}
return dispatch_(std::forward<Fn>(fn)...);
}
protected:
template <class Writer>
explicit writer(Writer * w) noexcept:
type_(get_type_name<Writer>()),
w_(w)
{
}
public:
template <class Writer>
Writer * get() noexcept
{
return type_ == get_type_name<Writer>() ? static_cast<Writer *>(w_) : nullptr;
}
template <class... Fn>
bool dispatch(Fn && ... fn)
{
using writer_types = leaf_detail_mp11::mp_list<typename std::decay<fn_arg_type<Fn, 0>>::type...>;
static_assert(std::is_same<writer_types, leaf_detail_mp11::mp_unique<writer_types>>::value, "Duplicate writer types in dispatch");
return dispatch_(std::forward<Fn>(fn)...);
}
};
template <class Writer>
struct writer_adaptor:
writer
{
explicit writer_adaptor(Writer & w) noexcept:
writer(&w)
{
}
};
} // namespace detail
} } // namespace boost::leaf
#endif // #ifndef BOOST_LEAF_DETAIL_WRITER_HPP_INCLUDED

View File

@@ -6,7 +6,6 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/leaf/config.hpp>
#include <boost/leaf/context.hpp>
#include <boost/leaf/handle_errors.hpp>
namespace boost { namespace leaf {
@@ -14,7 +13,7 @@ namespace boost { namespace leaf {
class diagnostic_info: public error_info
{
void const * tup_;
void (*serialize_tuple_contents_)(serialization::writer &, void const *, error_id);
void (*serialize_tuple_contents_)(detail::writer &, void const *, error_id);
protected:
@@ -31,6 +30,7 @@ protected:
template <class Writer>
void write_to_(Writer & w) const
{
static_assert(std::is_base_of<detail::writer, Writer>::value, "Writer must derive from detail::writer");
serialize_tuple_contents_(w, tup_, error());
}
@@ -39,8 +39,9 @@ public:
template <class Writer>
void write_to(Writer & w) const
{
error_info::write_to(w);
write_to_(w);
detail::writer_adaptor<Writer> wa(w);
error_info::write_to_(wa);
write_to_(wa);
}
template <class CharT, class Traits>
@@ -100,6 +101,7 @@ protected:
template <class Writer>
void write_to_(Writer & w) const
{
static_assert(std::is_base_of<detail::writer, Writer>::value, "Writer must derive from detail::writer");
if( da_ )
da_->write_to(w, error());
}
@@ -109,8 +111,10 @@ public:
template <class Writer>
void write_to(Writer & w) const
{
diagnostic_info::write_to(w);
write_to_(w);
detail::writer_adaptor<Writer> wa(w);
error_info::write_to_(wa);
diagnostic_info::write_to_(wa);
write_to_(wa);
}
template <class CharT, class Traits>
@@ -170,7 +174,9 @@ public:
template <class Writer>
void write_to(Writer & w) const
{
diagnostic_info::write_to(w);
detail::writer_adaptor<Writer> wa(w);
error_info::write_to_(wa);
diagnostic_info::write_to_(wa);
}
template <class CharT, class Traits>

View File

@@ -119,29 +119,29 @@ struct show_in_diagnostics<e_source_location>: std::false_type
namespace serialization
{
template <class W, class E>
typename std::enable_if<std::is_base_of<writer, W>::value>::type
serialize(W &, E const &, char const *)
template <class Writer, class T, class... Unused>
typename std::enable_if<std::is_base_of<detail::writer, Writer>::value>::type
serialize(Writer &, T const &, char const *, Unused && ...)
{
}
}
namespace detail
{
template <class E>
void serialize_(diagnostics_writer & w, E const & e)
template <class T>
void serialize_(diagnostics_writer & w, T const & x)
{
w.write(e);
w.write(x);
}
template <class E>
void serialize_(serialization::writer & w, E const & e)
template <class T>
void serialize_(writer & w, T const & x)
{
using namespace serialization;
char zstr[1024];
serialize(w, e, to_zstr(zstr, get_type_name<E>()));
if( detail::diagnostics_writer * dw = w.get<detail::diagnostics_writer>() )
dw->write(e);
serialize(w, x, to_zstr(zstr, get_type_name<T>()));
if( diagnostics_writer * dw = w.get<diagnostics_writer>() )
dw->write(x);
}
}
@@ -204,6 +204,7 @@ namespace detail
template <class Writer,class ErrorID>
void write_to(Writer & w, ErrorID id) const
{
static_assert(std::is_base_of<writer, Writer>::value, "Writer must derive from detail::writer");
if( int k = this->key() )
{
if( id && id.value() != k )
@@ -270,7 +271,7 @@ namespace detail
{
impl::unload(err_id);
}
void write_to(serialization::writer & w, error_id const & id) const override
void write_to(writer & w, error_id const & id) const override
{
impl::write_to(w, id);
}
@@ -305,7 +306,7 @@ namespace detail
{
std::rethrow_exception(ex_);
}
void write_to(serialization::writer &, error_id const &) const override
void write_to(writer &, error_id const &) const override
{
}
std::exception_ptr const ex_;
@@ -481,7 +482,7 @@ namespace detail
}
template <class ErrorID>
void write_to(serialization::writer &, ErrorID) const
void write_to(writer &, ErrorID) const
{
}
}; // slot specialization for dynamic_allocator

View File

@@ -45,6 +45,17 @@ protected:
error_info( error_info const & ) noexcept = default;
template <class Writer>
void write_to_(Writer & w) const
{
static_assert(std::is_base_of<detail::writer, Writer>::value, "Writer must derive from detail::writer");
detail::serialize_(w, err_id_);
#ifndef BOOST_LEAF_NO_EXCEPTIONS
if( ex_ )
detail::serialize_(w, *ex_);
#endif
}
public:
BOOST_LEAF_CONSTEXPR error_info(error_id id, std::exception * ex, e_source_location const * loc) noexcept:
@@ -79,11 +90,8 @@ public:
template <class Writer>
void write_to(Writer & w) const
{
detail::serialize_(w, err_id_);
#ifndef BOOST_LEAF_NO_EXCEPTIONS
if( ex_ )
detail::serialize_(w, *ex_);
#endif
detail::writer_adaptor<Writer> wa(w);
write_to_(wa);
}
template <class CharT, class Traits>

View File

@@ -299,6 +299,7 @@ protected:
template <class Writer>
error_id write_error_to(Writer & w) const
{
static_assert(std::is_base_of<detail::writer, Writer>::value, "Writer must derive from detail::writer");
result_discriminant const what = what_;
BOOST_LEAF_ASSERT(what.kind() != result_discriminant::val);
error_id const err_id = what.get_error_id();
@@ -309,6 +310,7 @@ protected:
template <class Writer>
void write_capture_to(Writer & w, error_id err_id) const
{
static_assert(std::is_base_of<detail::writer, Writer>::value, "Writer must derive from detail::writer");
if( what_.kind() == result_discriminant::err_id_capture_list )
{
#if BOOST_LEAF_CFG_CAPTURE
@@ -322,6 +324,7 @@ protected:
template <class Writer>
void print_error( Writer & w ) const
{
static_assert(std::is_base_of<detail::writer, Writer>::value, "Writer must derive from detail::writer");
error_id err_id = write_error_to(w);
w.set_prefix(", captured -> ");
w.set_delimiter(", ");
@@ -576,10 +579,11 @@ public:
template <class Writer>
void write_to(Writer & w) const
{
detail::writer_adaptor<Writer> wa(w);
if( what_.kind() == result_discriminant::val )
detail::serialize_(w, value());
detail::serialize_(wa, value());
else
write_capture_to(w, write_error_to(w));
write_capture_to(wa, write_error_to(wa));
}
template <class CharT, class Traits>
@@ -695,7 +699,10 @@ public:
void write_to(Writer & w) const
{
if( !*this )
write_error_to(w);
{
detail::writer_adaptor<Writer> wa(w);
write_error_to(wa);
}
}
template <class CharT, class Traits>

View File

@@ -1,55 +0,0 @@
#ifndef BOOST_LEAF_SERIALIZATION_JSON_WRITER_HPP_INCLUDED
#define BOOST_LEAF_SERIALIZATION_JSON_WRITER_HPP_INCLUDED
// Copyright 2018-2026 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 <boost/leaf/config.hpp>
#include <boost/leaf/serialization/writer.hpp>
#include <type_traits>
#include <utility>
namespace boost { namespace leaf {
namespace serialization
{
template <class Json, class E, class = void>
struct has_to_json : std::false_type {};
template <class Json, class E>
struct has_to_json<Json, E, decltype(to_json(std::declval<Json &>(), std::declval<E const &>()), void())> : std::true_type {};
template <class Json>
class json_writer: public writer
{
Json & j_;
public:
explicit json_writer(Json & j) noexcept:
writer(this),
j_(j)
{
}
template <class E>
friend void write_nested(json_writer & w, E const & e, char const * name)
{
json_writer nested(w.j_[name]);
write(nested, e);
}
template <class E>
friend typename std::enable_if<has_to_json<Json, E>::value>::type write(json_writer & w, E const & e)
{
to_json(w.j_, e);
}
};
} // namespace serialization
} } // namespace boost::leaf
#endif // #ifndef BOOST_LEAF_SERIALIZATION_JSON_WRITER_HPP_INCLUDED

View File

@@ -0,0 +1,37 @@
#ifndef BOOST_LEAF_SERIALIZATION_NLOHMANN_WRITER_HPP_INCLUDED
#define BOOST_LEAF_SERIALIZATION_NLOHMANN_WRITER_HPP_INCLUDED
// Copyright 2018-2026 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 <utility>
namespace boost { namespace leaf {
namespace serialization
{
template <class Json>
struct nlohmann_writer
{
Json & j_;
template <class T>
friend auto write(nlohmann_writer & w, T const & x) -> decltype(to_json(std::declval<Json &>(), x))
{
to_json(w.j_, x);
}
template <class T>
friend void write_nested(nlohmann_writer & w, T const & x, char const * name)
{
nlohmann_writer nested{w.j_[name]};
write(nested, x);
}
};
}
} }
#endif // #ifndef BOOST_LEAF_SERIALIZATION_NLOHMANN_WRITER_HPP_INCLUDED

View File

@@ -1,72 +0,0 @@
#ifndef BOOST_LEAF_SERIALIZATION_WRITER_HPP_INCLUDED
#define BOOST_LEAF_SERIALIZATION_WRITER_HPP_INCLUDED
// Copyright 2018-2026 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 <boost/leaf/config.hpp>
#include <boost/leaf/detail/type_name.hpp>
#include <type_traits>
namespace boost { namespace leaf {
namespace serialization
{
class writer
{
detail::type_name const type_;
protected:
template <class Derived>
explicit writer(Derived * d) noexcept:
type_(detail::get_type_name<Derived>())
{
BOOST_LEAF_ASSERT(d == this), (void) d;
}
~writer() noexcept
{
}
public:
template <class Derived>
Derived * get() noexcept
{
return type_ == detail::get_type_name<typename std::decay<Derived>::type>() ? static_cast<Derived *>(this) : nullptr;
}
};
}
} }
////////////////////////////////////////
namespace boost { namespace leaf {
namespace serialization
{
template <class T, class E = void>
struct has_member_value: std::false_type
{
};
template <class T>
struct has_member_value<T, decltype((void)std::declval<T const &>().value)>: std::true_type
{
};
template <class W, class E>
typename std::enable_if<has_member_value<E>::value>::type
write(W & w, E const & e)
{
write(w, e.value);
}
}
} } // namespace boost::leaf
#endif // #ifndef BOOST_LEAF_SERIALIZATION_WRITER_HPP_INCLUDED

View File

@@ -224,8 +224,7 @@ if option_enable_unit_tests
'_hpp_on_error_test',
'_hpp_pred_test',
'_hpp_result_test',
'_hpp_serialization_json_writer_test',
'_hpp_serialization_writer_test',
'_hpp_serialization_nlohmann_writer_test',
'_hpp_to_variant_test',
]
foreach t : header_tests

View File

@@ -44,8 +44,7 @@ compile _hpp_leaf_test.cpp ;
compile _hpp_on_error_test.cpp ;
compile _hpp_pred_test.cpp ;
compile _hpp_result_test.cpp ;
compile _hpp_serialization_json_writer_test.cpp ;
compile _hpp_serialization_writer_test.cpp ;
compile _hpp_serialization_nlohmann_writer_test.cpp ;
compile _hpp_to_variant_test.cpp ;
run boost_exception_test.cpp ;
@@ -160,11 +159,11 @@ compile-fail _compile-fail-error_obj_ptr.cpp ;
compile-fail _compile-fail-exception_1.cpp ;
compile-fail _compile-fail-exception_2.cpp ;
compile-fail _compile-fail-new_error.cpp ;
compile-fail _compile-fail-nlohmann.cpp ;
compile-fail _compile-fail-result_1.cpp ;
compile-fail _compile-fail-result_2.cpp ;
compile-fail _compile-fail-result_3.cpp ;
compile-fail _compile-fail-result_4.cpp ;
compile-fail _compile-fail-to_json.cpp ;
exe try_capture_all_exceptions : ../example/try_capture_all_exceptions.cpp : <threading>single:<build>no <exception-handling>off:<build>no <variant>leaf_debug_capture0:<build>no <variant>leaf_release_capture0:<build>no ;
exe try_capture_all_result : ../example/try_capture_all_result.cpp : <threading>single:<build>no <variant>leaf_debug_capture0:<build>no <variant>leaf_release_capture0:<build>no <variant>leaf_debug_embedded:<build>no <variant>leaf_release_embedded:<build>no ;

View File

@@ -2,7 +2,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 <boost/leaf/serialization/json_writer.hpp>
#include <boost/leaf/serialization/nlohmann_writer.hpp>
#include "nlohmann/json.hpp"
struct no_to_json {};
@@ -13,5 +13,6 @@ struct e_no_to_json
};
nlohmann::json j;
boost::leaf::serialization::nlohmann_writer<nlohmann::json> w{j};
e_no_to_json e;
auto x = (boost::leaf::serialization::to_json(j, e), 0);
auto x = (write(w, e), 0);

View File

@@ -1,7 +0,0 @@
// Copyright 2018-2025 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 <boost/leaf/serialization/json_writer.hpp>
#include <boost/leaf/serialization/json_writer.hpp>
int main() { return 0; }

View File

@@ -2,6 +2,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 <boost/leaf/serialization/writer.hpp>
#include <boost/leaf/serialization/writer.hpp>
#include <boost/leaf/serialization/nlohmann_writer.hpp>
#include <boost/leaf/serialization/nlohmann_writer.hpp>
int main() { return 0; }

View File

@@ -10,7 +10,7 @@
# include <boost/leaf/diagnostics.hpp>
# include <boost/leaf/common.hpp>
# include <boost/leaf/on_error.hpp>
# include <boost/leaf/serialization/json_writer.hpp>
# include <boost/leaf/serialization/nlohmann_writer.hpp>
#endif
#include "nlohmann/json.hpp"
@@ -27,17 +27,19 @@
namespace leaf = boost::leaf;
using nlohmann_writer = leaf::serialization::json_writer<nlohmann::ordered_json>;
using output_writer = leaf::serialization::nlohmann_writer<nlohmann::ordered_json>;
namespace boost { namespace leaf {
namespace serialization {
template <class E>
void serialize(writer & w, E const & e, char const * name)
template <class Handle, class E>
void serialize(Handle & h, E const & e, char const * name)
{
if( nlohmann_writer * nw = w.get<nlohmann_writer>() )
write_nested(*nw, e, name);
h.dispatch(
[&](nlohmann_writer<nlohmann::json> & w) { write_nested(w, e, name); },
[&](nlohmann_writer<nlohmann::ordered_json> & w) { write_nested(w, e, name); }
);
}
}
@@ -222,7 +224,7 @@ int main()
[&j](leaf::diagnostic_info const & di, my_error<1> const * e1)
{
BOOST_TEST(e1 != nullptr);
nlohmann_writer w(j);
output_writer w{j};
di.write_to(w);
}
);
@@ -240,7 +242,7 @@ int main()
[&j](leaf::diagnostic_details const & dd, my_error<1> const * e1)
{
BOOST_TEST(e1 != nullptr);
nlohmann_writer w(j);
output_writer w{j};
dd.write_to(w);
}
);
@@ -259,7 +261,7 @@ int main()
[&j](leaf::diagnostic_info const & di, my_error<1> const * e1)
{
BOOST_TEST(e1 != nullptr);
nlohmann_writer w(j);
output_writer w{j};
di.write_to(w);
}
);
@@ -278,7 +280,7 @@ int main()
[&j](leaf::diagnostic_details const & dd, my_error<1> const * e1)
{
BOOST_TEST(e1 != nullptr);
nlohmann_writer w(j);
output_writer w{j};
dd.write_to(w);
}
);
@@ -297,7 +299,7 @@ int main()
[&j](leaf::diagnostic_info const & di, my_error<1> const * e1)
{
BOOST_TEST(e1 != nullptr);
nlohmann_writer w(j);
output_writer w{j};
di.write_to(w);
}
);
@@ -315,7 +317,7 @@ int main()
[&j](leaf::diagnostic_details const & dd, my_error<1> const * e1)
{
BOOST_TEST(e1 != nullptr);
nlohmann_writer w(j);
output_writer w{j};
dd.write_to(w);
}
);
@@ -332,7 +334,7 @@ int main()
},
[&j](leaf::diagnostic_details const & dd, my_exception_ptr *)
{
nlohmann_writer w(j);
output_writer w{j};
dd.write_to(w);
}
);
@@ -354,7 +356,7 @@ int main()
},
[&j](leaf::diagnostic_details const & dd, my_exception_ptr *)
{
nlohmann_writer w(j);
output_writer w{j};
dd.write_to(w);
}
);
@@ -372,7 +374,7 @@ int main()
nlohmann::ordered_json j;
leaf::result<int> r = 42;
BOOST_TEST(r);
nlohmann_writer w(j);
output_writer w{j};
r.write_to(w);
std::cout << __LINE__ << " result<int> success JSON output:\n" << std::setw(2) << j << std::endl;
BOOST_TEST_EQ(j["int"].get<int>(), 42);
@@ -382,7 +384,7 @@ int main()
nlohmann::ordered_json j;
leaf::result<int> r = leaf::new_error();
BOOST_TEST(!r);
nlohmann_writer w(j);
output_writer w{j};
r.write_to(w);
std::cout << __LINE__ << " result<int> error JSON output:\n" << std::setw(2) << j << std::endl;
BOOST_TEST(j["boost::leaf::error_id"].get<int>() > 0);
@@ -397,7 +399,7 @@ int main()
return leaf::new_error(my_error<1>{1, "error one"}, my_error<2>{2, "error two"});
} );
BOOST_TEST(!r);
nlohmann_writer w(j);
output_writer w{j};
r.write_to(w);
std::cout << __LINE__ << " result<int> captured error JSON output:\n" << std::setw(2) << j << std::endl;
BOOST_TEST(j["boost::leaf::error_id"].get<int>() > 0);

View File

@@ -10,11 +10,6 @@
#include <cstring>
#include <cstdint>
#include <unordered_set>
#if __cplusplus >= 201703L
# include <string_view>
#endif
namespace leaf = boost::leaf;
namespace detail = boost::leaf::detail;
@@ -84,38 +79,5 @@ int main()
BOOST_TEST(get_type_name<int>() != get_type_name<float>());
BOOST_TEST(get_type_name<class_>() != get_type_name<struct_>());
BOOST_TEST(get_type_name<class_>() < get_type_name<struct_>()); // "class_" < "struct_"
BOOST_TEST(!(get_type_name<struct_>() < get_type_name<class_>()));
BOOST_TEST(!(get_type_name<int>() < get_type_name<int>())); // not less than itself
BOOST_TEST(get_type_name<int>() < get_type_name<long>()); // "int" < "long"
#if __cplusplus >= 201703L
{
auto sv = to_string_view(get_type_name<int>());
BOOST_TEST(sv == "int");
}
{
auto sv = to_string_view(get_type_name<class_>());
BOOST_TEST(sv == "class_");
}
{
auto sv = to_string_view(get_type_name<leaf_test::struct_>());
BOOST_TEST(sv == "leaf_test::struct_");
}
#endif
BOOST_TEST(std::hash<detail::type_name>()(get_type_name<int>()) == get_type_name<int>().hash);
BOOST_TEST(std::hash<detail::type_name>()(get_type_name<int>()) == std::hash<detail::type_name>()(get_type_name<int>()));
{
std::unordered_set<detail::type_name> s;
s.insert(get_type_name<int>());
s.insert(get_type_name<float>());
s.insert(get_type_name<int>()); // duplicate
BOOST_TEST(s.size() == 2);
BOOST_TEST(s.count(get_type_name<int>()) == 1);
BOOST_TEST(s.count(get_type_name<float>()) == 1);
BOOST_TEST(s.count(get_type_name<double>()) == 0);
}
return boost::report_errors();
}