diff --git a/include/nlohmann/detail/meta.hpp b/include/nlohmann/detail/meta.hpp index 49f1069f..b251afb6 100644 --- a/include/nlohmann/detail/meta.hpp +++ b/include/nlohmann/detail/meta.hpp @@ -233,7 +233,7 @@ struct is_compatible_complete_type { static constexpr bool value = not std::is_base_of::value and - not std::is_same::value and + not is_basic_json::value and not is_basic_json_nested_type::value and has_to_json::value; }; diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 10183262..1b15bdaf 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -704,116 +704,126 @@ class binary_writer oa->write_characters(vec.data(), sizeof(NumberType)); } - template + // UBJSON: write number (floating point) + template::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { - if (std::is_floating_point::value) + if (add_prefix) + { + oa->write_character(static_cast('D')); // float64 + } + write_number(n); + } + + // UBJSON: write number (unsigned integer) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { - oa->write_character(static_cast('D')); // float64 + oa->write_character(static_cast('i')); // int8 } - write_number(n); + write_number(static_cast(n)); } - else if (std::is_unsigned::value) + else if (n <= (std::numeric_limits::max)()) { - if (n <= (std::numeric_limits::max)()) + if (add_prefix) { - if (add_prefix) - { - oa->write_character(static_cast('i')); // int8 - } - write_number(static_cast(n)); + oa->write_character(static_cast('U')); // uint8 } - else if (n <= (std::numeric_limits::max)()) + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) { - if (add_prefix) - { - oa->write_character(static_cast('U')); // uint8 - } - write_number(static_cast(n)); + oa->write_character(static_cast('I')); // int16 } - else if (n <= (std::numeric_limits::max)()) + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) { - if (add_prefix) - { - oa->write_character(static_cast('I')); // int16 - } - write_number(static_cast(n)); + oa->write_character(static_cast('l')); // int32 } - else if (n <= (std::numeric_limits::max)()) + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) { - if (add_prefix) - { - oa->write_character(static_cast('l')); // int32 - } - write_number(static_cast(n)); - } - else if (n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('L')); // int64 - } - write_number(static_cast(n)); - } - else - { - JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); + oa->write_character(static_cast('L')); // int64 } + write_number(static_cast(n)); } else { - if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('i')); // int8 - } - write_number(static_cast(n)); - } - else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('U')); // uint8 - } - write_number(static_cast(n)); - } - else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('I')); // int16 - } - write_number(static_cast(n)); - } - else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('l')); // int32 - } - write_number(static_cast(n)); - } - else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('L')); // int64 - } - write_number(static_cast(n)); - } - // LCOV_EXCL_START - else - { - JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); - } - // LCOV_EXCL_STOP + JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); } } + // UBJSON: write number (signed integer) + template::value and + not std::is_floating_point::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('i')); // int8 + } + write_number(static_cast(n)); + } + else if (static_cast((std::numeric_limits::min)()) <= n and n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(static_cast('U')); // uint8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('I')); // int16 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('l')); // int32 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('L')); // int64 + } + write_number(static_cast(n)); + } + // LCOV_EXCL_START + else + { + JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); + } + // LCOV_EXCL_STOP + } + /*! @brief determine the type prefix of container values diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index cef504ff..3a52ec8c 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1210,6 +1210,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 has a @@ -1245,6 +1246,78 @@ 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 ::value and not std::is_same::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::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + 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 @@ -2417,6 +2490,29 @@ 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::value and + detail::is_basic_json::value, int> = 0> + BasicJsonType get() const + { + return *this; + } + /*! @brief get a value (explicit) @@ -2458,7 +2554,7 @@ class basic_json */ template, detail::enable_if_t < - not std::is_same::value and + not detail::is_basic_json::value and detail::has_from_json::value and not detail::has_non_default_from_json::value, int> = 0> @@ -2724,7 +2820,8 @@ class basic_json template < typename ValueType, typename std::enable_if < not std::is_pointer::value and not std::is_same>::value and - not std::is_same::value + not std::is_same::value and + not detail::is_basic_json::value #ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 and not std::is_same>::value #endif diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8aaa1f68..9fc301e2 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -466,7 +466,7 @@ struct is_compatible_complete_type { static constexpr bool value = not std::is_base_of::value and - not std::is_same::value and + not is_basic_json::value and not is_basic_json_nested_type::value and has_to_json::value; }; @@ -7401,116 +7401,126 @@ class binary_writer oa->write_characters(vec.data(), sizeof(NumberType)); } - template + // UBJSON: write number (floating point) + template::value, int>::type = 0> void write_number_with_ubjson_prefix(const NumberType n, const bool add_prefix) { - if (std::is_floating_point::value) + if (add_prefix) + { + oa->write_character(static_cast('D')); // float64 + } + write_number(n); + } + + // UBJSON: write number (unsigned integer) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (n <= static_cast((std::numeric_limits::max)())) { if (add_prefix) { - oa->write_character(static_cast('D')); // float64 + oa->write_character(static_cast('i')); // int8 } - write_number(n); + write_number(static_cast(n)); } - else if (std::is_unsigned::value) + else if (n <= (std::numeric_limits::max)()) { - if (n <= (std::numeric_limits::max)()) + if (add_prefix) { - if (add_prefix) - { - oa->write_character(static_cast('i')); // int8 - } - write_number(static_cast(n)); + oa->write_character(static_cast('U')); // uint8 } - else if (n <= (std::numeric_limits::max)()) + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) { - if (add_prefix) - { - oa->write_character(static_cast('U')); // uint8 - } - write_number(static_cast(n)); + oa->write_character(static_cast('I')); // int16 } - else if (n <= (std::numeric_limits::max)()) + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) { - if (add_prefix) - { - oa->write_character(static_cast('I')); // int16 - } - write_number(static_cast(n)); + oa->write_character(static_cast('l')); // int32 } - else if (n <= (std::numeric_limits::max)()) + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) { - if (add_prefix) - { - oa->write_character(static_cast('l')); // int32 - } - write_number(static_cast(n)); - } - else if (n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('L')); // int64 - } - write_number(static_cast(n)); - } - else - { - JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); + oa->write_character(static_cast('L')); // int64 } + write_number(static_cast(n)); } else { - if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('i')); // int8 - } - write_number(static_cast(n)); - } - else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('U')); // uint8 - } - write_number(static_cast(n)); - } - else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('I')); // int16 - } - write_number(static_cast(n)); - } - else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('l')); // int32 - } - write_number(static_cast(n)); - } - else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) - { - if (add_prefix) - { - oa->write_character(static_cast('L')); // int64 - } - write_number(static_cast(n)); - } - // LCOV_EXCL_START - else - { - JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); - } - // LCOV_EXCL_STOP + JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); } } + // UBJSON: write number (signed integer) + template::value and + not std::is_floating_point::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('i')); // int8 + } + write_number(static_cast(n)); + } + else if (static_cast((std::numeric_limits::min)()) <= n and n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(static_cast('U')); // uint8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('I')); // int16 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('l')); // int32 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('L')); // int64 + } + write_number(static_cast(n)); + } + // LCOV_EXCL_START + else + { + JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); + } + // LCOV_EXCL_STOP + } + /*! @brief determine the type prefix of container values @@ -11300,6 +11310,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 has a @@ -11335,6 +11346,78 @@ 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 ::value and not std::is_same::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::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + 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 @@ -12507,6 +12590,29 @@ 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::value and + detail::is_basic_json::value, int> = 0> + BasicJsonType get() const + { + return *this; + } + /*! @brief get a value (explicit) @@ -12548,7 +12654,7 @@ class basic_json */ template, detail::enable_if_t < - not std::is_same::value and + not detail::is_basic_json::value and detail::has_from_json::value and not detail::has_non_default_from_json::value, int> = 0> @@ -12814,7 +12920,8 @@ class basic_json template < typename ValueType, typename std::enable_if < not std::is_pointer::value and not std::is_same>::value and - not std::is_same::value + not std::is_same::value and + not detail::is_basic_json::value #ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 and not std::is_same>::value #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d53d5c4b..e5f6dc55 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -104,6 +104,7 @@ foreach(file ${files}) target_compile_definitions(${testcase} PRIVATE CATCH_CONFIG_FAST_COMPILE) target_include_directories(${testcase} PRIVATE "thirdparty/catch") + target_include_directories(${testcase} PRIVATE "thirdparty/fifo_map") target_include_directories(${testcase} PRIVATE ${NLOHMANN_JSON_INCLUDE_BUILD_DIR}) target_link_libraries(${testcase} ${NLOHMANN_JSON_TARGET_NAME}) diff --git a/test/Makefile b/test/Makefile index 4dc39916..51edae66 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ # additional flags CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic -Wcast-align -Wcast-qual -Wno-ctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wno-float-equal -CPPFLAGS += -I ../single_include -I . -I thirdparty/catch -DCATCH_CONFIG_FAST_COMPILE +CPPFLAGS += -I ../single_include -I . -I thirdparty/catch -I thirdparty/fifo_map -DCATCH_CONFIG_FAST_COMPILE SOURCES = src/unit.cpp \ src/unit-algorithms.cpp \ diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 39753d68..f9dad0d5 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -1527,6 +1527,89 @@ TEST_CASE("parser class") }); CHECK(j_empty_array == json()); } + + SECTION("skip in GeoJSON") + { + auto geojsonExample = R"( + { "type": "FeatureCollection", + "features": [ + { "type": "Feature", + "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, + "properties": {"prop0": "value0"} + }, + { "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0] + ] + }, + "properties": { + "prop0": "value0", + "prop1": 0.0 + } + }, + { "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], + [100.0, 1.0], [100.0, 0.0] ] + ] + }, + "properties": { + "prop0": "value0", + "prop1": {"this": "that"} + } + } + ] + })"; + + json::parser_callback_t cb = [&](int depth, json::parse_event_t event, json & parsed) + { + // skip uninteresting events + if (event == json::parse_event_t::value and !parsed.is_primitive()) + { + return false; + } + + switch (event) + { + case json::parse_event_t::key: + { + return true; + } + case json::parse_event_t::value: + { + return false; + } + case json::parse_event_t::object_start: + { + return true; + } + case json::parse_event_t::object_end: + { + return false; + } + case json::parse_event_t::array_start: + { + return true; + } + case json::parse_event_t::array_end: + { + return false; + } + + default: + { + return true; + } + } + }; + + auto j = json::parse(geojsonExample, cb, true); + CHECK(j == json()); + } } SECTION("constructing from contiguous containers") diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 97bdf0c1..c84c2750 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -32,10 +32,72 @@ SOFTWARE. #include using nlohmann::json; +#include "fifo_map.hpp" + #include #include #include +///////////////////////////////////////////////////////////////////// +// for #972 +///////////////////////////////////////////////////////////////////// + +template +using my_workaround_fifo_map = nlohmann::fifo_map, A>; +using my_json = nlohmann::basic_json; + +///////////////////////////////////////////////////////////////////// +// for #977 +///////////////////////////////////////////////////////////////////// + +namespace ns +{ +struct foo +{ + int x; +}; + +template +struct foo_serializer; + +template +struct foo_serializer::value>::type> +{ + template + static void to_json(BasicJsonType& j, const T& value) + { + j = BasicJsonType{{"x", value.x}}; + } + template + static void from_json(const BasicJsonType& j, T& value) // !!! + { + nlohmann::from_json(j.at("x"), value.x); + } +}; + +template +struct foo_serializer < T, typename std::enable_if < !std::is_same::value >::type > +{ + template + static void to_json(BasicJsonType& j, const T& value) noexcept + { + ::nlohmann::to_json(j, value); + } + template + static void from_json(const BasicJsonType& j, T& value) //!!! + { + ::nlohmann::from_json(j, value); + } +}; +} + +using foo_json = nlohmann::basic_json; + +///////////////////////////////////////////////////////////////////// +// for #805 +///////////////////////////////////////////////////////////////////// + namespace { struct nocopy @@ -1436,4 +1498,20 @@ TEST_CASE("regression tests") //CHECK_THROWS_WITH(json::from_ubjson(v_ubjson), // "[json.exception.out_of_range.408] excessive object size: 8658170730974374167"); } + + SECTION("issue #972 - Segmentation fault on G++ when trying to assign json string literal to custom json type") + { + my_json foo = R"([1, 2, 3])"_json; + } + + SECTION("issue #977 - Assigning between different json types") + { + foo_json lj = ns::foo{3}; + ns::foo ff = lj; + CHECK(lj.is_object()); + CHECK(lj.size() == 1); + CHECK(lj["x"] == 3); + CHECK(ff.x == 3); + nlohmann::json nj = lj; // This line works as expected + } } diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index f270fdc4..8a5ac73f 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -693,6 +693,83 @@ TEST_CASE("custom serializer that does adl by default", "[udt]") CHECK(me == cj.get()); } +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 {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>(); + CHECK(cj == m); + } + + SECTION("get") + { + json j = 42; + custom_json cj = j.get(); + CHECK(cj == 42); + } +} + namespace { struct incomplete; @@ -730,6 +807,6 @@ TEST_CASE("Issue #924") // Prevent get>() to throw auto j = json::array(); - (void) j.get(); - (void) j.get>(); + CHECK_NOTHROW(j.get()); + CHECK_NOTHROW(j.get>()); } diff --git a/test/thirdparty/fifo_map/LICENSE.MIT b/test/thirdparty/fifo_map/LICENSE.MIT new file mode 100644 index 00000000..8c59cdf2 --- /dev/null +++ b/test/thirdparty/fifo_map/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2015-2017 Niels Lohmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/test/thirdparty/fifo_map/fifo_map.hpp b/test/thirdparty/fifo_map/fifo_map.hpp new file mode 100644 index 00000000..c281e3be --- /dev/null +++ b/test/thirdparty/fifo_map/fifo_map.hpp @@ -0,0 +1,530 @@ +/* +The code is licensed under the MIT License : + +Copyright (c) 2015-2017 Niels Lohmann. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef NLOHMANN_FIFO_MAP_HPP +#define NLOHMANN_FIFO_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +*/ +namespace nlohmann +{ + +template +class fifo_map_compare +{ + public: + /// constructor given a pointer to a key storage + fifo_map_compare(std::unordered_map* k) : keys(k) {} + + /*! + This function compares two keys with respect to the order in which they + were added to the container. For this, the mapping keys is used. + */ + bool operator()(const Key& lhs, const Key& rhs) const + { + // look up timestamps for both keys + const auto timestamp_lhs = keys->find(lhs); + const auto timestamp_rhs = keys->find(rhs); + + if (timestamp_lhs == keys->end()) + { + // timestamp for lhs not found - cannot be smaller than for rhs + return false; + } + + if (timestamp_rhs == keys->end()) + { + // timestamp for rhs not found - timestamp for lhs is smaller + return true; + } + + // compare timestamps + return timestamp_lhs->second < timestamp_rhs->second; + } + + void add_key(const Key& key) + { + keys->insert({key, timestamp++}); + } + + void remove_key(const Key& key) + { + keys->erase(key); + } + + private: + /// pointer to a mapping from keys to insertion timestamps + std::unordered_map* keys = nullptr; + /// the next valid insertion timestamp + size_t timestamp = 1; +}; + + +template < + class Key, + class T, + class Compare = fifo_map_compare, + class Allocator = std::allocator> + > class fifo_map +{ + public: + using key_type = Key; + using mapped_type = T; + using value_type = std::pair; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using key_compare = Compare; + using allocator_type = Allocator; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + + using internal_map_type = std::map; + + using iterator = typename internal_map_type::iterator; + using const_iterator = typename internal_map_type::const_iterator; + using reverse_iterator = typename internal_map_type::reverse_iterator; + using const_reverse_iterator = typename internal_map_type::const_reverse_iterator; + + public: + /// default constructor + fifo_map() : m_keys(), m_compare(&m_keys), m_map(m_compare) {} + + /// copy constructor + fifo_map(const fifo_map &f) : m_keys(f.m_keys), m_compare(&m_keys), m_map(f.m_map.begin(), f.m_map.end(), m_compare) {} + + /// constructor for a range of elements + template + fifo_map(InputIterator first, InputIterator last) + : m_keys(), m_compare(&m_keys), m_map(m_compare) + { + for (auto it = first; it != last; ++it) + { + insert(*it); + } + } + + /// constructor for a list of elements + fifo_map(std::initializer_list init) : fifo_map() + { + for (auto x : init) + { + insert(x); + } + } + + + /* + * Element access + */ + + /// access specified element with bounds checking + T& at(const Key& key) + { + return m_map.at(key); + } + + /// access specified element with bounds checking + const T& at(const Key& key) const + { + return m_map.at(key); + } + + /// access specified element + T& operator[](const Key& key) + { + m_compare.add_key(key); + return m_map[key]; + } + + /// access specified element + T& operator[](Key&& key) + { + m_compare.add_key(key); + return m_map[key]; + } + + + /* + * Iterators + */ + + /// returns an iterator to the beginning + iterator begin() noexcept + { + return m_map.begin(); + } + + /// returns an iterator to the end + iterator end() noexcept + { + return m_map.end(); + } + + /// returns an iterator to the beginning + const_iterator begin() const noexcept + { + return m_map.begin(); + } + + /// returns an iterator to the end + const_iterator end() const noexcept + { + return m_map.end(); + } + + /// returns an iterator to the beginning + const_iterator cbegin() const noexcept + { + return m_map.cbegin(); + } + + /// returns an iterator to the end + const_iterator cend() const noexcept + { + return m_map.cend(); + } + + /// returns a reverse iterator to the beginning + reverse_iterator rbegin() noexcept + { + return m_map.rbegin(); + } + + /// returns a reverse iterator to the end + reverse_iterator rend() noexcept + { + return m_map.rend(); + } + + /// returns a reverse iterator to the beginning + const_reverse_iterator rbegin() const noexcept + { + return m_map.rbegin(); + } + + /// returns a reverse iterator to the end + const_reverse_iterator rend() const noexcept + { + return m_map.rend(); + } + + /// returns a reverse iterator to the beginning + const_reverse_iterator crbegin() const noexcept + { + return m_map.crbegin(); + } + + /// returns a reverse iterator to the end + const_reverse_iterator crend() const noexcept + { + return m_map.crend(); + } + + + /* + * Capacity + */ + + /// checks whether the container is empty + bool empty() const noexcept + { + return m_map.empty(); + } + + /// returns the number of elements + size_type size() const noexcept + { + return m_map.size(); + } + + /// returns the maximum possible number of elements + size_type max_size() const noexcept + { + return m_map.max_size(); + } + + + /* + * Modifiers + */ + + /// clears the contents + void clear() noexcept + { + m_map.clear(); + m_keys.clear(); + } + + /// insert value + std::pair insert(const value_type& value) + { + m_compare.add_key(value.first); + return m_map.insert(value); + } + + /// insert value + template + std::pair insert( P&& value ) + { + m_compare.add_key(value.first); + return m_map.insert(value); + } + + /// insert value with hint + iterator insert(const_iterator hint, const value_type& value) + { + m_compare.add_key(value.first); + return m_map.insert(hint, value); + } + + /// insert value with hint + iterator insert(const_iterator hint, value_type&& value) + { + m_compare.add_key(value.first); + return m_map.insert(hint, value); + } + + /// insert value range + template + void insert(InputIt first, InputIt last) + { + for (const_iterator it = first; it != last; ++it) + { + m_compare.add_key(it->first); + } + + m_map.insert(first, last); + } + + /// insert value list + void insert(std::initializer_list ilist) + { + for (auto value : ilist) + { + m_compare.add_key(value.first); + } + + m_map.insert(ilist); + } + + /// constructs element in-place + template + std::pair emplace(Args&& ... args) + { + typename fifo_map::value_type value(std::forward(args)...); + m_compare.add_key(value.first); + return m_map.emplace(std::move(value)); + } + + /// constructs element in-place with hint + template + iterator emplace_hint(const_iterator hint, Args&& ... args) + { + typename fifo_map::value_type value(std::forward(args)...); + m_compare.add_key(value.first); + return m_map.emplace_hint(hint, std::move(value)); + } + + /// remove element at position + iterator erase(const_iterator pos) + { + m_compare.remove_key(pos->first); + return m_map.erase(pos); + } + + /// remove elements in range + iterator erase(const_iterator first, const_iterator last) + { + for (const_iterator it = first; it != last; ++it) + { + m_compare.remove_key(it->first); + } + + return m_map.erase(first, last); + } + + /// remove elements with key + size_type erase(const key_type& key) + { + size_type res = m_map.erase(key); + + if (res > 0) + { + m_compare.remove_key(key); + } + + return res; + } + + /// swaps the contents + void swap(fifo_map& other) + { + std::swap(m_map, other.m_map); + std::swap(m_compare, other.m_compare); + std::swap(m_keys, other.m_keys); + } + + + /* + * Lookup + */ + + /// returns the number of elements matching specific key + size_type count(const Key& key) const + { + return m_map.count(key); + } + + /// finds element with specific key + iterator find(const Key& key) + { + return m_map.find(key); + } + + /// finds element with specific key + const_iterator find(const Key& key) const + { + return m_map.find(key); + } + + /// returns range of elements matching a specific key + std::pair equal_range(const Key& key) + { + return m_map.equal_range(key); + } + + /// returns range of elements matching a specific key + std::pair equal_range(const Key& key) const + { + return m_map.equal_range(key); + } + + /// returns an iterator to the first element not less than the given key + iterator lower_bound(const Key& key) + { + return m_map.lower_bound(key); + } + + /// returns an iterator to the first element not less than the given key + const_iterator lower_bound(const Key& key) const + { + return m_map.lower_bound(key); + } + + /// returns an iterator to the first element greater than the given key + iterator upper_bound(const Key& key) + { + return m_map.upper_bound(key); + } + + /// returns an iterator to the first element greater than the given key + const_iterator upper_bound(const Key& key) const + { + return m_map.upper_bound(key); + } + + + /* + * Observers + */ + + /// returns the function that compares keys + key_compare key_comp() const + { + return m_compare; + } + + + /* + * Non-member functions + */ + + friend bool operator==(const fifo_map& lhs, const fifo_map& rhs) + { + return lhs.m_map == rhs.m_map; + } + + friend bool operator!=(const fifo_map& lhs, const fifo_map& rhs) + { + return lhs.m_map != rhs.m_map; + } + + friend bool operator<(const fifo_map& lhs, const fifo_map& rhs) + { + return lhs.m_map < rhs.m_map; + } + + friend bool operator<=(const fifo_map& lhs, const fifo_map& rhs) + { + return lhs.m_map <= rhs.m_map; + } + + friend bool operator>(const fifo_map& lhs, const fifo_map& rhs) + { + return lhs.m_map > rhs.m_map; + } + + friend bool operator>=(const fifo_map& lhs, const fifo_map& rhs) + { + return lhs.m_map >= rhs.m_map; + } + + private: + /// the keys + std::unordered_map m_keys; + /// the comparison object + Compare m_compare; + /// the internal data structure + internal_map_type m_map; +}; + +} + +// specialization of std::swap +namespace std +{ +template +inline void swap(nlohmann::fifo_map& m1, + nlohmann::fifo_map& m2) +{ + m1.swap(m2); +} +} + +#endif