2
0
mirror of https://github.com/boostorg/leaf.git synced 2026-02-17 13:52:14 +00:00

Better SFINAE in JSON encoders

This commit is contained in:
Emil Dotchevski
2026-02-01 13:56:15 -05:00
parent 48a7f72301
commit 710797a4a7
5 changed files with 49 additions and 24 deletions

View File

@@ -2873,8 +2873,9 @@ namespace serialization
{
boost::json::value & v_;
// Enabled if x is assignable to boost::json::value, or
// if tag_invoke is defined for boost::json::value_from_tag.
// Uses unspecified SFINAE expression designed to select this overload
// only if no other compatible overload is found.
// Implemented in terms of boost::json::value_from.
template <class T>
friend void output( boost_json_encoder &, T const & x );
@@ -2906,7 +2907,9 @@ namespace serialization
{
Json & j_;
// Enabled if to_json is available for Json and T.
// Uses unspecified SFINAE expression designed to select this overload
// only if no other compatible overload is found.
// Implemented in terms of to_json.
template <class T>
friend void output( nlohmann_json_encoder &, T const & x );
@@ -3667,8 +3670,9 @@ namespace serialization
{
boost::json::value & v_;
// Enabled if x is assignable to boost::json::value, or
// if tag_invoke is defined for boost::json::value_from_tag.
// Uses unspecified SFINAE expression designed to select this overload
// only if no other compatible overload is found.
// Implemented in terms of boost::json::value_from.
template <class T>
friend void output( boost_json_encoder &, T const & x );
@@ -3680,10 +3684,7 @@ namespace serialization
} }
----
The `boost_json_encoder` type serializes error objects to JSON format using https://www.boost.org/doc/libs/release/libs/json/[Boost.JSON]. The `output` function is enabled for:
* Types directly assignable to `boost::json::value`
* Types for which a `tag_invoke` overload for `value_from_tag` can be found via ADL
The `boost_json_encoder` type serializes error objects to JSON format using https://www.boost.org/doc/libs/release/libs/json/[Boost.JSON]. The `output` function is implemented in terms of `boost::json::value_from`.
See <<tutorial-serialization>>.
@@ -4489,7 +4490,9 @@ namespace serialization
{
Json & j_;
// Enabled if to_json is available for Json and T.
// Uses unspecified SFINAE expression designed to select this overload
// only if no other compatible overload is found.
// Implemented in terms of to_json.
template <class T>
friend void output( nlohmann_json_encoder &, T const & x );

View File

@@ -8,6 +8,8 @@
#include <boost/leaf/config.hpp>
#include <boost/leaf/detail/type_name.hpp>
#include <boost/leaf/detail/function_traits.hpp>
#include <type_traits>
#include <utility>
namespace boost { namespace leaf {
@@ -16,7 +18,10 @@ namespace serialization
struct encoder_adl {};
template <class Encoder, class T>
auto output(Encoder & e, T const & x) -> decltype(output(e, x.value))
typename std::enable_if<
sizeof(T) == sizeof(decltype(std::declval<T const &>().value)),
decltype(output(std::declval<Encoder &>(), std::declval<T const &>().value), void())>::type
output(Encoder & e, T const & x)
{
output(e, x.value);
}

View File

@@ -5,12 +5,15 @@
// 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 <type_traits>
#include <utility>
namespace boost { namespace json {
class value;
struct value_from_tag;
template <class T>
void value_from(T &&, value &);
} }
@@ -18,21 +21,16 @@ namespace boost { namespace leaf {
namespace serialization
{
template <class Value = boost::json::value, class ValueFromTag = boost::json::value_from_tag>
template <class Value = boost::json::value>
struct boost_json_encoder_
{
Value & v_;
template <class T>
friend auto output(boost_json_encoder_ & e, T const & x) -> decltype(std::declval<Value &>() = x, void())
template <class Encoder, class T, class... Deprioritize>
friend typename std::enable_if<std::is_same<Encoder, boost_json_encoder_>::value>::type
output(Encoder & e, T const & x, Deprioritize...)
{
e.v_ = x;
}
template <class T>
friend auto output(boost_json_encoder_ & e, T const & x) -> decltype(tag_invoke(std::declval<ValueFromTag>(), std::declval<Value &>(), x), void())
{
tag_invoke(ValueFromTag{}, e.v_, x);
boost::json::value_from(x, e.v_);
}
template <class T>

View File

@@ -5,6 +5,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 <type_traits>
#include <utility>
namespace boost { namespace leaf {
@@ -16,10 +17,12 @@ namespace serialization
{
Json & j_;
template <class T>
friend auto output(nlohmann_json_encoder & e, T const & x) -> decltype(to_json(std::declval<Json &>(), x), void())
template <class Encoder, class T, class... Deprioritize>
friend typename std::enable_if<std::is_same<Encoder, nlohmann_json_encoder>::value, Json *>::type
output(Encoder & e, T const & x, Deprioritize...)
{
to_json(e.j_, x);
return 0;
}
template <class T>

View File

@@ -17,6 +17,7 @@
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <vector>
#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR
# include <system_error>
#endif
@@ -76,6 +77,11 @@ struct my_error
}
};
struct my_error_with_vector
{
std::vector<int> value;
};
leaf::result<void> fail()
{
return BOOST_LEAF_NEW_ERROR(
@@ -87,6 +93,7 @@ leaf::result<void> fail()
42,
my_error<1>{1, "error one"},
my_error<2>{2, "error two"},
my_error_with_vector{{10, 20, 30}},
leaf::e_errno{ENOENT},
leaf::e_api_function{"my_api_function"} );
}
@@ -103,6 +110,7 @@ void leaf_throw()
42,
my_error<1>{1, "error one"},
my_error<2>{2, "error two"},
my_error_with_vector{{10, 20, 30}},
leaf::e_errno{ENOENT},
leaf::e_api_function{"my_api_function"} );
}
@@ -118,6 +126,7 @@ void throw_()
42,
my_error<1>{1, "error one"},
my_error<2>{2, "error two"},
my_error_with_vector{{10, 20, 30}},
leaf::e_errno{ENOENT},
leaf::e_api_function{"my_api_function"} );
throw my_exception{};
@@ -167,6 +176,13 @@ void check_diagnostic_details(boost::json::value const & j, bool has_source_loca
BOOST_TEST_EQ(boost::json::value_to<int>(e2j.at("code")), 2);
BOOST_TEST_EQ(boost::json::value_to<std::string>(e2j.at("message")), "error two");
auto const & vj = j.at("my_error_with_vector");
BOOST_TEST(vj.is_array());
BOOST_TEST_EQ(vj.as_array().size(), 3);
BOOST_TEST_EQ(boost::json::value_to<int>(vj.as_array()[0]), 10);
BOOST_TEST_EQ(boost::json::value_to<int>(vj.as_array()[1]), 20);
BOOST_TEST_EQ(boost::json::value_to<int>(vj.as_array()[2]), 30);
auto const & ej = j.at("boost::leaf::e_errno");
BOOST_TEST_EQ(boost::json::value_to<int>(ej.at("errno")), ENOENT);
BOOST_TEST(!boost::json::value_to<std::string>(ej.at("strerror")).empty());