Skip to content

Commit 710797a

Browse files
committed
Better SFINAE in JSON encoders
1 parent 48a7f72 commit 710797a

File tree

5 files changed

+49
-24
lines changed

5 files changed

+49
-24
lines changed

doc/leaf.adoc

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2873,8 +2873,9 @@ namespace serialization
28732873
{
28742874
boost::json::value & v_;
28752875
2876-
// Enabled if x is assignable to boost::json::value, or
2877-
// if tag_invoke is defined for boost::json::value_from_tag.
2876+
// Uses unspecified SFINAE expression designed to select this overload
2877+
// only if no other compatible overload is found.
2878+
// Implemented in terms of boost::json::value_from.
28782879
template <class T>
28792880
friend void output( boost_json_encoder &, T const & x );
28802881
@@ -2906,7 +2907,9 @@ namespace serialization
29062907
{
29072908
Json & j_;
29082909
2909-
// Enabled if to_json is available for Json and T.
2910+
// Uses unspecified SFINAE expression designed to select this overload
2911+
// only if no other compatible overload is found.
2912+
// Implemented in terms of to_json.
29102913
template <class T>
29112914
friend void output( nlohmann_json_encoder &, T const & x );
29122915
@@ -3667,8 +3670,9 @@ namespace serialization
36673670
{
36683671
boost::json::value & v_;
36693672
3670-
// Enabled if x is assignable to boost::json::value, or
3671-
// if tag_invoke is defined for boost::json::value_from_tag.
3673+
// Uses unspecified SFINAE expression designed to select this overload
3674+
// only if no other compatible overload is found.
3675+
// Implemented in terms of boost::json::value_from.
36723676
template <class T>
36733677
friend void output( boost_json_encoder &, T const & x );
36743678
@@ -3680,10 +3684,7 @@ namespace serialization
36803684
} }
36813685
----
36823686

3683-
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:
3684-
3685-
* Types directly assignable to `boost::json::value`
3686-
* Types for which a `tag_invoke` overload for `value_from_tag` can be found via ADL
3687+
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`.
36873688

36883689
See <<tutorial-serialization>>.
36893690

@@ -4489,7 +4490,9 @@ namespace serialization
44894490
{
44904491
Json & j_;
44914492
4492-
// Enabled if to_json is available for Json and T.
4493+
// Uses unspecified SFINAE expression designed to select this overload
4494+
// only if no other compatible overload is found.
4495+
// Implemented in terms of to_json.
44934496
template <class T>
44944497
friend void output( nlohmann_json_encoder &, T const & x );
44954498

include/boost/leaf/detail/encoder.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <boost/leaf/config.hpp>
99
#include <boost/leaf/detail/type_name.hpp>
1010
#include <boost/leaf/detail/function_traits.hpp>
11+
#include <type_traits>
12+
#include <utility>
1113

1214
namespace boost { namespace leaf {
1315

@@ -16,7 +18,10 @@ namespace serialization
1618
struct encoder_adl {};
1719

1820
template <class Encoder, class T>
19-
auto output(Encoder & e, T const & x) -> decltype(output(e, x.value))
21+
typename std::enable_if<
22+
sizeof(T) == sizeof(decltype(std::declval<T const &>().value)),
23+
decltype(output(std::declval<Encoder &>(), std::declval<T const &>().value), void())>::type
24+
output(Encoder & e, T const & x)
2025
{
2126
output(e, x.value);
2227
}

include/boost/leaf/serialization/boost_json_encoder.hpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,32 @@
55
// Distributed under the Boost Software License, Version 1.0. (See accompanying
66
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
77

8+
#include <type_traits>
89
#include <utility>
910

1011
namespace boost { namespace json {
1112

1213
class value;
13-
struct value_from_tag;
14+
15+
template <class T>
16+
void value_from(T &&, value &);
1417

1518
} }
1619

1720
namespace boost { namespace leaf {
1821

1922
namespace serialization
2023
{
21-
template <class Value = boost::json::value, class ValueFromTag = boost::json::value_from_tag>
24+
template <class Value = boost::json::value>
2225
struct boost_json_encoder_
2326
{
2427
Value & v_;
2528

26-
template <class T>
27-
friend auto output(boost_json_encoder_ & e, T const & x) -> decltype(std::declval<Value &>() = x, void())
28-
{
29-
e.v_ = x;
30-
}
31-
32-
template <class T>
33-
friend auto output(boost_json_encoder_ & e, T const & x) -> decltype(tag_invoke(std::declval<ValueFromTag>(), std::declval<Value &>(), x), void())
29+
template <class Encoder, class T, class... Deprioritize>
30+
friend typename std::enable_if<std::is_same<Encoder, boost_json_encoder_>::value>::type
31+
output(Encoder & e, T const & x, Deprioritize...)
3432
{
35-
tag_invoke(ValueFromTag{}, e.v_, x);
33+
boost::json::value_from(x, e.v_);
3634
}
3735

3836
template <class T>

include/boost/leaf/serialization/nlohmann_json_encoder.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Distributed under the Boost Software License, Version 1.0. (See accompanying
66
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
77

8+
#include <type_traits>
89
#include <utility>
910

1011
namespace boost { namespace leaf {
@@ -16,10 +17,12 @@ namespace serialization
1617
{
1718
Json & j_;
1819

19-
template <class T>
20-
friend auto output(nlohmann_json_encoder & e, T const & x) -> decltype(to_json(std::declval<Json &>(), x), void())
20+
template <class Encoder, class T, class... Deprioritize>
21+
friend typename std::enable_if<std::is_same<Encoder, nlohmann_json_encoder>::value, Json *>::type
22+
output(Encoder & e, T const & x, Deprioritize...)
2123
{
2224
to_json(e.j_, x);
25+
return 0;
2326
}
2427

2528
template <class T>

test/boost_json_encoder_test.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <iomanip>
1818
#include <iostream>
1919
#include <stdexcept>
20+
#include <vector>
2021
#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR
2122
# include <system_error>
2223
#endif
@@ -76,6 +77,11 @@ struct my_error
7677
}
7778
};
7879

80+
struct my_error_with_vector
81+
{
82+
std::vector<int> value;
83+
};
84+
7985
leaf::result<void> fail()
8086
{
8187
return BOOST_LEAF_NEW_ERROR(
@@ -87,6 +93,7 @@ leaf::result<void> fail()
8793
42,
8894
my_error<1>{1, "error one"},
8995
my_error<2>{2, "error two"},
96+
my_error_with_vector{{10, 20, 30}},
9097
leaf::e_errno{ENOENT},
9198
leaf::e_api_function{"my_api_function"} );
9299
}
@@ -103,6 +110,7 @@ void leaf_throw()
103110
42,
104111
my_error<1>{1, "error one"},
105112
my_error<2>{2, "error two"},
113+
my_error_with_vector{{10, 20, 30}},
106114
leaf::e_errno{ENOENT},
107115
leaf::e_api_function{"my_api_function"} );
108116
}
@@ -118,6 +126,7 @@ void throw_()
118126
42,
119127
my_error<1>{1, "error one"},
120128
my_error<2>{2, "error two"},
129+
my_error_with_vector{{10, 20, 30}},
121130
leaf::e_errno{ENOENT},
122131
leaf::e_api_function{"my_api_function"} );
123132
throw my_exception{};
@@ -167,6 +176,13 @@ void check_diagnostic_details(boost::json::value const & j, bool has_source_loca
167176
BOOST_TEST_EQ(boost::json::value_to<int>(e2j.at("code")), 2);
168177
BOOST_TEST_EQ(boost::json::value_to<std::string>(e2j.at("message")), "error two");
169178

179+
auto const & vj = j.at("my_error_with_vector");
180+
BOOST_TEST(vj.is_array());
181+
BOOST_TEST_EQ(vj.as_array().size(), 3);
182+
BOOST_TEST_EQ(boost::json::value_to<int>(vj.as_array()[0]), 10);
183+
BOOST_TEST_EQ(boost::json::value_to<int>(vj.as_array()[1]), 20);
184+
BOOST_TEST_EQ(boost::json::value_to<int>(vj.as_array()[2]), 30);
185+
170186
auto const & ej = j.at("boost::leaf::e_errno");
171187
BOOST_TEST_EQ(boost::json::value_to<int>(ej.at("errno")), ENOENT);
172188
BOOST_TEST(!boost::json::value_to<std::string>(ej.at("strerror")).empty());

0 commit comments

Comments
 (0)