From 794dae8911d2a4ea42a28066a536d3b5f42227d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= <theo@tanker.io>
Date: Sun, 15 Jan 2017 21:01:29 +0100
Subject: [PATCH] apply changes to json.hpp.re2c

---
 src/json.hpp.re2c | 191 ++++++++++++++++++++++++++++++----------------
 1 file changed, 125 insertions(+), 66 deletions(-)

diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 5dc2e027..4dbeda8a 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -168,7 +168,7 @@ using is_unscoped_enum =
 
 namespace detail
 {
-template <typename Json> std::string type_name(Json const &j)
+template <typename Json> std::string type_name(const  Json &j)
 {
   switch (j.m_type)
   {
@@ -189,6 +189,11 @@ template <typename Json> std::string type_name(Json const &j)
   }
 }
 
+// dispatch utility (taken from ranges-v3)
+template <unsigned N> struct priority_tag : priority_tag<N - 1> {};
+
+template <> struct priority_tag<0> {};
+
 // This is an experiment. I need this to move constructors out of basic_json.
 // I'm sure there is a better way, but this might need a big basic_json refactoring
 template <value_t> struct external_constructor;
@@ -491,14 +496,15 @@ template <typename Json, typename ArithmeticType,
                           not std::is_same<ArithmeticType,
                                            typename Json::boolean_t>::value,
                       int> = 0>
-void get_arithmetic_value(Json const &j, ArithmeticType &val)
+void get_arithmetic_value(const  Json &j, ArithmeticType &val)
 {
-  if (j.is_number_integer())
-    val = *const_cast<Json&>(j).template get_ptr<typename Json::number_integer_t*>();
-  else if (j.is_number_unsigned())
-    val = *const_cast<Json&>(j).template get_ptr<typename Json::number_unsigned_t*>();
+  // unsigned must be checked first, since is_number_integer() == true for unsigned
+  if (j.is_number_unsigned())
+    val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_unsigned_t*>());
+  else if (j.is_number_integer())
+    val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_integer_t*>());
   else if (j.is_number_float())
-    val = *const_cast<Json&>(j).template get_ptr<typename Json::number_float_t*>();
+    val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_float_t*>());
   else
     throw std::domain_error("type must be number, but is " + type_name(j));
 }
@@ -522,7 +528,7 @@ template <typename Json, typename FloatType,
           enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
 void to_json(Json &j, FloatType val) noexcept
 {
-  external_constructor<value_t::number_float>::construct(j, val);
+  external_constructor<value_t::number_float>::construct(j, static_cast<typename Json::number_float_t>(val));
 }
 
 
@@ -533,7 +539,7 @@ template <
                 int> = 0>
 void to_json(Json &j, CompatibleNumberUnsignedType val) noexcept
 {
-  external_constructor<value_t::number_unsigned>::construct(j, val);
+  external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename Json::number_unsigned_t>(val));
 }
 
 template <
@@ -543,7 +549,7 @@ template <
                 int> = 0>
 void to_json(Json &j, CompatibleNumberIntegerType val) noexcept
 {
-  external_constructor<value_t::number_integer>::construct(j, val);
+  external_constructor<value_t::number_integer>::construct(j, static_cast<typename Json::number_integer_t>(val));
 }
 
 template <typename Json, typename UnscopedEnumType,
@@ -559,7 +565,7 @@ template <
         is_compatible_array_type<Json, CompatibleArrayType>::value or
             std::is_same<typename Json::array_t, CompatibleArrayType>::value,
         int> = 0>
-void to_json(Json &j, CompatibleArrayType const &arr)
+void to_json(Json &j, const  CompatibleArrayType &arr)
 {
   external_constructor<value_t::array>::construct(j, arr);
 }
@@ -568,48 +574,48 @@ template <
     typename Json, typename CompatibleObjectType,
     enable_if_t<is_compatible_object_type<Json, CompatibleObjectType>::value,
                 int> = 0>
-void to_json(Json &j, CompatibleObjectType const &arr)
+void to_json(Json &j, const  CompatibleObjectType &arr)
 {
   external_constructor<value_t::object>::construct(j, arr);
 }
 
 template <typename Json>
-void from_json(Json const& j, typename Json::boolean_t& b)
+void from_json(const Json & j, typename Json::boolean_t& b)
 {
   if (!j.is_boolean())
     throw std::domain_error("type must be boolean, but is " + type_name(j));
-  b = *const_cast<Json&>(j).template get_ptr<typename Json::boolean_t*>();
+  b = *j.template get_ptr<const typename Json::boolean_t*>();
 }
 
 template <typename Json>
-void from_json(Json const& j, typename Json::string_t& s)
+void from_json(const Json & j, typename Json::string_t& s)
 {
   if (!j.is_string())
     throw std::domain_error("type must be string, but is " + type_name(j));
-  s = *const_cast<Json&>(j).template get_ptr<typename Json::string_t*>();
+  s = *j.template get_ptr<const typename Json::string_t*>();
 }
 
 template <typename Json>
-void from_json(Json const& j, typename Json::number_float_t& val)
+void from_json(const Json & j, typename Json::number_float_t& val)
 {
   get_arithmetic_value(j, val);
 }
 
 template <typename Json>
-void from_json(Json const& j, typename Json::number_unsigned_t& val)
+void from_json(const Json & j, typename Json::number_unsigned_t& val)
 {
   get_arithmetic_value(j, val);
 }
 
 template <typename Json>
-void from_json(Json const& j, typename Json::number_integer_t& val)
+void from_json(const Json & j, typename Json::number_integer_t& val)
 {
   get_arithmetic_value(j, val);
 }
 
 template <typename Json, typename UnscopedEnumType,
           enable_if_t<is_unscoped_enum<UnscopedEnumType>::value, int> = 0>
-void from_json(Json const &j, UnscopedEnumType& e)
+void from_json(const  Json &j, UnscopedEnumType& e)
 {
   typename std::underlying_type<UnscopedEnumType>::type val = e;
   get_arithmetic_value(j, val);
@@ -617,16 +623,16 @@ void from_json(Json const &j, UnscopedEnumType& e)
 }
 
 template <typename Json>
-void from_json(Json const &j, typename Json::array_t &arr)
+void from_json(const  Json &j, typename Json::array_t &arr)
 {
   if (!j.is_array())
     throw std::domain_error("type must be array, but is " + type_name(j));
-  arr = *const_cast<Json&>(j).template get_ptr<typename Json::array_t*>();
+  arr = *j.template get_ptr<const typename Json::array_t*>();
 }
 
 // forward_list doesn't have an insert method, TODO find a way to avoid including forward_list
 template <typename Json, typename T, typename Allocator>
-void from_json(Json const&j, std::forward_list<T, Allocator>& l)
+void from_json(const Json &j, std::forward_list<T, Allocator>& l)
 {
   // do not perform the check when user wants to retrieve jsons
   // (except when it's null.. ?)
@@ -641,13 +647,47 @@ void from_json(Json const&j, std::forward_list<T, Allocator>& l)
     l.push_front(it->template get<T>());
 }
 
+template <typename Json, typename CompatibleArrayType>
+void from_json_array_impl(const  Json &j, CompatibleArrayType &arr, priority_tag<0>)
+{
+  using std::begin;
+  using std::end;
+
+  std::transform(
+      j.begin(), j.end(), std::inserter(arr, end(arr)), [](const  Json &i)
+      {
+        // get<Json>() returns *this, this won't call a from_json method when
+        // value_type is Json
+        return i.template get<typename CompatibleArrayType::value_type>();
+      });
+}
+
+template <typename Json, typename CompatibleArrayType>
+auto from_json_array_impl(const  Json &j, CompatibleArrayType &arr, priority_tag<1>)
+    -> decltype(
+        arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
+        void())
+{
+  using std::begin;
+  using std::end;
+
+  arr.reserve(j.size());
+  std::transform(
+      j.begin(), j.end(), std::inserter(arr, end(arr)), [](const  Json &i)
+      {
+        // get<Json>() returns *this, this won't call a from_json method when
+        // value_type is Json
+        return i.template get<typename CompatibleArrayType::value_type>();
+      });
+}
+
 template <
     typename Json, typename CompatibleArrayType,
     enable_if_t<is_compatible_array_type<Json, CompatibleArrayType>::value and
                     not std::is_same<typename Json::array_t,
                                      CompatibleArrayType>::value,
                 int> = 0>
-void from_json(Json const &j, CompatibleArrayType &arr)
+void from_json(const  Json &j, CompatibleArrayType &arr)
 {
   if (j.is_null())
       throw std::domain_error("type must be array, but is " + type_name(j));
@@ -657,16 +697,7 @@ void from_json(Json const &j, CompatibleArrayType &arr)
     if (!j.is_array())
       throw std::domain_error("type must be array, but is " + type_name(j));
   }
-
-  using std::begin;
-  using std::end;
-  std::transform(
-      j.begin(), j.end(), std::inserter(arr, end(arr)), [](Json const &i)
-      {
-        // get<Json>() returns *this, this won't call a from_json method when
-        // value_type is Json
-        return i.template get<typename CompatibleArrayType::value_type>();
-      });
+  from_json_array_impl(j, arr, priority_tag<1>{});
 }
 
 
@@ -674,12 +705,12 @@ template <
     typename Json, typename CompatibleObjectType,
     enable_if_t<is_compatible_object_type<Json, CompatibleObjectType>::value,
                 int> = 0>
-void from_json(Json const &j, CompatibleObjectType &obj)
+void from_json(const  Json &j, CompatibleObjectType &obj)
 {
   if (!j.is_object())
     throw std::domain_error("type must be object, but is " + type_name(j));
 
-  auto inner_object = const_cast<Json&>(j).template get_ptr<typename Json::object_t*>();
+  auto inner_object = j.template get_ptr<const typename Json::object_t*>();
   using std::begin;
   using std::end;
   // we could avoid the assignment, but this might require a for loop, which
@@ -700,16 +731,16 @@ template <
             not std::is_same<ArithmeticType, typename Json::number_float_t>::value and
             not std::is_same<ArithmeticType, typename Json::boolean_t>::value,
         int> = 0>
-void from_json(Json const &j, ArithmeticType &val)
+void from_json(const  Json &j, ArithmeticType &val)
 {
-  if (j.is_number_integer())
-    val = *const_cast<Json&>(j).template get_ptr<typename Json::number_integer_t*>();
-  else if (j.is_number_unsigned())
-    val = *const_cast<Json&>(j).template get_ptr<typename Json::number_unsigned_t*>();
+  if (j.is_number_unsigned())
+    val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_unsigned_t*>());
+  else if (j.is_number_integer())
+    val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_integer_t*>());
   else if (j.is_number_float())
-    val = *const_cast<Json&>(j).template get_ptr<typename Json::number_float_t*>();
+    val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_float_t*>());
   else if (j.is_boolean())
-    val = *const_cast<Json&>(j).template get_ptr<typename Json::boolean_t*>();
+    val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::boolean_t*>());
   else
     throw std::domain_error("type must be number, but is " + type_name(j));
 }
@@ -717,24 +748,53 @@ void from_json(Json const &j, ArithmeticType &val)
 struct to_json_fn
 {
     template <typename Json, typename T>
-    constexpr auto 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)),
+    auto call(Json& j, T&& val, priority_tag<1>) const
+    noexcept(noexcept(to_json(j, std::forward<T>(val))))
+    -> decltype(to_json(j, std::forward<T>(val)),
                 void())
     {
-        return to_json(std::forward<Json>(j), std::forward<T>(val));
+        return to_json(j, std::forward<T>(val));
     }
+
+    template <typename Json, typename T>
+    void call(Json&, T&&, priority_tag<0>) const noexcept
+    {
+        static_assert(sizeof(Json) == 0, "to_json method in T's namespace can not be called");
+    }
+
+public:
+  template <typename Json, typename T>
+  void operator()(Json &j, T &&val) const
+      noexcept(noexcept(std::declval<to_json_fn>().call(j, std::forward<T>(val), priority_tag<1>{})))
+  {
+      return call(j, std::forward<T>(val), priority_tag<1>{});
+  }
 };
 
 struct from_json_fn
 {
-    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), void())
-    {
-        return from_json(std::forward<Json>(j), val);
-    }
+private:
+  template <typename Json, typename T>
+  auto call(const  Json &j, T &val, priority_tag<1>) const
+      noexcept(noexcept(from_json(j, val)))
+          -> decltype(from_json(j, val), void())
+  {
+    return from_json(j, val);
+  }
+
+  template <typename Json, typename T>
+  void call(const Json &, T&, priority_tag<0>) const noexcept
+  {
+      static_assert(sizeof(Json) == 0, "from_json method in T's namespace can not be called");
+  }
+
+public:
+  template <typename Json, typename T>
+  void operator()(const  Json &j, T &val) const
+      noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1>{})))
+  {
+      return call(j, val, priority_tag<1>{});
+  }
 };
 
 /*!
@@ -756,7 +816,6 @@ struct DecimalSeparator : std::numpunct<char>
         return '.';
     }
 };
-
 }
 
 // taken from ranges-v3
@@ -772,8 +831,8 @@ constexpr T static_const<T>::value;
 
 inline namespace
 {
-constexpr auto const& to_json = static_const<detail::to_json_fn>::value;
-constexpr auto const& from_json = static_const<detail::from_json_fn>::value;
+constexpr const auto & to_json = static_const<detail::to_json_fn>::value;
+constexpr const auto & from_json = static_const<detail::from_json_fn>::value;
 }
 
 // default JSONSerializer template argument, doesn't care about template argument
@@ -788,9 +847,10 @@ struct adl_serializer
     }
 
     template <typename Json, typename T>
-    static void to_json(Json& j, T&& val)
+    static void to_json(Json &j, T &&val) noexcept(
+        noexcept(::nlohmann::to_json(j, std::forward<T>(val))))
     {
-        ::nlohmann::to_json(j, std::forward<T>(val));
+      ::nlohmann::to_json(j, std::forward<T>(val));
     }
 };
 
@@ -888,7 +948,7 @@ class basic_json
 {
   private:
     template <::nlohmann::value_t> friend struct detail::external_constructor;
-    template <typename Json> friend std::string detail::type_name(Json const &);
+    template <typename Json> friend std::string detail::type_name(const  Json &);
     /// workaround type for MSVC
     using basic_json_t = basic_json<ObjectType, ArrayType, StringType,
           BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType,
@@ -2967,16 +3027,16 @@ class basic_json
                                                       uncvref_t<T>>::value,
             int> = 0>
     // do we really want the uncvref ? if a user call get<int &>, shouldn't we
-    // i know there is a special behaviour for boolean_t* and such
     // static assert ?
+    // i know there is a special behaviour for boolean_t* and such
     auto get() const noexcept(noexcept(JSONSerializer<uncvref_t<T>>::from_json(
-        std::declval<basic_json_t const &>(), std::declval<uncvref_t<T> &>())))
+        std::declval<const basic_json_t &>(), std::declval<uncvref_t<T> &>())))
         -> uncvref_t<T>
     {
       using type = uncvref_t<T>;
-      static_assert(std::is_default_constructible<type>::value &&
+      static_assert(std::is_default_constructible<type>::value and
                         std::is_copy_constructible<type>::value,
-                    "user-defined types must be DefaultConstructible and "
+                    "Types must be DefaultConstructible and "
                     "CopyConstructible when used with get");
       type ret;
       JSONSerializer<type>::from_json(*this, ret);
@@ -2992,12 +3052,11 @@ class basic_json
                         detail::has_non_default_from_json<basic_json_t,
                                                           uncvref_t<T>>::value,
                     int> = 0>
-    constexpr uncvref_t<T> get() const noexcept(noexcept(JSONSerializer<T>::from_json(std::declval<basic_json_t const&>())))
+    uncvref_t<T> get() const noexcept(noexcept(JSONSerializer<T>::from_json(std::declval<const basic_json_t &>())))
     {
       return JSONSerializer<T>::from_json(*this);
     }
 
-    // TODO what to do with those...
     /*!
     @brief get a pointer value (explicit)