diff --git a/src/json.hpp b/src/json.hpp index 8b281d93..8dc9b383 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -106,17 +106,6 @@ SOFTWARE. */ 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 template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type; @@ -166,64 +155,24 @@ void from_json(); struct to_json_fn { - private: - // fallback overload template <typename T> - static constexpr auto - impl(T &&val, long) noexcept(noexcept(to_json(std::forward<T>(val)))) + constexpr auto + operator()(T &&val) const noexcept(noexcept(to_json(std::forward<T>(val)))) -> decltype(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 { - private: template <typename T, typename Json> - static constexpr auto impl(Json const &j, T &val, - long) noexcept(noexcept(from_json(j, val))) - -> decltype(from_json(j, val)) + constexpr auto operator()(Json &&j, T &val) const + noexcept(noexcept(from_json(std::forward<Json>(j), val))) + -> decltype(from_json(std::forward<Json>(j), val)) { - return from_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); + return from_json(std::forward<Json>(j), val); } }; @@ -265,6 +214,32 @@ inline namespace 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 @@ -352,7 +327,8 @@ template < class NumberIntegerType = std::int64_t, class NumberUnsignedType = std::uint64_t, class NumberFloatType = double, - template<typename U> class AllocatorType = std::allocator + template<typename U> class AllocatorType = std::allocator, + class JSONSerializer = adl_serializer > class basic_json { @@ -1421,30 +1397,10 @@ class basic_json assert_invariant(); } - // constructor chosen for user-defined types that either have: - // - a to_json free function in their type's namespace - // - 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>>()))> + // constructor chosen when JSONSerializer::to_json exists for type T + template <typename T, typename = decltype(JSONSerializer::to_json(std::declval<uncvref_t<T>>()))> 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) @@ -3115,16 +3071,11 @@ class basic_json return get_impl(static_cast<ValueType*>(nullptr)); } - template <typename ValueType> - auto get() const -> remove_reference_t< - decltype(::nlohmann::from_json(*this, std::declval<ValueType &>()), - std::declval<ValueType>())> + template <typename ValueType, typename = enable_if_t<std::is_default_constructible<uncvref_t<ValueType>>::value, float>> + auto get() const -> remove_reference_t<decltype(JSONSerializer::from_json(*this, std::declval<ValueType&>()), std::declval<ValueType>())> { - static_assert(std::is_default_constructible<ValueType>::value, - "ValueType must be default-constructible when user-defined " - "from_json method is used"); - ValueType ret; - ::nlohmann::from_json(*this, ret); + uncvref_t<ValueType> ret; + JSONSerializer::from_json(*this, ret); return ret; } diff --git a/test/src/unit-constructor3.cpp b/test/src/unit-constructor3.cpp index de52762f..77b31c6a 100644 --- a/test/src/unit-constructor3.cpp +++ b/test/src/unit-constructor3.cpp @@ -35,9 +35,6 @@ using nlohmann::json; namespace udt { -// only used by counter_type -auto nb_free_function_calls = 0; - struct empty_type {}; struct pod_type { int a; @@ -51,10 +48,6 @@ struct bit_more_complex_type { std::string c; }; -struct counter_type -{ -}; - // best optional implementation ever template <typename T> class optional_type @@ -75,11 +68,6 @@ private: std::shared_ptr<T> _val; }; -struct no_json_traits_type -{ - int a; -}; - // free to/from_json functions json to_json(empty_type) @@ -107,17 +95,6 @@ json to_json(optional_type<T> const& 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) { assert(j.empty()); @@ -136,11 +113,6 @@ void from_json(json const&j, bit_more_complex_type& t) 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> 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>(); } -void from_json(json const&, counter_type&) -{ - ++nb_free_function_calls; -} - 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); @@ -174,106 +141,7 @@ inline bool operator==(optional_type<T> const& lhs, optional_type<T> const& rhs) return false; 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]") { @@ -415,24 +283,6 @@ TEST_CASE("get<> for user-defined types", "[udt]") 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]") @@ -477,16 +327,6 @@ TEST_CASE("to_json free function", "[udt]") 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]") @@ -535,16 +375,4 @@ TEST_CASE("from_json free function", "[udt]") 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); - } }