From 2bc685f6b4ae2f53d80eaf526a073244c060a80b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= <delrieutheo@gmail.com>
Date: Wed, 9 Nov 2016 23:55:03 +0100
Subject: [PATCH] to_json and from_json takes both two arguments now

the first is the basic_json type, the second the user-defined type
---
 src/json.hpp          | 49 +++++++++++++++++++------------------------
 test/src/unit-udt.cpp | 35 +++++++++++++++++--------------
 2 files changed, 41 insertions(+), 43 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index bd59e83b..4f471070 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -155,22 +155,21 @@ void from_json();
 
 struct to_json_fn
 {
-    template <typename T>
+    template <typename Json, typename T>
     constexpr auto
-    operator()(T &&val) const noexcept(noexcept(to_json(std::forward<T>(val))))
-        -> decltype(to_json(std::forward<T>(val)))
+    operator()(Json&& j, T &&val) const noexcept(noexcept(to_json(std::forward<Json>(j), std::forward<T>(val))))
+        -> decltype(to_json(std::forward<Json>(j), std::forward<T>(val)), void())
     {
-      return to_json(std::forward<T>(val));
+      return to_json(std::forward<Json>(j), std::forward<T>(val));
     }
-
 };
 
 struct from_json_fn
 {
-    template <typename T, typename Json>
+    template <typename Json, typename T>
     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))
+        -> decltype(from_json(std::forward<Json>(j), val), void())
     {
       return from_json(std::forward<Json>(j), val);
     }
@@ -219,28 +218,19 @@ inline namespace
 template <typename = void, typename = void>
 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))
+  template <typename Json, typename T>
+  static auto from_json(Json&& j, T& val) -> decltype(::nlohmann::from_json(std::forward<Json>(j), val), void())
   {
     ::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)))
+  template <typename Json, typename T>
+  static auto to_json(Json& j, T&& val) -> decltype(::nlohmann::to_json(j, std::forward<T>(val)), void())
   {
-    return ::nlohmann::to_json(std::forward<T>(val));
+    ::nlohmann::to_json(j, std::forward<T>(val));
   }
 };
 
-
 /*!
 @brief a class to store JSON values
 
@@ -1399,9 +1389,11 @@ class basic_json
     }
 
     // constructor chosen when JSONSerializer::to_json exists for type T
-    template <typename T, typename = decltype(JSONSerializer<uncvref_t<T>>::to_json(std::declval<uncvref_t<T>>()))>
+    template <typename T, typename = decltype(JSONSerializer<uncvref_t<T>>::to_json(std::declval<basic_json&>(), std::declval<uncvref_t<T>>()))>
     explicit basic_json(T &&val)
-        : basic_json(JSONSerializer<uncvref_t<T>>::to_json(std::forward<T>(val))) {}
+    {
+      JSONSerializer<uncvref_t<T>>::to_json(*this, std::forward<T>(val));
+    }
 
     /*!
     @brief create a string (explicit)
@@ -3072,11 +3064,14 @@ class basic_json
         return get_impl(static_cast<ValueType*>(nullptr));
     }
 
-    template <typename ValueType, typename = enable_if_t<std::is_default_constructible<uncvref_t<ValueType>>::value, float>>
-    auto get() const -> remove_reference_t<decltype(JSONSerializer<uncvref_t<ValueType>>::from_json(*this, std::declval<ValueType&>()), std::declval<ValueType>())>
+    template <typename ValueType, typename = decltype(JSONSerializer<uncvref_t<ValueType>>::from_json(std::declval<basic_json>(), std::declval<ValueType&>()))>
+    auto get() const -> uncvref_t<ValueType>
     {
-      uncvref_t<ValueType> ret;
-      JSONSerializer<uncvref_t<ValueType>>::from_json(*this, ret);
+      using type = uncvref_t<ValueType>;
+      static_assert(std::is_default_constructible<type>::value && std::is_copy_constructible<type>::value,
+                    "user-defined types must be DefaultConstructible and CopyConstructible when used with get");
+      type ret;
+      JSONSerializer<type>::from_json(*this, ret);
       return ret;
     }
 
diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp
index 77b31c6a..17494a45 100644
--- a/test/src/unit-udt.cpp
+++ b/test/src/unit-udt.cpp
@@ -70,32 +70,31 @@ private:
 
 // free to/from_json functions
 
-json to_json(empty_type)
+void to_json(json& j, empty_type)
 {
-  return json::object();
+  j = json::object();
 }
 
-json to_json(pod_type const& p)
+void to_json(json& j, pod_type const& p)
 {
-  return {{"a", p.a}, {"b", p.b}, {"c", p.c}};
+  j = json{{"a", p.a}, {"b", p.b}, {"c", p.c}};
 }
 
-json to_json(bit_more_complex_type const& p)
+void to_json(json& j, bit_more_complex_type const& p)
 {
-  using nlohmann::to_json;
-  return json{{"a", to_json(p.a)}, {"b", to_json(p.b)}, {"c", p.c}};
+  j = json{{"a", json(p.a)}, {"b", json(p.b)}, {"c", p.c}};
 }
 
 template <typename T>
-json to_json(optional_type<T> const& opt)
+void to_json(json& j, optional_type<T> const& opt)
 {
-  using nlohmann::to_json;
   if (!opt)
-    return nullptr;
-  return json(*opt);
+    j = nullptr;
+  else
+    j = json(*opt);
 }
 
-void from_json(json const&j, empty_type& t)
+void from_json(json const& j, empty_type& t)
 {
   assert(j.empty());
   t = empty_type{};
@@ -292,7 +291,8 @@ TEST_CASE("to_json free function", "[udt]")
     auto const e = udt::pod_type{42, 42, 42};
     auto const expected = json{{"a", 42}, {"b", 42}, {"c", 42}};
 
-    auto const j = nlohmann::to_json(e);
+    json j;
+    nlohmann::to_json(j, e);
     CHECK(j == expected);
   }
 
@@ -303,7 +303,8 @@ TEST_CASE("to_json free function", "[udt]")
     auto const expected = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}},
                         {"b", {{"a", 41}, {"b", 41}, {"c", 41}}},
                         {"c", "forty"}};
-    auto const j = nlohmann::to_json(e);
+    json j;
+    nlohmann::to_json(j, e);
     CHECK(j == expected);
   }
 
@@ -314,7 +315,8 @@ TEST_CASE("to_json free function", "[udt]")
       udt::optional_type<udt::pod_type> o;
 
       json expected;
-      auto const j = nlohmann::to_json(o);
+      json j;
+      nlohmann::to_json(j, o);
       CHECK(expected == j);
     }
 
@@ -323,7 +325,8 @@ TEST_CASE("to_json free function", "[udt]")
       udt::optional_type<udt::pod_type> o{{42, 42, 42}};
 
       auto const expected = json{{"a", 42}, {"b", 42}, {"c", 42}};
-      auto const j = nlohmann::to_json(o);
+      json j;
+      nlohmann::to_json(j, o);
       CHECK(expected == j);
     }
   }