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

Serialization refactoring

This commit is contained in:
Emil Dotchevski
2026-01-11 13:26:14 -05:00
parent ea2e690d14
commit b72d2f1875
30 changed files with 606 additions and 926 deletions

View File

@@ -1740,85 +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`:
* `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>
void write(E const & e)
{
.... // Serialize e
}
};
----
LEAF does not define a specific API for user-defined writer types; for example, a writer could provide a `write` member function as shown above.
To enable serialization of error objects to custom writer types, define a `serialize` function template in the `boost::leaf::serialization` namespace:
[source,c++]
----
namespace boost { namespace leaf {
namespace serialization {
template <class E>
void serialize(writer & w, E const & e)
{
if( my_writer * mw = w.get<my_writer>() )
mw->write(e);
}
}
} }
----
The `serialize` function receives a polymorphic `writer &` reference. Use the `get<Derived>()` member function to check for a specific writer type. If the cast succeeds (returns non-null), call member functions as needed (e.g. `write`) to serialize the error object. To support multiple output formats, this user-defined `serialize` function should check for each writer type accordingly.
==== JSON Serialization
LEAF provides `json_writer`, a writer class template for JSON serialization:
.#include <boost/leaf/serialization/json_writer.hpp>
[source,c++]
----
namespace boost { namespace leaf {
namespace serialization {
template <class Json>
class json_writer: public writer
{
public:
explicit json_writer(Json &) noexcept;
template <class E>
void write(E const &);
};
}
} }
----
The `json_writer::write` member function serializes error objects by calling `to_json(Json &, E const &)`, found via ADL. These overloads use `operator[](char const *)` and `to_json` to serialize member values.
LEAF provides `to_json` overloads for:
LEAF provides generic `write` overloads for the following types:
* `error_id`
* `e_source_location`
@@ -1827,35 +1754,109 @@ LEAF provides `to_json` overloads for:
* `std::error_condition`
* `std::exception`
* `std::exception_ptr`
* any type with a `.value` member for which `to_json` can be found via ADL
* any type with a `.value` member for which a suitable `write` can be found via ADL
This interface is compatible with https://github.com/nlohmann/json[nlohmann/json]:
[[custom-writers]]
==== Custom Writers
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++]
----
#include <boost/leaf/diagnostics.hpp>
#include <boost/leaf/serialization/json_writer.hpp>
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++]
----
namespace boost { namespace leaf {
namespace serialization {
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 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.
To support multiple output formats, pass multiple functions to `h.dispatch`:
[source,c++]
----
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)
{
if( nlohmann_writer * nw = w.get<nlohmann_writer>() )
nw->write(e);
}
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;
@@ -1900,9 +1901,7 @@ leaf::try_handle_all(
"status": 403,
"message": "Access denied"
},
"e_request_url": {
"value": "/api/admin/settings"
}
"e_request_url": "/api/admin/settings"
}
----
@@ -1912,7 +1911,7 @@ leaf::try_handle_all(
'''
[[tutorial-std_error_code]]
=== Working with `std::error_code`, `std::error_condition`
=== Working with `std::error_code` and `std::error_condition`
==== Introduction
@@ -2868,98 +2867,11 @@ Reference: <<to_variant>>
=== Serialization
[[type_name.hpp]]
==== `type_name.hpp`
[[nlohmann_writer.hpp]]
==== `nlohmann_writer.hpp`
====
.#include <boost/leaf/serialization/type_name.hpp>
[source,c++]
----
namespace boost { namespace leaf {
namespace serialization
{
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 &, type_name const & ) noexcept;
friend bool operator!=( type_name const &, type_name const & ) noexcept;
friend bool operator<( type_name const &, type_name const & ) noexcept;
template <class CharT, class Traits>
friend std::ostream & operator<<( std::basic_ostream<CharT, Traits> &, type_name const & );
#if __cplusplus >= 201703L
friend std::string_view to_string_view( type_name const & ) noexcept;
#endif
#if BOOST_LEAF_CFG_STD_STRING
friend std::string to_string( type_name const & );
#endif
};
template <class T>
type_name get_type_name() noexcept;
} // namespace serialization
} }
namespace std
{
template <> struct hash<boost::leaf::serialization::type_name>;
}
----
[.text-right]
Reference: <<type_name>> | <<get_type_name>>
====
[[writer.hpp]]
==== `writer.hpp`
====
.#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;
};
} // namespace serialization
} }
----
[.text-right]
Reference: <<writer>> | <<serialize>>
====
[[json_writer.hpp]]
==== `json_writer.hpp`
====
.#include <boost/leaf/serialization/json_writer.hpp>
.#include <boost/leaf/serialization/nlohmann_writer.hpp>
[source,c++]
----
namespace boost { namespace leaf {
@@ -2967,49 +2879,24 @@ namespace boost { namespace leaf {
namespace serialization
{
template <class Json>
void to_json( Json &, error_id );
template <class Json>
void to_json( Json &, e_source_location const & );
template <class Json>
void to_json( Json &, e_errno const & );
template <class Json>
void to_json( Json &, std::error_code const & );
template <class Json>
void to_json( Json &, std::error_condition const & );
template <class Json>
void to_json( Json &, std::exception const & );
template <class Json>
void to_json( Json &, std::exception_ptr const & );
// SFINAE: any type E with a .value member for which to_json can be bound via ADL.
template <class Json, class E>
auto to_json( Json &, E const & ) ->
decltype(to_json(std::declval<Json &>(), std::declval<E const &>().value), void());
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>
void write( E const & );
template <class T>
friend void write_nested( nlohmann_writer &, T const &, char const * name );
};
}
} }
----
[.text-right]
Reference: <<to_json>> | <<json_writer>>
Reference: <<nlohmann_writer>>
====
[[functions]]
@@ -3196,27 +3083,6 @@ TIP: See also <<tutorial-exception_to_result>> from the tutorial.
'''
[[get_type_name]]
=== `get_type_name`
.#include <boost/leaf/serialization/type_name.hpp>
[source,c++]
----
namespace boost { namespace leaf {
namespace serialization
{
template <class T>
type_name get_type_name() noexcept;
}
} }
----
Returns a <<type_name>> object representing the type `T`. The type name is extracted automatically in constant time.
'''
[[make_context]]
=== `make_context`
@@ -3330,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 & );
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>>.
'''
@@ -3424,52 +3289,6 @@ NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[_
'''
[[to_json]]
=== `to_json`
.#include <boost/leaf/serialization/json_writer.hpp>
[source,c++]
----
namespace boost { namespace leaf {
namespace serialization
{
template <class Json>
void to_json( Json &, error_id );
template <class Json>
void to_json( Json &, e_source_location const & );
template <class Json>
void to_json( Json &, e_errno const & );
template <class Json>
void to_json( Json &, std::error_code const & );
template <class Json>
void to_json( Json &, std::error_condition const & );
template <class Json>
void to_json( Json &, std::exception const & );
template <class Json>
void to_json( Json &, std::exception_ptr const & );
// SFINAE: any type E with a .value member for which to_json can be bound via ADL.
template <class Json, class E>
auto to_json( Json &, E const & ) ->
decltype(to_json(std::declval<Json &>(), std::declval<E const &>().value), void());
}
} }
----
The `to_json` function is used by <<json_writer>> to serialize error objects to a JSON object. Users can define `to_json` overloads for their own error types, which will be found via ADL. These overloads should use `operator[]` to create JSON object fields and call `to_json` to serialize member values.
NOTE: This interface is compatible with https://github.com/nlohmann/json[nlohmann/json].
'''
[[to_variant]]
=== `to_variant`
@@ -4623,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 {
@@ -4634,25 +4453,25 @@ 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;
// Enabled for types for which a suitable to_json overload
// 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 E>
void write( E const & );
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` member function serializes an error object by calling `<<to_json>>(json, e)`, found via ADL; 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>>.
'''
@@ -5155,106 +4974,6 @@ namespace boost { namespace leaf {
} }
----
'''
[[type_name]]
=== `type_name`
.#include <boost/leaf/serialization/type_name.hpp>
[source,c++]
----
namespace boost { namespace leaf {
namespace serialization
{
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 &, type_name const & ) noexcept;
friend bool operator!=( type_name const &, type_name const & ) noexcept;
friend bool operator<( type_name const &, type_name const & ) noexcept;
template <class CharT, class Traits>
friend std::ostream & operator<<( std::basic_ostream<CharT, Traits> &, type_name const & );
#if __cplusplus >= 201703L
friend std::string_view to_string_view( type_name const & ) noexcept;
#endif
#if BOOST_LEAF_CFG_STD_STRING
friend std::string to_string( type_name const & );
#endif
};
} // namespace serialization
} }
namespace std
{
template <> struct hash<boost::leaf::serialization::type_name>;
}
----
The `type_name` struct represents a type name, extracted automatically in constant time. It contains a pointer to the type name string, its length, and a precomputed hash for efficient comparison.
Two `type_name` objects compare equal if they represent the same type. The `operator<` provides alphabetical ordering. A `std::hash` specialization is provided for use with `std::unordered_map`. See <<get_type_name>>.
The type names are not mangled.
.Example:
[source,c++]
----
namespace app
{
struct my_error { };
}
using leaf::serialization::get_type_name;
assert(to_string_view(get_type_name<int>()) == "int");
assert(to_string_view(get_type_name<app::my_error>()) == "app::my_error");
----
'''
[[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;
};
}
} }
----
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; see <<tutorial-serialization>>.
[[predicates]]
== Reference: Predicates

View File

@@ -452,11 +452,11 @@ code{font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;font-weight:400;co
pre,pre>code{line-height:1.45;color:rgba(255,255,255,.67);font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeLegibility;font-size:1.05em;background-color:#101010}
a:not(pre)>code:hover {color:#00cc99}
kbd{font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
h1 code{color:#00cc99; font-size:113%}
h2 code{color:#00cc99; font-size:113%}
h3 code{color:#00cc99; font-size:113%}
h4 code{color:#00cc99; font-size:113%}
h5 code{color:#00cc99; font-size:113%}
h1 code{color:#00cc99; font-size:104%}
h2 code{color:#00cc99; font-size:104%}
h3 code{color:#00cc99; font-size:104%}
h4 code{color:#00cc99; font-size:104%}
h5 code{color:#00cc99; font-size:104%}
#header>h1:first-child{font-family:"Poiret One";color:#00cc99;margin-top:2.25rem;margin-bottom:0;letter-spacing:-.07em}
#author{color:#a366ff}
#toc ul{font-family:"Quicksand","DejaVu Sans",sans-serif;list-style-type:none}

View File

@@ -446,11 +446,11 @@ code{font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;font-weight:400;co
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeLegibility;font-size:1.05em;background-color:#f7f8f7}
a:not(pre)>code:hover {color:#4101a7}
kbd{font-family:"Anonymous Pro","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
h1 code{color:#4101a7; font-size:113%}
h2 code{color:#4101a7; font-size:113%}
h3 code{color:#4101a7; font-size:113%}
h4 code{color:#4101a7; font-size:113%}
h5 code{color:#4101a7; font-size:113%}
h1 code{color:#4101a7; font-size:104%}
h2 code{color:#4101a7; font-size:104%}
h3 code{color:#4101a7; font-size:104%}
h4 code{color:#4101a7; font-size:104%}
h5 code{color:#4101a7; font-size:104%}
#header>h1:first-child{font-family:"Poiret One";color:#ff5100;margin-top:2.25rem;margin-bottom:0;letter-spacing:-.07em}
#author{color: #4101a7;}
#toc ul{font-family:"Quicksand","DejaVu Sans",sans-serif;list-style-type:none}

View File

@@ -57,6 +57,13 @@ struct e_errno
{
return os << err.value << ", \"" << std::strerror(err.value) << '"';
}
template <class Writer>
friend void write( Writer & w, e_errno const & e )
{
write_nested(w, e.value, "errno");
write_nested(w, std::strerror(e.value), "strerror");
}
};
struct e_type_info_name { char const * value; };

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/diagnostics_writer.hpp>
#include <boost/leaf/detail/diagnostics_writer.hpp>
#include <boost/leaf/error.hpp>
#if !defined(BOOST_LEAF_NO_THREADS) && !defined(NDEBUG)
@@ -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());
}
@@ -376,7 +376,7 @@ public:
template <class CharT, class Traits>
friend std::ostream & operator<<( std::basic_ostream<CharT, Traits> & os, context const & ctx )
{
serialization::diagnostics_writer w(os);
detail::diagnostics_writer w(os);
w.set_prefix("Contents:");
ctx.write_to(w);
return os;

View File

@@ -11,7 +11,5 @@
#include <boost/leaf/on_error.hpp>
#include <boost/leaf/pred.hpp>
#include <boost/leaf/result.hpp>
#include <boost/leaf/serialization/diagnostics_writer.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

@@ -1,12 +1,12 @@
#ifndef BOOST_LEAF_SERIALIZATION_DIAGNOSTICS_WRITER_HPP_INCLUDED
#define BOOST_LEAF_SERIALIZATION_DIAGNOSTICS_WRITER_HPP_INCLUDED
#ifndef BOOST_LEAF_DETAIL_DIAGNOSTICS_WRITER_HPP_INCLUDED
#define BOOST_LEAF_DETAIL_DIAGNOSTICS_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 <boost/leaf/detail/writer.hpp>
#include <boost/leaf/detail/exception_base.hpp>
#include <type_traits>
@@ -28,7 +28,7 @@ struct show_in_diagnostics: std::integral_constant<bool, BOOST_LEAF_CFG_DIAGNOST
{
};
namespace serialization
namespace detail
{
template <class T, class E = void>
struct is_printable: std::false_type
@@ -50,16 +50,6 @@ 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
{
};
////////////////////////////////////////
class diagnostics_writer: public writer
@@ -79,7 +69,7 @@ namespace serialization
BOOST_LEAF_ASSERT(delimiter);
char const * p = prefix;
prefix = nullptr;
os << (p ? p : delimiter) << get_type_name<T>();
os << (p ? p : delimiter) << detail::get_type_name<T>();
}
template <class T, class PrintableInfo, class CharT, class Traits>
@@ -171,10 +161,10 @@ namespace serialization
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
@@ -245,8 +235,8 @@ namespace serialization
}
};
} // namespace serialization
} // namespace detail
} } // namespace boost::leaf
#endif // #ifndef BOOST_LEAF_SERIALIZATION_DIAGNOSTICS_WRITER_HPP_INCLUDED
#endif // #ifndef BOOST_LEAF_DETAIL_DIAGNOSTICS_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/serialization/type_name.hpp>
namespace boost { namespace leaf {
@@ -14,11 +13,13 @@ class error_id;
namespace detail
{
struct type_name;
class exception_base
{
public:
virtual error_id get_error_id() const noexcept = 0;
virtual serialization::type_name get_type_name() const = 0;
virtual type_name get_type_name() const = 0;
protected:
exception_base() noexcept { }
~exception_base() noexcept { }

View File

@@ -0,0 +1,57 @@
#ifndef BOOST_LEAF_DETAIL_TYPE_NAME_HPP_INCLUDED
#define BOOST_LEAF_DETAIL_TYPE_NAME_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/detail/demangle.hpp>
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
{
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()
{
n::r parsed = n::p<T>();
return { parsed.name_not_zero_terminated_at_length, parsed.length, parsed.hash };
}
} // namespace detail
} } // namespace boost::leaf
#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,14 +39,15 @@ 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>
friend std::ostream & operator<<( std::basic_ostream<CharT, Traits> & os, diagnostic_info const & x )
{
serialization::diagnostics_writer w(os, x.error(), x.source_location(), x.exception());
detail::diagnostics_writer w(os, x.error(), x.source_location(), x.exception());
#if BOOST_LEAF_CFG_DIAGNOSTICS
x.write_to_(w);
#else
@@ -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,14 +111,16 @@ 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>
friend std::ostream & operator<<( std::basic_ostream<CharT, Traits> & os, diagnostic_details const & x )
{
serialization::diagnostics_writer w(os, x.error(), x.source_location(), x.exception());
detail::diagnostics_writer w(os, x.error(), x.source_location(), x.exception());
#if BOOST_LEAF_CFG_DIAGNOSTICS
x.diagnostic_info::write_to_(w);
w.set_prefix("\nDiagnostic details:" BOOST_LEAF_CFG_DIAGNOSTICS_FIRST_DELIMITER);
@@ -170,13 +174,15 @@ 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>
friend std::ostream & operator<<( std::basic_ostream<CharT, Traits> & os, diagnostic_details const & x )
{
serialization::diagnostics_writer w(os, x.error(), x.source_location(), x.exception());
detail::diagnostics_writer w(os, x.error(), x.source_location(), x.exception());
#if BOOST_LEAF_CFG_DIAGNOSTICS
x.diagnostic_info::write_to_(w);
os << "\nboost::leaf::diagnostic_details N/A due to BOOST_LEAF_CFG_CAPTURE=0";

View File

@@ -9,12 +9,41 @@
#include <boost/leaf/detail/optional.hpp>
#include <boost/leaf/detail/function_traits.hpp>
#include <boost/leaf/detail/capture_list.hpp>
#include <boost/leaf/serialization/diagnostics_writer.hpp>
#include <boost/leaf/detail/diagnostics_writer.hpp>
////////////////////////////////////////
#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR
# include <system_error>
#include <system_error>
namespace boost { namespace leaf {
namespace serialization
{
template <class Writer>
void write(Writer & w, std::error_code const & ec)
{
write_nested(w, ec.category().name(), "category");
write_nested(w, ec.value(), "value");
write_nested(w, ec.message(), "message");
}
template <class Writer>
void write(Writer & w, std::error_condition const & ec)
{
write_nested(w, ec.category().name(), "category");
write_nested(w, ec.value(), "value");
write_nested(w, ec.message(), "message");
}
}
} }
#endif
////////////////////////////////////////
#define BOOST_LEAF_TOKEN_PASTE(x, y) x ## y
#define BOOST_LEAF_TOKEN_PASTE2(x, y) BOOST_LEAF_TOKEN_PASTE(x, y)
#define BOOST_LEAF_TMP BOOST_LEAF_TOKEN_PASTE2(boost_leaf_tmp_, __LINE__)
@@ -32,26 +61,26 @@
#if BOOST_LEAF_CFG_GNUC_STMTEXPR
#define BOOST_LEAF_CHECK(r)\
({\
auto && BOOST_LEAF_TMP = (r);\
static_assert(::boost::leaf::is_result_type<typename std::decay<decltype(BOOST_LEAF_TMP)>::type>::value,\
"BOOST_LEAF_CHECK requires a result object (see is_result_type)");\
if( !BOOST_LEAF_TMP )\
return BOOST_LEAF_TMP.error();\
std::move(BOOST_LEAF_TMP);\
}).value()
# define BOOST_LEAF_CHECK(r)\
({\
auto && BOOST_LEAF_TMP = (r);\
static_assert(::boost::leaf::is_result_type<typename std::decay<decltype(BOOST_LEAF_TMP)>::type>::value,\
"BOOST_LEAF_CHECK requires a result object (see is_result_type)");\
if( !BOOST_LEAF_TMP )\
return BOOST_LEAF_TMP.error();\
std::move(BOOST_LEAF_TMP);\
}).value()
#else // #if BOOST_LEAF_CFG_GNUC_STMTEXPR
#define BOOST_LEAF_CHECK(r)\
{\
auto && BOOST_LEAF_TMP = (r);\
static_assert(::boost::leaf::is_result_type<typename std::decay<decltype(BOOST_LEAF_TMP)>::type>::value,\
"BOOST_LEAF_CHECK requires a result object (see is_result_type)");\
if( !BOOST_LEAF_TMP )\
return BOOST_LEAF_TMP.error();\
}
# define BOOST_LEAF_CHECK(r)\
{\
auto && BOOST_LEAF_TMP = (r);\
static_assert(::boost::leaf::is_result_type<typename std::decay<decltype(BOOST_LEAF_TMP)>::type>::value,\
"BOOST_LEAF_CHECK requires a result object (see is_result_type)");\
if( !BOOST_LEAF_TMP )\
return BOOST_LEAF_TMP.error();\
}
#endif // #else (#if BOOST_LEAF_CFG_GNUC_STMTEXPR)
@@ -70,6 +99,14 @@ struct e_source_location
{
return os << x.file << '(' << x.line << ") in function " << x.function;
}
template <class Writer>
friend void write( Writer & w, e_source_location const & x )
{
write_nested(w, x.file, "file");
write_nested(w, x.line, "line");
write_nested(w, x.function, "function");
}
};
template <>
@@ -79,16 +116,32 @@ struct show_in_diagnostics<e_source_location>: std::false_type
////////////////////////////////////////
namespace serialization
{
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 Writer, class E>
void serialize_(Writer & w, E const & e)
template <class T>
void serialize_(diagnostics_writer & w, T const & x)
{
w.write(x);
}
template <class T>
void serialize_(writer & w, T const & x)
{
using namespace serialization;
typename dependent_writer<Writer>::type & wr = w;
serialize(wr, e);
if( diagnostics_writer * dw = w.template get<diagnostics_writer>() )
dw->write(e);
char zstr[1024];
serialize(w, x, to_zstr(zstr, get_type_name<T>()));
if( diagnostics_writer * dw = w.get<diagnostics_writer>() )
dw->write(x);
}
}
@@ -148,9 +201,10 @@ namespace detail
void unload( int err_id ) noexcept(!BOOST_LEAF_CFG_CAPTURE);
template <class ErrorID>
void write_to(serialization::writer & w, ErrorID id) const
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 )
@@ -217,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);
}
@@ -252,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_;
@@ -428,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
@@ -784,6 +838,12 @@ public:
return os << (x.value_ / 4);
}
template <class Writer>
friend void write( Writer & w, error_id x )
{
write(w, x.value_ / 4);
}
BOOST_LEAF_CONSTEXPR void load_source_location_( char const * file, int line, char const * function ) const noexcept(!BOOST_LEAF_CFG_CAPTURE)
{
BOOST_LEAF_ASSERT(file&&*file);

View File

@@ -9,6 +9,59 @@
#include <boost/leaf/error.hpp>
#include <boost/leaf/detail/exception_base.hpp>
#ifndef BOOST_LEAF_NO_EXCEPTIONS
# include <typeinfo>
#endif
namespace boost { namespace leaf {
namespace serialization
{
template <class Writer>
void write(Writer & w, std::exception const & ex)
{
char const dynamic_type[] = "dynamic_type";
char const what[] = "what";
#ifdef BOOST_LEAF_NO_EXCEPTIONS
write_nested(w, "<<unknown>>", dynamic_type);
#else
write_nested(w, detail::demangler(typeid(ex).name()).get(), dynamic_type);
#endif
if( char const * wh = ex.what() )
write_nested(w, wh, what);
else
write_nested(w, "<<nullptr>>", what);
}
template <class Writer>
void write(Writer & w, std::exception_ptr const & ep)
{
if( ep )
{
#ifndef BOOST_LEAF_NO_EXCEPTIONS
try
{
std::rethrow_exception(ep);
}
catch( std::exception const & ex )
{
write(w, ex);
return;
}
catch( ... )
{
}
#endif
write_nested(w, "<<unknown>>", "dynamic_type");
}
else
write_nested(w, "<<empty>>", "dynamic_type");
write_nested(w, "N/A", "what");
}
}
} }
////////////////////////////////////////
#define BOOST_LEAF_THROW_EXCEPTION ::boost::leaf::detail::throw_with_loc{__FILE__,__LINE__,__FUNCTION__}+::boost::leaf::detail::make_exception
@@ -57,9 +110,9 @@ namespace detail
return *this;
}
serialization::type_name get_type_name() const override
detail::type_name get_type_name() const override
{
return serialization::get_type_name<Ex>();
return detail::get_type_name<Ex>();
}
public:

View File

@@ -7,7 +7,7 @@
#include <boost/leaf/config.hpp>
#include <boost/leaf/context.hpp>
#include <boost/leaf/serialization/diagnostics_writer.hpp>
#include <boost/leaf/detail/diagnostics_writer.hpp>
namespace boost { namespace leaf {
@@ -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,17 +90,14 @@ 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>
friend std::ostream & operator<<(std::basic_ostream<CharT, Traits> & os, error_info const & x)
{
serialization::diagnostics_writer w(os, x.error(), x.source_location(), x.exception());
detail::diagnostics_writer w(os, x.error(), x.source_location(), x.exception());
return os;
}
}; // class error_info
@@ -804,6 +812,10 @@ try_capture_all( TryBlock && try_block ) noexcept
} } // namespace boost::leaf
////////////////////////////////////////
#ifndef BOOST_LEAF_NO_EXCEPTIONS
// Boost Exception Integration
namespace boost { class exception; }
@@ -866,4 +878,6 @@ namespace detail
} } // namespace boost::leaf
#endif
#endif // #ifndef BOOST_LEAF_HANDLE_ERRORS_HPP_INCLUDED

View File

@@ -7,7 +7,7 @@
#include <boost/leaf/config.hpp>
#include <boost/leaf/exception.hpp>
#include <boost/leaf/serialization/diagnostics_writer.hpp>
#include <boost/leaf/detail/diagnostics_writer.hpp>
#include <boost/leaf/detail/capture_list.hpp>
#include <functional>
@@ -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,16 +579,17 @@ 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>
friend std::ostream & operator<<( std::basic_ostream<CharT, Traits> & os, result const & r )
{
serialization::diagnostics_writer w(os);
detail::diagnostics_writer w(os);
w.set_prefix(": ");
if( r )
{
@@ -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>
@@ -705,7 +712,7 @@ public:
os << "Success";
else
{
serialization::diagnostics_writer w(os);
detail::diagnostics_writer w(os);
w.set_prefix(": ");
os << "Failure";
r.print_error(w);

View File

@@ -1,160 +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 <boost/leaf/detail/exception_base.hpp>
#include <boost/leaf/error.hpp>
#include <boost/leaf/common.hpp>
#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR
# include <system_error>
#endif
#ifndef BOOST_LEAF_NO_EXCEPTIONS
# include <typeinfo>
#endif
namespace boost { namespace leaf {
namespace serialization
{
template <class Json>
void to_json(Json & j, error_id x)
{
to_json(j, x.value() / 4);
}
template <class Json>
void to_json(Json & j, e_source_location const & x)
{
to_json(j["file"], x.file);
to_json(j["line"], x.line);
to_json(j["function"], x.function);
}
template <class Json>
void to_json(Json & j, e_errno const & e)
{
to_json(j["value"], e.value);
to_json(j["message"], std::strerror(e.value));
}
#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR
template <class Json>
void to_json(Json & j, std::error_code const & ec)
{
to_json(j["category"], ec.category().name());
to_json(j["value"], ec.value());
to_json(j["message"], ec.message());
}
template <class Json>
void to_json(Json & j, std::error_condition const & ec)
{
to_json(j["category"], ec.category().name());
to_json(j["value"], ec.value());
to_json(j["message"], ec.message());
}
#endif
template <class Json>
void to_json(Json & j, detail::exception_base const & eb)
{
char zstr[1024];
to_json(j["type"], to_zstr(zstr, eb.get_type_name()));
char const * what = "N/A";
#ifndef BOOST_LEAF_NO_EXCEPTIONS
if( std::exception const * ex = dynamic_cast<std::exception const *>(&eb) )
what = ex->what();
#endif
to_json(j["what"], what ? what : "<<nullptr>>");
}
#ifndef BOOST_LEAF_NO_EXCEPTIONS
template <class Json>
void to_json(Json & j, std::exception const & ex)
{
if( detail::exception_base const * eb = dynamic_cast<detail::exception_base const *>(&ex) )
{
char zstr[1024];
to_json(j["type"], to_zstr(zstr, eb->get_type_name()));
}
else
to_json(j["type"], detail::demangler(typeid(ex).name()).get());
if( char const * w = ex.what() )
to_json(j["what"], w);
else
to_json(j["what"], "<<nullptr>>");
}
#endif
template <class Json>
void to_json(Json & j, std::exception_ptr const & ep)
{
if( ep )
{
#ifndef BOOST_LEAF_NO_EXCEPTIONS
try
{
std::rethrow_exception(ep);
}
catch( detail::exception_base const & eb )
{
to_json(j, eb);
return;
}
catch( std::exception const & ex )
{
to_json(j, ex);
return;
}
catch( ... )
{
}
#endif
to_json(j["type"], "<<unknown>>");
}
else
to_json(j["type"], "<<empty>>");
to_json(j["what"], "N/A");
}
template <class Json, class E>
auto to_json(Json & j, E const & e) -> decltype(to_json(j, e.value), void())
{
to_json(j["value"], e.value);
}
////////////////////////////////////////
template <class Json>
class json_writer: public writer
{
Json & j_;
public:
explicit json_writer(Json & j) noexcept:
writer(this),
j_(j)
{
}
template <class E>
void write(E const & e)
{
char zstr[1024];
to_json(j_[to_zstr(zstr, get_type_name<E>())], 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,102 +0,0 @@
#ifndef BOOST_LEAF_SERIALIZATION_TYPE_NAME_HPP_INCLUDED
#define BOOST_LEAF_SERIALIZATION_TYPE_NAME_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/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 serialization
{
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
{
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);
}
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 serialization
} } // namespace boost::leaf
namespace std
{
template <>
struct hash<boost::leaf::serialization::type_name>
{
std::size_t operator()(boost::leaf::serialization::type_name const & x) const noexcept
{
return x.hash;
}
};
} // namespace std
#endif // #ifndef BOOST_LEAF_SERIALIZATION_TYPE_NAME_HPP_INCLUDED

View File

@@ -1,61 +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/serialization/type_name.hpp>
#include <type_traits>
namespace boost { namespace leaf {
namespace serialization
{
class writer
{
type_name const type_;
protected:
template <class Derived>
explicit writer(Derived * d) noexcept:
type_(get_type_name<Derived>())
{
BOOST_LEAF_ASSERT(d == this), (void) d;
}
~writer() noexcept
{
}
public:
template <class Derived>
Derived * get() noexcept
{
return type_ == get_type_name<typename std::decay<Derived>::type>() ? static_cast<Derived *>(this) : nullptr;
}
};
template <class W, class E>
typename std::enable_if<std::is_base_of<writer, W>::value>::type
serialize(W &, E const &)
{
}
}
namespace detail
{
template <class>
struct dependent_writer
{
using type = serialization::writer;
};
}
} } // namespace boost::leaf
#endif // #ifndef BOOST_LEAF_SERIALIZATION_WRITER_HPP_INCLUDED

View File

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

View File

@@ -44,10 +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_diagnostics_writer_test.cpp ;
compile _hpp_serialization_json_writer_test.cpp ;
compile _hpp_serialization_writer_test.cpp ;
compile _hpp_serialization_type_name_test.cpp ;
compile _hpp_serialization_nlohmann_writer_test.cpp ;
compile _hpp_to_variant_test.cpp ;
run boost_exception_test.cpp ;
@@ -146,8 +143,8 @@ lib so_dll_static_lib1 : so_dll_lib1.cpp : <link>static <define>BOOST_LEAF_SO_DL
lib so_dll_static_lib2 : so_dll_lib2.cpp : <link>static <define>BOOST_LEAF_SO_DLL_TEST_STATIC <target-os>windows:<define>BOOST_LEAF_CFG_WIN32=2 ;
run so_dll_test.cpp so_dll_static_lib1 so_dll_static_lib2 : : : <define>BOOST_LEAF_SO_DLL_TEST_STATIC <target-os>windows:<define>BOOST_LEAF_CFG_WIN32=2 : so_dll_static_test ;
compile-fail _compile-fail-arg_boost_error_info_1.cpp ;
compile-fail _compile-fail-arg_boost_error_info_2.cpp ;
compile-fail _compile-fail-arg_boost_error_info_1.cpp : <exception-handling>off:<build>no ;
compile-fail _compile-fail-arg_boost_error_info_2.cpp : <exception-handling>off:<build>no ;
compile-fail _compile-fail-arg_catch_1.cpp ;
compile-fail _compile-fail-arg_catch_2.cpp ;
compile-fail _compile-fail-arg_match_1.cpp ;
@@ -162,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/diagnostics_writer.hpp>
#include <boost/leaf/serialization/diagnostics_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/json_writer.hpp>
#include <boost/leaf/serialization/json_writer.hpp>
#include <boost/leaf/serialization/nlohmann_writer.hpp>
#include <boost/leaf/serialization/nlohmann_writer.hpp>
int main() { return 0; }

View File

@@ -1,7 +0,0 @@
// 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/serialization/type_name.hpp>
#include <boost/leaf/serialization/type_name.hpp>
int main() { return 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/writer.hpp>
#include <boost/leaf/serialization/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"
@@ -23,19 +23,23 @@
#include "lightweight_test.hpp"
#if BOOST_LEAF_CFG_STD_STRING
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)
template <class Handle, class E>
void serialize(Handle & h, E const & e, char const * name)
{
if( nlohmann_writer * nw = w.get<nlohmann_writer>() )
nw->write(e);
h.dispatch(
[&](nlohmann_writer<nlohmann::json> & w) { write_nested(w, e, name); },
[&](nlohmann_writer<nlohmann::ordered_json> & w) { write_nested(w, e, name); }
);
}
}
@@ -49,6 +53,11 @@ struct my_exception_ptr
std::exception_ptr value;
};
struct my_error_code
{
std::error_code value;
};
template <int N>
struct my_error
{
@@ -74,6 +83,7 @@ leaf::result<void> fail()
#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR
std::make_error_code(std::errc::invalid_argument),
std::make_error_condition(std::errc::io_error),
my_error_code{std::make_error_code(std::errc::invalid_argument)},
#endif
42,
my_error<1>{1, "error one"},
@@ -89,6 +99,7 @@ void leaf_throw()
#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR
std::make_error_code(std::errc::invalid_argument),
std::make_error_condition(std::errc::io_error),
my_error_code{std::make_error_code(std::errc::invalid_argument)},
#endif
42,
my_error<1>{1, "error one"},
@@ -103,6 +114,7 @@ void throw_()
#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR
std::make_error_code(std::errc::invalid_argument),
std::make_error_condition(std::errc::io_error),
my_error_code{std::make_error_code(std::errc::invalid_argument)},
#endif
42,
my_error<1>{1, "error one"},
@@ -157,10 +169,10 @@ void check_diagnostic_details(nlohmann::ordered_json const & j, bool has_source_
BOOST_TEST_EQ(e2j["message"].get<std::string>(), "error two");
auto const & ej = j["boost::leaf::e_errno"];
BOOST_TEST_EQ(ej["value"].get<int>(), ENOENT);
BOOST_TEST(!ej["message"].get<std::string>().empty());
BOOST_TEST_EQ(ej["errno"].get<int>(), ENOENT);
BOOST_TEST(!ej["strerror"].get<std::string>().empty());
BOOST_TEST_EQ(j["boost::leaf::e_api_function"]["value"].get<std::string>(), "my_api_function");
BOOST_TEST_EQ(j["boost::leaf::e_api_function"].get<std::string>(), "my_api_function");
if( BOOST_LEAF_CFG_STD_SYSTEM_ERROR )
{
@@ -173,6 +185,11 @@ void check_diagnostic_details(nlohmann::ordered_json const & j, bool has_source_
BOOST_TEST_EQ(econdj["value"].get<int>(), static_cast<int>(std::errc::io_error));
BOOST_TEST(!econdj["category"].get<std::string>().empty());
BOOST_TEST(!econdj["message"].get<std::string>().empty());
auto const & mecj = j["my_error_code"];
BOOST_TEST_EQ(mecj["value"].get<int>(), static_cast<int>(std::errc::invalid_argument));
BOOST_TEST(!mecj["category"].get<std::string>().empty());
BOOST_TEST(!mecj["message"].get<std::string>().empty());
}
}
else
@@ -190,7 +207,7 @@ void check_diagnostic_details(nlohmann::ordered_json const & j, bool has_source_
void check_exception(nlohmann::ordered_json const & j)
{
auto const & exj = j["std::exception"];
BOOST_TEST(!exj["type"].get<std::string>().empty());
BOOST_TEST(!exj["dynamic_type"].get<std::string>().empty());
BOOST_TEST(!exj["what"].get<std::string>().empty());
}
#endif
@@ -207,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);
}
);
@@ -225,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);
}
);
@@ -244,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);
}
);
@@ -263,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);
}
);
@@ -282,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);
}
);
@@ -300,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);
}
);
@@ -317,14 +334,14 @@ int main()
},
[&j](leaf::diagnostic_details const & dd, my_exception_ptr *)
{
nlohmann_writer w(j);
output_writer w{j};
dd.write_to(w);
}
);
std::cout << __LINE__ << " std::exception_ptr JSON output:\n" << std::setw(2) << j << std::endl;
auto const & ep = j["my_exception_ptr"]["value"];
std::string type = ep["type"].get<std::string>();
auto const & ep = j["my_exception_ptr"];
std::string type = ep["dynamic_type"].get<std::string>();
std::string what = ep["what"].get<std::string>();
BOOST_TEST(type.find("std::runtime_error") != std::string::npos);
BOOST_TEST_EQ(what, "test exception");
@@ -339,14 +356,14 @@ int main()
},
[&j](leaf::diagnostic_details const & dd, my_exception_ptr *)
{
nlohmann_writer w(j);
output_writer w{j};
dd.write_to(w);
}
);
std::cout << __LINE__ << " non-std::exception_ptr JSON output:\n" << std::setw(2) << j << std::endl;
auto const & ep = j["my_exception_ptr"]["value"];
std::string type = ep["type"].get<std::string>();
auto const & ep = j["my_exception_ptr"];
std::string type = ep["dynamic_type"].get<std::string>();
std::string what = ep["what"].get<std::string>();
BOOST_TEST_EQ(type, "<<unknown>>");
BOOST_TEST_EQ(what, "N/A");
@@ -357,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);
@@ -367,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);
@@ -382,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);
@@ -397,3 +414,12 @@ int main()
return boost::report_errors();
}
#else
int main()
{
return 0;
}
#endif

View File

@@ -19,7 +19,7 @@ int main()
#ifdef BOOST_LEAF_TEST_SINGLE_HEADER
# include "leaf.hpp"
#else
# include <boost/leaf/serialization/diagnostics_writer.hpp>
# include <boost/leaf/detail/diagnostics_writer.hpp>
#endif
#include <sstream>
@@ -72,7 +72,7 @@ template <int Line, class T>
std::string print(T const & x, char const * prefix, char const * delimiter)
{
std::ostringstream s;
leaf::serialization::diagnostics_writer w(s);
leaf::detail::diagnostics_writer w(s);
w.set_prefix(prefix);
w.set_delimiter(delimiter);
w.write(x);

View File

@@ -5,19 +5,14 @@
#ifdef BOOST_LEAF_TEST_SINGLE_HEADER
# include "leaf.hpp"
#else
# include <boost/leaf/serialization/type_name.hpp>
# include <boost/leaf/detail/type_name.hpp>
#endif
#include <cstring>
#include <cstdint>
#include <unordered_set>
#if __cplusplus >= 201703L
# include <string_view>
#endif
namespace leaf = boost::leaf;
namespace serialization = boost::leaf::serialization;
namespace detail = boost::leaf::detail;
#include "lightweight_test.hpp"
@@ -44,7 +39,7 @@ template <int> struct struct_template1 { };
template <class> class class_template2 { };
template <class> struct struct_template2 { };
bool test(serialization::type_name const & tn, char const * correct)
bool test(detail::type_name const & tn, char const * correct)
{
return
std::strlen(correct) == tn.length &&
@@ -53,7 +48,7 @@ bool test(serialization::type_name const & tn, char const * correct)
int main()
{
using serialization::get_type_name;
using leaf::detail::get_type_name;
BOOST_TEST(test(get_type_name<leaf::in_namespace_boost_leaf>(), "boost::leaf::in_namespace_boost_leaf"));
@@ -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<serialization::type_name>()(get_type_name<int>()) == get_type_name<int>().hash);
BOOST_TEST(std::hash<serialization::type_name>()(get_type_name<int>()) == std::hash<serialization::type_name>()(get_type_name<int>()));
{
std::unordered_set<serialization::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();
}