support construction from other basic_json types
Before this patch, `basic_json` types with different template arguments were treated as `CompatibleArrayType`. Which sometimes leads to recursive calls and stack overflows. This patch adds a constructor and a `get` overload to deal with different `basic_json` types.
This commit is contained in:
		
							parent
							
								
									c22f2d41f3
								
							
						
					
					
						commit
						8711ec6034
					
				
					 5 changed files with 281 additions and 8 deletions
				
			
		|  | @ -233,7 +233,7 @@ struct is_compatible_complete_type | |||
| { | ||||
|     static constexpr bool value = | ||||
|         not std::is_base_of<std::istream, CompatibleCompleteType>::value and | ||||
|         not std::is_same<BasicJsonType, CompatibleCompleteType>::value and | ||||
|         not is_basic_json<CompatibleCompleteType>::value and | ||||
|         not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and | ||||
|         has_to_json<BasicJsonType, CompatibleCompleteType>::value; | ||||
| }; | ||||
|  |  | |||
|  | @ -1207,6 +1207,7 @@ class basic_json | |||
|     - @a CompatibleType is not derived from `std::istream`, | ||||
|     - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move | ||||
|          constructors), | ||||
|     - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments) | ||||
|     - @a CompatibleType is not a @ref basic_json nested type (e.g., | ||||
|          @ref json_pointer, @ref iterator, etc ...) | ||||
|     - @ref @ref json_serializer<U> has a | ||||
|  | @ -1242,6 +1243,77 @@ class basic_json | |||
|         assert_invariant(); | ||||
|     } | ||||
| 
 | ||||
|     /*!
 | ||||
|     @brief create a JSON value from an existing one | ||||
| 
 | ||||
|     This is a constructor for existing @ref basic_json types. | ||||
|     It does not hijack copy/move constructors, since the parameter has different template arguments than the current ones. | ||||
| 
 | ||||
|     The constructor tries to convert the internal @ref m_value of the parameter. | ||||
| 
 | ||||
|     @tparam BasicJsonType a type such that: | ||||
|     - @a BasicJsonType is a @ref basic_json type. | ||||
|     - @a BasicJsonType has different template arguments than @ref basic_json_t. | ||||
| 
 | ||||
|     @param[in] val the @ref basic_json value to be converted. | ||||
| 
 | ||||
|     @complexity Usually linear in the size of the passed @a val, also | ||||
|                 depending on the implementation of the called `to_json()` | ||||
|                 method. | ||||
| 
 | ||||
|     @exceptionsafety Depends on the called constructor. For types directly | ||||
|     supported by the library (i.e., all types for which no `to_json()` function | ||||
|     was provided), strong guarantee holds: if an exception is thrown, there are | ||||
|     no changes to any JSON value. | ||||
| 
 | ||||
|     @since version 3.1.2 | ||||
|     */ | ||||
|     template <typename BasicJsonType, | ||||
|               detail::enable_if_t< | ||||
|                   detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0> | ||||
|     basic_json(const BasicJsonType& val) | ||||
|     { | ||||
|         using other_boolean_t = typename BasicJsonType::boolean_t; | ||||
|         using other_number_float_t = typename BasicJsonType::number_float_t; | ||||
|         using other_number_integer_t = typename BasicJsonType::number_integer_t; | ||||
|         using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; | ||||
|         using other_string_t = typename BasicJsonType::string_t; | ||||
|         using other_object_t = typename BasicJsonType::object_t; | ||||
|         using other_array_t = typename BasicJsonType::array_t; | ||||
| 
 | ||||
|         switch (val.type()) | ||||
|         { | ||||
|             case value_t::boolean: | ||||
|                 JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>()); | ||||
|                 break; | ||||
|             case value_t::number_float: | ||||
|                 JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>()); | ||||
|                 break; | ||||
|             case value_t::number_integer: | ||||
|                 JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>()); | ||||
|                 break; | ||||
|             case value_t::number_unsigned: | ||||
|                 JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>()); | ||||
|                 break; | ||||
|             case value_t::string: | ||||
|                 JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>()); | ||||
|                 break; | ||||
|             case value_t::object: | ||||
|                 JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>()); | ||||
|                 break; | ||||
|             case value_t::array: | ||||
|                 JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>()); | ||||
|                 break; | ||||
|             case value_t::null: | ||||
|                 *this = nullptr; | ||||
|                 break; | ||||
|             case value_t::discarded: | ||||
|                 m_type = value_t::discarded; | ||||
|                 break; | ||||
|         } | ||||
|         assert_invariant(); | ||||
|     } | ||||
| 
 | ||||
|     /*!
 | ||||
|     @brief create a container (array or object) from an initializer list | ||||
| 
 | ||||
|  | @ -2414,6 +2486,31 @@ class basic_json | |||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     /*!
 | ||||
|     @brief get special-case overload | ||||
| 
 | ||||
|     This overloads converts the current @ref basic_json in a different @ref basic_json type | ||||
| 
 | ||||
|     @tparam BasicJsonType == @ref basic_json | ||||
| 
 | ||||
|     @return a copy of *this, converted into @tparam BasicJsonType | ||||
| 
 | ||||
|     @complexity Depending on the implementation of the called `from_json()` | ||||
|                 method. | ||||
| 
 | ||||
|     @since version 3.1.2 | ||||
|     */ | ||||
|     template<typename BasicJsonType, detail::enable_if_t< | ||||
|                  not std::is_same<BasicJsonType, basic_json>::value and | ||||
|                  detail::is_basic_json<BasicJsonType>::value | ||||
|                  , | ||||
|                  int> = 0> | ||||
|     BasicJsonType get() const | ||||
|     { | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /*!
 | ||||
|     @brief get a value (explicit) | ||||
| 
 | ||||
|  | @ -2455,7 +2552,7 @@ class basic_json | |||
|     */ | ||||
|     template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>, | ||||
|              detail::enable_if_t < | ||||
|                  not std::is_same<basic_json_t, ValueType>::value and | ||||
|                  not detail::is_basic_json<ValueType>::value and | ||||
|                  detail::has_from_json<basic_json_t, ValueType>::value and | ||||
|                  not detail::has_non_default_from_json<basic_json_t, ValueType>::value, | ||||
|                  int> = 0> | ||||
|  | @ -2721,7 +2818,8 @@ class basic_json | |||
|     template < typename ValueType, typename std::enable_if < | ||||
|                    not std::is_pointer<ValueType>::value and | ||||
|                    not std::is_same<ValueType, detail::json_ref<basic_json>>::value and | ||||
|                    not std::is_same<ValueType, typename string_t::value_type>::value | ||||
|                    not std::is_same<ValueType, typename string_t::value_type>::value and | ||||
|                    not detail::is_basic_json<ValueType>::value | ||||
| #ifndef _MSC_VER  // fix for issue #167 operator<< ambiguity under VS2015
 | ||||
|                    and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value | ||||
| #endif | ||||
|  |  | |||
|  | @ -466,7 +466,7 @@ struct is_compatible_complete_type | |||
| { | ||||
|     static constexpr bool value = | ||||
|         not std::is_base_of<std::istream, CompatibleCompleteType>::value and | ||||
|         not std::is_same<BasicJsonType, CompatibleCompleteType>::value and | ||||
|         not is_basic_json<CompatibleCompleteType>::value and | ||||
|         not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and | ||||
|         has_to_json<BasicJsonType, CompatibleCompleteType>::value; | ||||
| }; | ||||
|  | @ -10805,6 +10805,7 @@ class basic_json | |||
|     - @a CompatibleType is not derived from `std::istream`, | ||||
|     - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move | ||||
|          constructors), | ||||
|     - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments) | ||||
|     - @a CompatibleType is not a @ref basic_json nested type (e.g., | ||||
|          @ref json_pointer, @ref iterator, etc ...) | ||||
|     - @ref @ref json_serializer<U> has a | ||||
|  | @ -10840,6 +10841,77 @@ class basic_json | |||
|         assert_invariant(); | ||||
|     } | ||||
| 
 | ||||
|     /*!
 | ||||
|     @brief create a JSON value from an existing one | ||||
| 
 | ||||
|     This is a constructor for existing @ref basic_json types. | ||||
|     It does not hijack copy/move constructors, since the parameter has different template arguments than the current ones. | ||||
| 
 | ||||
|     The constructor tries to convert the internal @ref m_value of the parameter. | ||||
| 
 | ||||
|     @tparam BasicJsonType a type such that: | ||||
|     - @a BasicJsonType is a @ref basic_json type. | ||||
|     - @a BasicJsonType has different template arguments than @ref basic_json_t. | ||||
| 
 | ||||
|     @param[in] val the @ref basic_json value to be converted. | ||||
| 
 | ||||
|     @complexity Usually linear in the size of the passed @a val, also | ||||
|                 depending on the implementation of the called `to_json()` | ||||
|                 method. | ||||
| 
 | ||||
|     @exceptionsafety Depends on the called constructor. For types directly | ||||
|     supported by the library (i.e., all types for which no `to_json()` function | ||||
|     was provided), strong guarantee holds: if an exception is thrown, there are | ||||
|     no changes to any JSON value. | ||||
| 
 | ||||
|     @since version 3.1.2 | ||||
|     */ | ||||
|     template <typename BasicJsonType, | ||||
|               detail::enable_if_t< | ||||
|                   detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0> | ||||
|     basic_json(const BasicJsonType& val) | ||||
|     { | ||||
|         using other_boolean_t = typename BasicJsonType::boolean_t; | ||||
|         using other_number_float_t = typename BasicJsonType::number_float_t; | ||||
|         using other_number_integer_t = typename BasicJsonType::number_integer_t; | ||||
|         using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; | ||||
|         using other_string_t = typename BasicJsonType::string_t; | ||||
|         using other_object_t = typename BasicJsonType::object_t; | ||||
|         using other_array_t = typename BasicJsonType::array_t; | ||||
| 
 | ||||
|         switch (val.type()) | ||||
|         { | ||||
|             case value_t::boolean: | ||||
|                 JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>()); | ||||
|                 break; | ||||
|             case value_t::number_float: | ||||
|                 JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>()); | ||||
|                 break; | ||||
|             case value_t::number_integer: | ||||
|                 JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>()); | ||||
|                 break; | ||||
|             case value_t::number_unsigned: | ||||
|                 JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>()); | ||||
|                 break; | ||||
|             case value_t::string: | ||||
|                 JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>()); | ||||
|                 break; | ||||
|             case value_t::object: | ||||
|                 JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>()); | ||||
|                 break; | ||||
|             case value_t::array: | ||||
|                 JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>()); | ||||
|                 break; | ||||
|             case value_t::null: | ||||
|                 *this = nullptr; | ||||
|                 break; | ||||
|             case value_t::discarded: | ||||
|                 m_type = value_t::discarded; | ||||
|                 break; | ||||
|         } | ||||
|         assert_invariant(); | ||||
|     } | ||||
| 
 | ||||
|     /*!
 | ||||
|     @brief create a container (array or object) from an initializer list | ||||
| 
 | ||||
|  | @ -12012,6 +12084,31 @@ class basic_json | |||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     /*!
 | ||||
|     @brief get special-case overload | ||||
| 
 | ||||
|     This overloads converts the current @ref basic_json in a different @ref basic_json type | ||||
| 
 | ||||
|     @tparam BasicJsonType == @ref basic_json | ||||
| 
 | ||||
|     @return a copy of *this, converted into @tparam BasicJsonType | ||||
| 
 | ||||
|     @complexity Depending on the implementation of the called `from_json()` | ||||
|                 method. | ||||
| 
 | ||||
|     @since version 3.1.2 | ||||
|     */ | ||||
|     template<typename BasicJsonType, detail::enable_if_t< | ||||
|                  not std::is_same<BasicJsonType, basic_json>::value and | ||||
|                  detail::is_basic_json<BasicJsonType>::value | ||||
|                  , | ||||
|                  int> = 0> | ||||
|     BasicJsonType get() const | ||||
|     { | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /*!
 | ||||
|     @brief get a value (explicit) | ||||
| 
 | ||||
|  | @ -12053,7 +12150,7 @@ class basic_json | |||
|     */ | ||||
|     template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>, | ||||
|              detail::enable_if_t < | ||||
|                  not std::is_same<basic_json_t, ValueType>::value and | ||||
|                  not detail::is_basic_json<ValueType>::value and | ||||
|                  detail::has_from_json<basic_json_t, ValueType>::value and | ||||
|                  not detail::has_non_default_from_json<basic_json_t, ValueType>::value, | ||||
|                  int> = 0> | ||||
|  | @ -12319,7 +12416,8 @@ class basic_json | |||
|     template < typename ValueType, typename std::enable_if < | ||||
|                    not std::is_pointer<ValueType>::value and | ||||
|                    not std::is_same<ValueType, detail::json_ref<basic_json>>::value and | ||||
|                    not std::is_same<ValueType, typename string_t::value_type>::value | ||||
|                    not std::is_same<ValueType, typename string_t::value_type>::value and | ||||
|                    not detail::is_basic_json<ValueType>::value | ||||
| #ifndef _MSC_VER  // fix for issue #167 operator<< ambiguity under VS2015
 | ||||
|                    and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value | ||||
| #endif | ||||
|  |  | |||
|  | @ -316,8 +316,8 @@ TEST_CASE("object inspection") | |||
|     SECTION("round trips") | ||||
|     { | ||||
|         for (const auto& s : | ||||
|                 {"3.141592653589793", "1000000000000000010E5" | ||||
|                 }) | ||||
|     {"3.141592653589793", "1000000000000000010E5" | ||||
|     }) | ||||
|         { | ||||
|             json j1 = json::parse(s); | ||||
|             std::string s1 = j1.dump(); | ||||
|  |  | |||
|  | @ -693,6 +693,83 @@ TEST_CASE("custom serializer that does adl by default", "[udt]") | |||
|     CHECK(me == cj.get<udt::person>()); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("different basic_json types conversions") | ||||
| { | ||||
|     using json = nlohmann::json; | ||||
| 
 | ||||
|     SECTION("null") | ||||
|     { | ||||
|         json j; | ||||
|         custom_json cj = j; | ||||
|         CHECK(cj == nullptr); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("boolean") | ||||
|     { | ||||
|         json j = true; | ||||
|         custom_json cj = j; | ||||
|         CHECK(cj == true); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("discarded") | ||||
|     { | ||||
|         json j(json::value_t::discarded); | ||||
|         custom_json cj; | ||||
|         CHECK_NOTHROW(cj = j); | ||||
|         CHECK(cj.type() == custom_json::value_t::discarded); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("array") | ||||
|     { | ||||
|         json j = {1, 2, 3}; | ||||
|         custom_json cj = j; | ||||
|         CHECK((cj == std::vector<int> {1, 2, 3})); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("integer") | ||||
|     { | ||||
|         json j = 42; | ||||
|         custom_json cj = j; | ||||
|         CHECK(cj == 42); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("float") | ||||
|     { | ||||
|         json j = 42.0; | ||||
|         custom_json cj = j; | ||||
|         CHECK(cj == 42.0); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("unsigned") | ||||
|     { | ||||
|         json j = 42u; | ||||
|         custom_json cj = j; | ||||
|         CHECK(cj == 42u); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("string") | ||||
|     { | ||||
|         json j = "forty-two"; | ||||
|         custom_json cj = j; | ||||
|         CHECK(cj == "forty-two"); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("object") | ||||
|     { | ||||
|         json j = {{"forty", "two"}}; | ||||
|         custom_json cj = j; | ||||
|         auto m = j.get<std::map<std::string, std::string>>(); | ||||
|         CHECK(cj == m); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("get<custom_json>") | ||||
|     { | ||||
|         json j = 42; | ||||
|         custom_json cj = j.get<custom_json>(); | ||||
|         CHECK(cj == 42); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| struct incomplete; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue