add first version for alternate implementation
This commit is contained in:
parent
4cdc61e493
commit
7dc268ef42
2 changed files with 41 additions and 262 deletions
131
src/json.hpp
131
src/json.hpp
|
@ -106,17 +106,6 @@ SOFTWARE.
|
||||||
*/
|
*/
|
||||||
namespace nlohmann
|
namespace nlohmann
|
||||||
{
|
{
|
||||||
// TODO add real documentation before PR
|
|
||||||
|
|
||||||
// Traits structure declaration, users can specialize it for their own types
|
|
||||||
//
|
|
||||||
// constructing a json object from a user-defined type will call the
|
|
||||||
// 'json to_json(T)' function
|
|
||||||
//
|
|
||||||
// whereas calling json::get<T> will call 'T from_json(json const&)'
|
|
||||||
template <typename T, typename = void>
|
|
||||||
struct json_traits;
|
|
||||||
|
|
||||||
// alias templates to reduce boilerplate
|
// alias templates to reduce boilerplate
|
||||||
template <bool B, typename T = void>
|
template <bool B, typename T = void>
|
||||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||||
|
@ -166,64 +155,24 @@ void from_json();
|
||||||
|
|
||||||
struct to_json_fn
|
struct to_json_fn
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
// fallback overload
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static constexpr auto
|
constexpr auto
|
||||||
impl(T &&val, long) noexcept(noexcept(to_json(std::forward<T>(val))))
|
operator()(T &&val) const noexcept(noexcept(to_json(std::forward<T>(val))))
|
||||||
-> decltype(to_json(std::forward<T>(val)))
|
-> decltype(to_json(std::forward<T>(val)))
|
||||||
{
|
{
|
||||||
return to_json(std::forward<T>(val));
|
return to_json(std::forward<T>(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
// preferred overload
|
|
||||||
template <typename T>
|
|
||||||
static constexpr auto impl(T &&val, int) noexcept(
|
|
||||||
noexcept(json_traits<uncvref_t<T>>::to_json(std::forward<T>(val))))
|
|
||||||
-> decltype(json_traits<uncvref_t<T>>::to_json(std::forward<T>(val)))
|
|
||||||
{
|
|
||||||
return json_traits<uncvref_t<T>>::to_json(std::forward<T>(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename T>
|
|
||||||
constexpr auto operator()(T &&val) const
|
|
||||||
noexcept(noexcept(to_json_fn::impl(std::forward<T>(val), 0)))
|
|
||||||
-> decltype(to_json_fn::impl(std::forward<T>(val), 0))
|
|
||||||
{
|
|
||||||
// decltype(0) -> int, so the compiler will try to take the 'preferred overload'
|
|
||||||
// if there is no specialization, the 'fallback overload' will be taken by converting 0 to long
|
|
||||||
return to_json_fn::impl(std::forward<T>(val), 0);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct from_json_fn
|
struct from_json_fn
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
template <typename T, typename Json>
|
template <typename T, typename Json>
|
||||||
static constexpr auto impl(Json const &j, T &val,
|
constexpr auto operator()(Json &&j, T &val) const
|
||||||
long) noexcept(noexcept(from_json(j, val)))
|
noexcept(noexcept(from_json(std::forward<Json>(j), val)))
|
||||||
-> decltype(from_json(j, val))
|
-> decltype(from_json(std::forward<Json>(j), val))
|
||||||
{
|
{
|
||||||
return from_json(j, val);
|
return from_json(std::forward<Json>(j), val);
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename Json>
|
|
||||||
static constexpr auto
|
|
||||||
impl(Json const &j, T &val,
|
|
||||||
int) noexcept(noexcept(json_traits<T>::from_json(j, val)))
|
|
||||||
-> decltype(json_traits<T>::from_json(j, val))
|
|
||||||
{
|
|
||||||
return json_traits<T>::from_json(j, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename T, typename Json>
|
|
||||||
constexpr auto operator()(Json const &j, T &val) const
|
|
||||||
noexcept(noexcept(from_json_fn::impl(j, val, 0)))
|
|
||||||
-> decltype(from_json_fn::impl(j, val, 0))
|
|
||||||
{
|
|
||||||
return from_json_fn::impl(j, val, 0);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -265,6 +214,32 @@ inline namespace
|
||||||
constexpr auto const& from_json = _static_const<detail::from_json_fn>::value;
|
constexpr auto const& from_json = _static_const<detail::from_json_fn>::value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// default JSONSerializer template argument
|
||||||
|
// will use ADL for serialization
|
||||||
|
struct adl_serializer
|
||||||
|
{
|
||||||
|
template <typename T, typename Json, typename = enable_if_t<std::is_default_constructible<uncvref_t<T>>::value>>
|
||||||
|
static auto from_json(Json&& j) -> uncvref_t<decltype(::nlohmann::from_json(std::forward<Json>(j), std::declval<T&>()), std::declval<T>())>
|
||||||
|
{
|
||||||
|
uncvref_t<T> ret;
|
||||||
|
::nlohmann::from_json(std::forward<Json>(j), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Json>
|
||||||
|
static auto from_json(Json&& j, T& val) -> decltype(::nlohmann::from_json(std::forward<Json>(j), val))
|
||||||
|
{
|
||||||
|
::nlohmann::from_json(std::forward<Json>(j), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static auto to_json(T&& val) -> decltype(::nlohmann::to_json(std::forward<T>(val)))
|
||||||
|
{
|
||||||
|
return ::nlohmann::to_json(std::forward<T>(val));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief a class to store JSON values
|
@brief a class to store JSON values
|
||||||
|
|
||||||
|
@ -352,7 +327,8 @@ template <
|
||||||
class NumberIntegerType = std::int64_t,
|
class NumberIntegerType = std::int64_t,
|
||||||
class NumberUnsignedType = std::uint64_t,
|
class NumberUnsignedType = std::uint64_t,
|
||||||
class NumberFloatType = double,
|
class NumberFloatType = double,
|
||||||
template<typename U> class AllocatorType = std::allocator
|
template<typename U> class AllocatorType = std::allocator,
|
||||||
|
class JSONSerializer = adl_serializer
|
||||||
>
|
>
|
||||||
class basic_json
|
class basic_json
|
||||||
{
|
{
|
||||||
|
@ -1421,30 +1397,10 @@ class basic_json
|
||||||
assert_invariant();
|
assert_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
// constructor chosen for user-defined types that either have:
|
// constructor chosen when JSONSerializer::to_json exists for type T
|
||||||
// - a to_json free function in their type's namespace
|
template <typename T, typename = decltype(JSONSerializer::to_json(std::declval<uncvref_t<T>>()))>
|
||||||
// - a json_traits specialization for their type
|
|
||||||
//
|
|
||||||
// If there is both a free function and a specialization, the latter will be chosen,
|
|
||||||
// since it is a more advanced use
|
|
||||||
//
|
|
||||||
// note: constructor is marked explicit to avoid the following issue:
|
|
||||||
//
|
|
||||||
// struct not_equality_comparable{};
|
|
||||||
//
|
|
||||||
// not_equality_comparable{} == not_equality_comparable{};
|
|
||||||
//
|
|
||||||
// this will construct implicitely 2 json objects and call operator== on them
|
|
||||||
// which can cause nasty bugs on the user's in json-unrelated code
|
|
||||||
//
|
|
||||||
// the trade-off is expressiveness in initializer-lists
|
|
||||||
// auto j = json{{"a", json(not_equality_comparable{})}};
|
|
||||||
//
|
|
||||||
// we can remove this constraint though, since lots of ctor are not explicit already
|
|
||||||
template <typename T, typename = decltype(::nlohmann::to_json(
|
|
||||||
std::declval<uncvref_t<T>>()))>
|
|
||||||
explicit basic_json(T &&val)
|
explicit basic_json(T &&val)
|
||||||
: basic_json(::nlohmann::to_json(std::forward<T>(val))) {}
|
: basic_json(JSONSerializer::to_json(std::forward<T>(val))) {}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief create a string (explicit)
|
@brief create a string (explicit)
|
||||||
|
@ -3115,16 +3071,11 @@ class basic_json
|
||||||
return get_impl(static_cast<ValueType*>(nullptr));
|
return get_impl(static_cast<ValueType*>(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ValueType>
|
template <typename ValueType, typename = enable_if_t<std::is_default_constructible<uncvref_t<ValueType>>::value, float>>
|
||||||
auto get() const -> remove_reference_t<
|
auto get() const -> remove_reference_t<decltype(JSONSerializer::from_json(*this, std::declval<ValueType&>()), std::declval<ValueType>())>
|
||||||
decltype(::nlohmann::from_json(*this, std::declval<ValueType &>()),
|
|
||||||
std::declval<ValueType>())>
|
|
||||||
{
|
{
|
||||||
static_assert(std::is_default_constructible<ValueType>::value,
|
uncvref_t<ValueType> ret;
|
||||||
"ValueType must be default-constructible when user-defined "
|
JSONSerializer::from_json(*this, ret);
|
||||||
"from_json method is used");
|
|
||||||
ValueType ret;
|
|
||||||
::nlohmann::from_json(*this, ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,9 +35,6 @@ using nlohmann::json;
|
||||||
|
|
||||||
namespace udt
|
namespace udt
|
||||||
{
|
{
|
||||||
// only used by counter_type
|
|
||||||
auto nb_free_function_calls = 0;
|
|
||||||
|
|
||||||
struct empty_type {};
|
struct empty_type {};
|
||||||
struct pod_type {
|
struct pod_type {
|
||||||
int a;
|
int a;
|
||||||
|
@ -51,10 +48,6 @@ struct bit_more_complex_type {
|
||||||
std::string c;
|
std::string c;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct counter_type
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
// best optional implementation ever
|
// best optional implementation ever
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class optional_type
|
class optional_type
|
||||||
|
@ -75,11 +68,6 @@ private:
|
||||||
std::shared_ptr<T> _val;
|
std::shared_ptr<T> _val;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct no_json_traits_type
|
|
||||||
{
|
|
||||||
int a;
|
|
||||||
};
|
|
||||||
|
|
||||||
// free to/from_json functions
|
// free to/from_json functions
|
||||||
|
|
||||||
json to_json(empty_type)
|
json to_json(empty_type)
|
||||||
|
@ -107,17 +95,6 @@ json to_json(optional_type<T> const& opt)
|
||||||
return json(*opt);
|
return json(*opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
json to_json(no_json_traits_type const& p)
|
|
||||||
{
|
|
||||||
return {{"a", p.a}};
|
|
||||||
}
|
|
||||||
|
|
||||||
json to_json(counter_type)
|
|
||||||
{
|
|
||||||
++nb_free_function_calls;
|
|
||||||
return json::object();
|
|
||||||
}
|
|
||||||
|
|
||||||
void from_json(json const&j, empty_type& t)
|
void from_json(json const&j, empty_type& t)
|
||||||
{
|
{
|
||||||
assert(j.empty());
|
assert(j.empty());
|
||||||
|
@ -136,11 +113,6 @@ void from_json(json const&j, bit_more_complex_type& t)
|
||||||
j["c"].get<std::string>()};
|
j["c"].get<std::string>()};
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_json(json const& j, no_json_traits_type& t)
|
|
||||||
{
|
|
||||||
t.a = j["a"].get<int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void from_json(json const& j, optional_type<T>& t)
|
void from_json(json const& j, optional_type<T>& t)
|
||||||
{
|
{
|
||||||
|
@ -150,11 +122,6 @@ void from_json(json const& j, optional_type<T>& t)
|
||||||
t = j.get<T>();
|
t = j.get<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_json(json const&, counter_type&)
|
|
||||||
{
|
|
||||||
++nb_free_function_calls;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator==(pod_type const& lhs, pod_type const& rhs) noexcept
|
inline bool operator==(pod_type const& lhs, pod_type const& rhs) noexcept
|
||||||
{
|
{
|
||||||
return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c);
|
return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c);
|
||||||
|
@ -174,106 +141,7 @@ inline bool operator==(optional_type<T> const& lhs, optional_type<T> const& rhs)
|
||||||
return false;
|
return false;
|
||||||
return *lhs == *rhs;
|
return *lhs == *rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(no_json_traits_type const& lhs, no_json_traits_type const& rhs)
|
|
||||||
{
|
|
||||||
return lhs.a == rhs.a;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
template <>
|
|
||||||
struct json_traits<udt::empty_type>
|
|
||||||
{
|
|
||||||
using type = udt::empty_type;
|
|
||||||
|
|
||||||
static json to_json(type)
|
|
||||||
{
|
|
||||||
return json::object();
|
|
||||||
}
|
|
||||||
|
|
||||||
static type from_json(json const& j)
|
|
||||||
{
|
|
||||||
assert(j.is_object() and j.empty());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct json_traits<udt::pod_type>
|
|
||||||
{
|
|
||||||
using type = udt::pod_type;
|
|
||||||
|
|
||||||
static json to_json(type const& t)
|
|
||||||
{
|
|
||||||
return {{"a", t.a}, {"b", t.b}, {"c", t.c}};
|
|
||||||
}
|
|
||||||
|
|
||||||
static type from_json(json const& j)
|
|
||||||
{
|
|
||||||
assert(j.is_object());
|
|
||||||
return {j["a"].get<int>(), j["b"].get<char>(), j["c"].get<short>()};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct json_traits<udt::bit_more_complex_type>
|
|
||||||
{
|
|
||||||
using type = udt::bit_more_complex_type;
|
|
||||||
|
|
||||||
static json to_json(type const& t)
|
|
||||||
{
|
|
||||||
return json{{"a", json{t.a}}, {"b", json{t.b}}, {"c", t.c}};
|
|
||||||
}
|
|
||||||
|
|
||||||
static type from_json(json const& j)
|
|
||||||
{
|
|
||||||
return {j["a"].get<udt::pod_type>(), j["b"].get<udt::pod_type>(),
|
|
||||||
j["c"].get<std::string>()};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct json_traits<udt::optional_type<T>>
|
|
||||||
{
|
|
||||||
using type = udt::optional_type<T>;
|
|
||||||
|
|
||||||
static json to_json(type const& t)
|
|
||||||
{
|
|
||||||
if (t)
|
|
||||||
return json(*t);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
static type from_json(json const& j)
|
|
||||||
{
|
|
||||||
if (j.is_null())
|
|
||||||
return {};
|
|
||||||
return type{j.get<T>()};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct json_traits<udt::counter_type>
|
|
||||||
{
|
|
||||||
using type = udt::counter_type;
|
|
||||||
static int nb_calls;
|
|
||||||
|
|
||||||
static json to_json(type)
|
|
||||||
{
|
|
||||||
++nb_calls;
|
|
||||||
return json::object();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void from_json(json const&, type&)
|
|
||||||
{
|
|
||||||
++nb_calls;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
int json_traits<udt::counter_type>::nb_calls{0};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("constructors for user-defined types", "[udt]")
|
TEST_CASE("constructors for user-defined types", "[udt]")
|
||||||
{
|
{
|
||||||
|
@ -415,24 +283,6 @@ TEST_CASE("get<> for user-defined types", "[udt]")
|
||||||
CHECK(*v == expected);
|
CHECK(*v == expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("no json_traits specialization, use of ADL")
|
|
||||||
{
|
|
||||||
udt::no_json_traits_type val{42};
|
|
||||||
auto const expected = json{{"a", 42}};
|
|
||||||
auto const j = json(val);
|
|
||||||
CHECK(j == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("counter_type")
|
|
||||||
{
|
|
||||||
// check that the traits specialization is chosen
|
|
||||||
auto const j = json{udt::counter_type{}};
|
|
||||||
CHECK(nlohmann::json_traits<udt::counter_type>::nb_calls == 1);
|
|
||||||
auto const elem = j.get<udt::counter_type>();
|
|
||||||
CHECK(nlohmann::json_traits<udt::counter_type>::nb_calls == 2);
|
|
||||||
CHECK(udt::nb_free_function_calls == 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("to_json free function", "[udt]")
|
TEST_CASE("to_json free function", "[udt]")
|
||||||
|
@ -477,16 +327,6 @@ TEST_CASE("to_json free function", "[udt]")
|
||||||
CHECK(expected == j);
|
CHECK(expected == j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("no json_traits specialization")
|
|
||||||
{
|
|
||||||
udt::no_json_traits_type t{42};
|
|
||||||
|
|
||||||
json expected;
|
|
||||||
expected["a"] = 42;
|
|
||||||
auto const j = nlohmann::to_json(t);
|
|
||||||
CHECK(j == expected);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("from_json free function", "[udt]")
|
TEST_CASE("from_json free function", "[udt]")
|
||||||
|
@ -535,16 +375,4 @@ TEST_CASE("from_json free function", "[udt]")
|
||||||
CHECK(expected == o);
|
CHECK(expected == o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("no json_traits specialization")
|
|
||||||
{
|
|
||||||
udt::no_json_traits_type expected{42};
|
|
||||||
udt::no_json_traits_type res;
|
|
||||||
auto const j = json{{"a", 42}};
|
|
||||||
nlohmann::from_json(j, res);
|
|
||||||
CHECK(res == expected);
|
|
||||||
|
|
||||||
res = j.get<udt::no_json_traits_type>();
|
|
||||||
CHECK(res == expected);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue