Improved SFINAE in the JSON encoders

This commit is contained in:
Emil Dotchevski
2026-02-01 13:57:16 -05:00
parent 4c3e9427bf
commit 0bb2696058
4 changed files with 49 additions and 39 deletions
+16 -11
View File
@@ -1084,8 +1084,9 @@ struct boost_json_encoder
{ {
boost::json::value & v_; boost::json::value & v_;
// Enabled if x is assignable to boost::json::value, or // Uses unspecified SFINAE expression designed to make the
// if tag_invoke is defined for boost::json::value_from_tag. // overload selected only if no other compatible overload is found.
// Implemented in terms of boost::json::value_from.
template <class T> template <class T>
friend void output( boost_json_encoder &, T const & x ); friend void output( boost_json_encoder &, T const & x );
@@ -1114,7 +1115,9 @@ struct nlohmann_json_encoder
{ {
Json & j_; Json & j_;
// Enabled if to_json is available for Json and T. // Uses unspecified SFINAE expression designed to make the
// overload selected only if no other compatible overload is found.
// Implemented in terms of to_json.
template <class T> template <class T>
friend void output( nlohmann_json_encoder &, T const & x ); friend void output( nlohmann_json_encoder &, T const & x );
@@ -1156,8 +1159,9 @@ struct boost_json_encoder
{ {
boost::json::value & v_; boost::json::value & v_;
// Enabled if x is assignable to boost::json::value, or // Uses unspecified SFINAE expression designed to make the
// if tag_invoke is defined for boost::json::value_from_tag. // overload selected only if no other compatible overload is found.
// Implemented in terms of boost::json::value_from.
template <class T> template <class T>
friend void output( boost_json_encoder &, T const & x ); friend void output( boost_json_encoder &, T const & x );
@@ -1168,10 +1172,7 @@ struct boost_json_encoder
} } } }
---- ----
The `boost_json_encoder` type serializes objects to JSON format using https://www.boost.org/doc/libs/release/libs/json/[Boost.JSON]. The `output` function is enabled for: The `boost_json_encoder` type serializes 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`.
* Types directly assignable to `boost::json::value`
* Types for which a `tag_invoke` overload for `value_from_tag` can be found via ADL
See <<tutorial_serialization>>. See <<tutorial_serialization>>.
@@ -1592,7 +1593,9 @@ struct nlohmann_json_encoder
{ {
Json & j_; Json & j_;
// Enabled if to_json is available for Json and T. // Uses unspecified SFINAE expression designed to make the
// overload selected only if no other compatible overload is found.
// Implemented in terms of to_json.
template <class T> template <class T>
friend void output( nlohmann_json_encoder &, T const & x ); friend void output( nlohmann_json_encoder &, T const & x );
@@ -1603,7 +1606,9 @@ struct nlohmann_json_encoder
} } } }
---- ----
The `nlohmann_json_encoder` type serializes objects to JSON format based on ADL calls to `to_json`. This is compatible with https://github.com/nlohmann/json[nlohmann/json]. See <<tutorial_serialization>>. The `nlohmann_json_encoder` type serializes objects to JSON format based on ADL calls to `to_json`. This is compatible with https://github.com/nlohmann/json[nlohmann/json].
See <<tutorial_serialization>>.
''' '''
@@ -6,43 +6,36 @@
#ifndef BOOST_EXCEPTION_SERIALIZATION_BOOST_JSON_ENCODER_HPP_INCLUDED #ifndef BOOST_EXCEPTION_SERIALIZATION_BOOST_JSON_ENCODER_HPP_INCLUDED
#define BOOST_EXCEPTION_SERIALIZATION_BOOST_JSON_ENCODER_HPP_INCLUDED #define BOOST_EXCEPTION_SERIALIZATION_BOOST_JSON_ENCODER_HPP_INCLUDED
#include <type_traits>
#include <utility> #include <utility>
namespace boost { namespace json {
class value;
template <class T>
void value_from(T &&, value &);
} }
namespace namespace
boost boost
{ {
namespace
json
{
class value;
struct value_from_tag;
}
namespace namespace
exception_serialization exception_serialization
{ {
// Baast.JSON does not provide ADL interface for serializing user-defined types. template <class Value = boost::json::value>
// This limits the functionality of boost_json_encoder to only types that provide tag_invoke.
template <class Value = boost::json::value, class ValueFromTag = boost::json::value_from_tag>
struct struct
boost_json_encoder_ boost_json_encoder_
{ {
Value & v_; Value & v_;
template <class T> template <class Encoder, class T, class... Deprioritize>
friend friend
auto typename std::enable_if<std::is_same<Encoder, boost_json_encoder_>::value>::type
output(boost_json_encoder_ & e, T const & x) -> decltype(std::declval<Value &>() = x, void()) output(Encoder & e, T const & x, Deprioritize...)
{ {
e.v_ = x; boost::json::value_from(x, e.v_);
}
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);
} }
template <class T> template <class T>
@@ -6,6 +6,7 @@
#ifndef BOOST_EXCEPTION_SERIALIZATION_NLOHMANN_JSON_ENCODER_HPP_INCLUDED #ifndef BOOST_EXCEPTION_SERIALIZATION_NLOHMANN_JSON_ENCODER_HPP_INCLUDED
#define BOOST_EXCEPTION_SERIALIZATION_NLOHMANN_JSON_ENCODER_HPP_INCLUDED #define BOOST_EXCEPTION_SERIALIZATION_NLOHMANN_JSON_ENCODER_HPP_INCLUDED
#include <type_traits>
#include <utility> #include <utility>
namespace namespace
@@ -20,12 +21,12 @@ boost
{ {
Json & j_; Json & j_;
template <class T> template <class Encoder, class T, class... Deprioritize>
friend friend typename std::enable_if<std::is_same<Encoder, nlohmann_json_encoder>::value, Json *>::type
auto output(Encoder & e, T const & x, Deprioritize...)
output(nlohmann_json_encoder & e, T const & x) -> decltype(to_json(std::declval<Json &>(), x))
{ {
to_json(e.j_, x); to_json(e.j_, x);
return 0;
} }
template <class T> template <class T>
+13 -2
View File
@@ -18,6 +18,7 @@
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <exception> #include <exception>
#include <vector>
using output_encoder = boost::exception_serialization::boost_json_encoder; using output_encoder = boost::exception_serialization::boost_json_encoder;
@@ -39,6 +40,7 @@ boost
typedef boost::error_info<struct my_error1_, int> my_error1; typedef boost::error_info<struct my_error1_, int> my_error1;
typedef boost::error_info<struct my_error2_, std::string> my_error2; typedef boost::error_info<struct my_error2_, std::string> my_error2;
typedef boost::error_info<struct my_error4_, std::vector<int>> my_error4;
struct struct
my_info my_info
@@ -97,6 +99,13 @@ check_output(boost::json::value const & j, bool has_source_location)
auto const & mij = obj.at("my_error3_").as_object(); auto const & mij = obj.at("my_error3_").as_object();
BOOST_TEST_EQ(mij.at("code").as_int64(), 1); BOOST_TEST_EQ(mij.at("code").as_int64(), 1);
BOOST_TEST_EQ(mij.at("message").as_string(), "error one"); BOOST_TEST_EQ(mij.at("message").as_string(), "error one");
BOOST_TEST(obj.contains("my_error4_"));
auto const & vec = obj.at("my_error4_").as_array();
BOOST_TEST_EQ(vec.size(), 3u);
BOOST_TEST_EQ(vec[0].as_int64(), 1);
BOOST_TEST_EQ(vec[1].as_int64(), 2);
BOOST_TEST_EQ(vec[2].as_int64(), 3);
} }
int int
@@ -111,7 +120,8 @@ main()
e << e <<
my_error1(42) << my_error1(42) <<
my_error2("hello") << my_error2("hello") <<
my_error3({1, "error one"}); my_error3({1, "error one"}) <<
my_error4({1, 2, 3});
BOOST_THROW_EXCEPTION(e); BOOST_THROW_EXCEPTION(e);
} }
catch( test_exception & e ) catch( test_exception & e )
@@ -132,7 +142,8 @@ main()
e << e <<
my_error1(42) << my_error1(42) <<
my_error2("hello") << my_error2("hello") <<
my_error3({1, "error one"}); my_error3({1, "error one"}) <<
my_error4({1, 2, 3});
BOOST_THROW_EXCEPTION(e); BOOST_THROW_EXCEPTION(e);
} }
catch( ... ) catch( ... )