From aea648bb7a89dcb99ed683825f7e4a42d678077c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= <theo.delrieu@tanker.io>
Date: Fri, 7 Sep 2018 12:42:49 +0200
Subject: [PATCH] remove now-useless traits. check for is_basic_json where
 needed

---
 .../nlohmann/detail/conversions/to_json.hpp   |  5 +-
 include/nlohmann/detail/meta/type_traits.hpp  | 48 +++++++++++-----
 include/nlohmann/json.hpp                     |  2 +-
 single_include/nlohmann/json.hpp              | 55 +++++++++++++------
 4 files changed, 76 insertions(+), 34 deletions(-)

diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp
index 8f2bd266..0a802369 100644
--- a/include/nlohmann/detail/conversions/to_json.hpp
+++ b/include/nlohmann/detail/conversions/to_json.hpp
@@ -253,7 +253,8 @@ template <typename BasicJsonType, typename CompatibleArrayType,
                       CompatibleArrayType>::value and
                       not is_compatible_object_type<
                           BasicJsonType, CompatibleArrayType>::value and
-                      not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value,
+                      not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and
+                      not is_basic_json<CompatibleArrayType>::value,
                       int> = 0>
 void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
 {
@@ -274,7 +275,7 @@ void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
 }
 
 template<typename BasicJsonType, typename CompatibleObjectType,
-         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
+         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value and not is_basic_json<CompatibleObjectType>::value, int> = 0>
 void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
 {
     external_constructor<value_t::object>::construct(j, obj);
diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp
index 0383472b..b358de65 100644
--- a/include/nlohmann/detail/meta/type_traits.hpp
+++ b/include/nlohmann/detail/meta/type_traits.hpp
@@ -44,6 +44,18 @@ using key_type_t = typename T::key_type;
 template <typename T>
 using value_type_t = typename T::value_type;
 
+template <typename T>
+using difference_type_t = typename T::difference_type;
+
+template <typename T>
+using pointer_t = typename T::pointer;
+
+template <typename T>
+using reference_t = typename T::reference;
+
+template <typename T>
+using iterator_category_t = typename T::iterator_category;
+
 template <typename T>
 using iterator_t = typename T::iterator;
 
@@ -57,6 +69,24 @@ using from_json_function = decltype(T::from_json(std::declval<Args>()...));
 // is_ functions //
 ///////////////////
 
+template <typename T, typename = void>
+struct is_iterator_traits : std::false_type {};
+
+template <typename T>
+struct is_iterator_traits<std::iterator_traits<T>>
+{
+  private:
+    using traits = std::iterator_traits<T>;
+
+  public:
+    static constexpr auto value =
+        is_detected<value_type_t, traits>::value &&
+        is_detected<difference_type_t, traits>::value &&
+        is_detected<pointer_t, traits>::value &&
+        is_detected<iterator_category_t, traits>::value &&
+        is_detected<reference_t, traits>::value;
+};
+
 // source: https://stackoverflow.com/a/37193089/4116453
 
 template <typename T, typename = void>
@@ -108,15 +138,6 @@ template <typename BasicJsonType, typename CompatibleStringType>
 struct is_compatible_string_type
     : is_compatible_string_type_impl<BasicJsonType, CompatibleStringType> {};
 
-template<typename BasicJsonType, typename T>
-struct is_basic_json_nested_type
-{
-    static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
-                                  std::is_same<T, typename BasicJsonType::const_iterator>::value or
-                                  std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
-                                  std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value;
-};
-
 template <typename BasicJsonType, typename CompatibleArrayType, typename = void>
 struct is_compatible_array_type_impl : std::false_type {};
 
@@ -126,8 +147,10 @@ struct is_compatible_array_type_impl <
     enable_if_t<is_detected<value_type_t, CompatibleArrayType>::value and
     is_detected<iterator_t, CompatibleArrayType>::value >>
 {
-    static constexpr auto value = not(
-                                      is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>::value);
+    // This is needed because json_reverse_iterator has a ::iterator type...
+    // Therefore it is detected as a CompatibleArrayType.
+    // The real fix would be to have an Iterable concept.
+    static constexpr bool value = not is_iterator_traits<std::iterator_traits<CompatibleArrayType>>::value;
 };
 
 template <typename BasicJsonType, typename CompatibleArrayType>
@@ -204,9 +227,6 @@ struct is_compatible_type_impl <
     enable_if_t<is_complete_type<CompatibleType>::value >>
 {
     static constexpr bool value =
-        not std::is_base_of<std::istream, CompatibleType>::value and
-        not is_basic_json<CompatibleType>::value and
-        not is_basic_json_nested_type<BasicJsonType, CompatibleType>::value and
         has_to_json<BasicJsonType, CompatibleType>::value;
 };
 
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index 1f39e3ec..ee78c1c1 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -1243,7 +1243,7 @@ class basic_json
     template <typename CompatibleType,
               typename U = detail::uncvref_t<CompatibleType>,
               detail::enable_if_t<
-                  detail::is_compatible_type<basic_json_t, U>::value, int> = 0>
+                  not detail::is_basic_json<U>::value and detail::is_compatible_type<basic_json_t, U>::value, int> = 0>
     basic_json(CompatibleType && val) noexcept(noexcept(
                 JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
                                            std::forward<CompatibleType>(val))))
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 1754c9a9..96e469ee 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -404,6 +404,18 @@ using key_type_t = typename T::key_type;
 template <typename T>
 using value_type_t = typename T::value_type;
 
+template <typename T>
+using difference_type_t = typename T::difference_type;
+
+template <typename T>
+using pointer_t = typename T::pointer;
+
+template <typename T>
+using reference_t = typename T::reference;
+
+template <typename T>
+using iterator_category_t = typename T::iterator_category;
+
 template <typename T>
 using iterator_t = typename T::iterator;
 
@@ -417,6 +429,24 @@ using from_json_function = decltype(T::from_json(std::declval<Args>()...));
 // is_ functions //
 ///////////////////
 
+template <typename T, typename = void>
+struct is_iterator_traits : std::false_type {};
+
+template <typename T>
+struct is_iterator_traits<std::iterator_traits<T>>
+{
+  private:
+    using traits = std::iterator_traits<T>;
+
+  public:
+    static constexpr auto value =
+        is_detected<value_type_t, traits>::value &&
+        is_detected<difference_type_t, traits>::value &&
+        is_detected<pointer_t, traits>::value &&
+        is_detected<iterator_category_t, traits>::value &&
+        is_detected<reference_t, traits>::value;
+};
+
 // source: https://stackoverflow.com/a/37193089/4116453
 
 template <typename T, typename = void>
@@ -468,15 +498,6 @@ template <typename BasicJsonType, typename CompatibleStringType>
 struct is_compatible_string_type
     : is_compatible_string_type_impl<BasicJsonType, CompatibleStringType> {};
 
-template<typename BasicJsonType, typename T>
-struct is_basic_json_nested_type
-{
-    static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
-                                  std::is_same<T, typename BasicJsonType::const_iterator>::value or
-                                  std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
-                                  std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value;
-};
-
 template <typename BasicJsonType, typename CompatibleArrayType, typename = void>
 struct is_compatible_array_type_impl : std::false_type {};
 
@@ -486,8 +507,10 @@ struct is_compatible_array_type_impl <
     enable_if_t<is_detected<value_type_t, CompatibleArrayType>::value and
     is_detected<iterator_t, CompatibleArrayType>::value >>
 {
-    static constexpr auto value = not(
-                                      is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>::value);
+    // This is needed because json_reverse_iterator has a ::iterator type...
+    // Therefore it is detected as a CompatibleArrayType.
+    // The real fix would be to have an Iterable concept.
+    static constexpr bool value = not is_iterator_traits<std::iterator_traits<CompatibleArrayType>>::value;
 };
 
 template <typename BasicJsonType, typename CompatibleArrayType>
@@ -564,9 +587,6 @@ struct is_compatible_type_impl <
     enable_if_t<is_complete_type<CompatibleType>::value >>
 {
     static constexpr bool value =
-        not std::is_base_of<std::istream, CompatibleType>::value and
-        not is_basic_json<CompatibleType>::value and
-        not is_basic_json_nested_type<BasicJsonType, CompatibleType>::value and
         has_to_json<BasicJsonType, CompatibleType>::value;
 };
 
@@ -1752,7 +1772,8 @@ template <typename BasicJsonType, typename CompatibleArrayType,
                       CompatibleArrayType>::value and
                       not is_compatible_object_type<
                           BasicJsonType, CompatibleArrayType>::value and
-                      not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value,
+                      not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and
+                      not is_basic_json<CompatibleArrayType>::value,
                       int> = 0>
 void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
 {
@@ -1773,7 +1794,7 @@ void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
 }
 
 template<typename BasicJsonType, typename CompatibleObjectType,
-         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
+         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value and not is_basic_json<CompatibleObjectType>::value, int> = 0>
 void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
 {
     external_constructor<value_t::object>::construct(j, obj);
@@ -12292,7 +12313,7 @@ class basic_json
     template <typename CompatibleType,
               typename U = detail::uncvref_t<CompatibleType>,
               detail::enable_if_t<
-                  detail::is_compatible_type<basic_json_t, U>::value, int> = 0>
+                  not detail::is_basic_json<U>::value and detail::is_compatible_type<basic_json_t, U>::value, int> = 0>
     basic_json(CompatibleType && val) noexcept(noexcept(
                 JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
                                            std::forward<CompatibleType>(val))))