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

View File

@@ -1084,8 +1084,9 @@ struct boost_json_encoder
{
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 make the
// overload selected 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 );
@@ -1114,7 +1115,9 @@ struct nlohmann_json_encoder
{
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>
friend void output( nlohmann_json_encoder &, T const & x );
@@ -1156,8 +1159,9 @@ struct boost_json_encoder
{
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 make the
// overload selected 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 );
@@ -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:
* 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 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>>.
@@ -1592,7 +1593,9 @@ struct nlohmann_json_encoder
{
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>
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>>.
'''

View File

@@ -6,43 +6,36 @@
#ifndef BOOST_EXCEPTION_SERIALIZATION_BOOST_JSON_ENCODER_HPP_INCLUDED
#define BOOST_EXCEPTION_SERIALIZATION_BOOST_JSON_ENCODER_HPP_INCLUDED
#include <type_traits>
#include <utility>
namespace boost { namespace json {
class value;
template <class T>
void value_from(T &&, value &);
} }
namespace
boost
{
namespace
json
{
class value;
struct value_from_tag;
}
namespace
exception_serialization
{
// Baast.JSON does not provide ADL interface for serializing user-defined types.
// 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>
template <class Value = boost::json::value>
struct
boost_json_encoder_
{
Value & v_;
template <class T>
template <class Encoder, class T, class... Deprioritize>
friend
auto
output(boost_json_encoder_ & e, T const & x) -> decltype(std::declval<Value &>() = x, void())
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

@@ -6,6 +6,7 @@
#ifndef BOOST_EXCEPTION_SERIALIZATION_NLOHMANN_JSON_ENCODER_HPP_INCLUDED
#define BOOST_EXCEPTION_SERIALIZATION_NLOHMANN_JSON_ENCODER_HPP_INCLUDED
#include <type_traits>
#include <utility>
namespace
@@ -20,12 +21,12 @@ boost
{
Json & j_;
template <class T>
friend
auto
output(nlohmann_json_encoder & e, T const & x) -> decltype(to_json(std::declval<Json &>(), x))
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

@@ -18,6 +18,7 @@
#include <iomanip>
#include <iostream>
#include <exception>
#include <vector>
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_error2_, std::string> my_error2;
typedef boost::error_info<struct my_error4_, std::vector<int>> my_error4;
struct
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();
BOOST_TEST_EQ(mij.at("code").as_int64(), 1);
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
@@ -111,7 +120,8 @@ main()
e <<
my_error1(42) <<
my_error2("hello") <<
my_error3({1, "error one"});
my_error3({1, "error one"}) <<
my_error4({1, 2, 3});
BOOST_THROW_EXCEPTION(e);
}
catch( test_exception & e )
@@ -132,7 +142,8 @@ main()
e <<
my_error1(42) <<
my_error2("hello") <<
my_error3({1, "error one"});
my_error3({1, "error one"}) <<
my_error4({1, 2, 3});
BOOST_THROW_EXCEPTION(e);
}
catch( ... )