mirror of
https://github.com/nlohmann/json.git
synced 2026-10-06 14:40:32 +00:00
* Fix CI on develop after #5600, #5607, and #5755 - binary_reader: cast the result of -1 - number back to number_integer_t, because a number_integer_t narrower than int is promoted to int, which GCC's -Warith-conversion rejects (ci_test_gcc, ci_test_standards_gcc) - JSON_DELETE_DEPRECATED_FUNCTIONS: declare the deleted stream operators as function templates at namespace scope, because GCC < 5 rejects deleted friend functions ("can't initialize friend function") and Clang 7-9 report a redefinition when a class template has a deleted friend function - docs: give the examples of JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS "Example:" titles and add the page to the docset (style_check) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Ignore libstdc++'s <format> sign change in the sanitizer job libstdc++ 14's <format> initializes a size_t parameter with -1 (GCC bug 119429), so every std::format call fails ci_test_clang_sanitizer under -fsanitize=integer (test-std-format_cpp20). Exclude only the implicit-integer-sign-change check and only that header via -fsanitize-ignorelist. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix the AppVeyor (MSVC 2015-2019) build - binary_reader: emit_signed/emit_unsigned pass integers that do not fit the number types to emit_float as long double, because MSVC's <cmath> has no integer overloads of std::isfinite (C2668 'fpclassify'), from #5607 - scalar comparisons: the friend operators take the JSON type for their noexcept from their parameter, because MSVC 2015/2017 take basic_json as the class template there (C3203) and MSVC 2019 16.0 does not see member types or template parameters, from #5751 - unit-conversions2: skip the !is_nothrow_constructible static_assert for std::optional on MSVC 2017, which evaluates the conditional noexcept as true (C2607), from #5754 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Re-amalgamate Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Avoid MSVC 2015's C4800 for enums with underlying type bool MSVC 2015 warns about any conversion to bool (C4800), even with an explicit cast, so the enum conversions from #5754 (#5671) failed the AppVeyor build with /WX. Convert to bool by comparing with zero via the new detail::bool_aware_static_cast, and keep doctest from printing the enum in the test. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Skip the #5650 allocator test on MSVC 2015 debug builds MSVC 2015's debug STL constructs the containers' debug proxies through the allocator in noexcept constructors, so countdown_allocator's failing construction crashes test-allocator (SIGSEGV) instead of throwing std::bad_alloc. Use the guard #5585 uses for the same reason. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Skip deleted-function detection checks on MSVC 2015 MSVC 2015 does not treat selecting a deleted function in decltype as a substitution failure, so the detection traits in unit-delete_deprecated_functions (#5755) and the integral-key checks in unit-element_access2 (#5657) report deleted overloads as callable there. Calling them still fails to compile. Skip those checks for _MSC_VER < 1910, and use the stream operators for real in the runtime section, so MSVC 2015 still compiles them with the macro set. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Split the Visual Studio 2017 AppVeyor jobs in two The VS 2017 jobs hit AppVeyor's 60-minute limit per job while still compiling the tests (77 of about 108 test targets after 58 minutes). They pass /std:c++17 for everything anyway, so build only the C++17 variant of each test (JSON_TestStandards=17), and split the unit test files across two jobs each with the new JSON_TestShard=<index>/<count> option, which keeps every <count>-th test file starting at <index>. The extra variants of single test files are built in shard 0 only. CMAKE_OPTIONS is no longer quoted in appveyor.yml, so that it can hold more than one option. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix std::terminate when converting std::optional with MSVC 2017 MSVC 2017 evaluates std::is_nothrow_assignable<json&, const T&> as true even if T's to_json throws, so to_json(json&, const std::optional<T>&) was noexcept there and the exception from #5642's test called std::terminate instead of propagating. Make that conversion never noexcept on MSVC 2017; all other compilers keep the exact condition. The static_asserts on the condition are skipped for MSVC 2017; the runtime check that the exception propagates still runs there. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
230 lines
10 KiB
C++
230 lines
10 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
// | | |__ | | | | | | version 3.12.0
|
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
//
|
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "doctest_compatibility.h"
|
|
|
|
// This file tests the opt-in JSON_DELETE_DEPRECATED_FUNCTIONS, so it defines the
|
|
// macro itself instead of relying on the build configuration.
|
|
#ifdef JSON_DELETE_DEPRECATED_FUNCTIONS
|
|
#undef JSON_DELETE_DEPRECATED_FUNCTIONS
|
|
#endif
|
|
#define JSON_DELETE_DEPRECATED_FUNCTIONS 1
|
|
|
|
#include <nlohmann/json.hpp>
|
|
using nlohmann::json;
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <istream>
|
|
#include <ostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
namespace
|
|
{
|
|
template<typename...>
|
|
struct make_void
|
|
{
|
|
using type = void;
|
|
};
|
|
|
|
template<typename... Ts>
|
|
using void_t = typename make_void<Ts...>::type;
|
|
|
|
// A deleted function is still found by overload resolution, but naming it in an
|
|
// unevaluated operand is ill-formed, so each of the following traits is false
|
|
// exactly if the expression selects a deleted (or no) function.
|
|
#define JSON_TEST_DETECT(name, expr) \
|
|
template<typename J, typename = void> \
|
|
struct name : std::false_type {}; \
|
|
template<typename J> \
|
|
struct name<J, void_t<decltype(expr)>> : std::true_type {}
|
|
|
|
using ptr_t = const std::uint8_t*;
|
|
|
|
JSON_TEST_DETECT(from_cbor_ptr_len, J::from_cbor(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
|
JSON_TEST_DETECT(from_msgpack_ptr_len, J::from_msgpack(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
|
JSON_TEST_DETECT(from_ubjson_ptr_len, J::from_ubjson(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
|
JSON_TEST_DETECT(from_bjdata_ptr_len, J::from_bjdata(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
|
JSON_TEST_DETECT(from_bon8_ptr_len, J::from_bon8(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
|
JSON_TEST_DETECT(from_bson_ptr_len, J::from_bson(std::declval<ptr_t>(), std::declval<std::size_t>()));
|
|
|
|
JSON_TEST_DETECT(from_cbor_ptr_ptr, J::from_cbor(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
|
JSON_TEST_DETECT(from_msgpack_ptr_ptr, J::from_msgpack(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
|
JSON_TEST_DETECT(from_ubjson_ptr_ptr, J::from_ubjson(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
|
JSON_TEST_DETECT(from_bjdata_ptr_ptr, J::from_bjdata(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
|
JSON_TEST_DETECT(from_bon8_ptr_ptr, J::from_bon8(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
|
JSON_TEST_DETECT(from_bson_ptr_ptr, J::from_bson(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
|
|
|
JSON_TEST_DETECT(from_cbor_init_list, J::from_cbor({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
|
JSON_TEST_DETECT(from_msgpack_init_list, J::from_msgpack({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
|
JSON_TEST_DETECT(from_ubjson_init_list, J::from_ubjson({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
|
JSON_TEST_DETECT(from_bson_init_list, J::from_bson({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
|
|
|
JSON_TEST_DETECT(parse_init_list, J::parse({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
|
JSON_TEST_DETECT(accept_init_list, J::accept({std::declval<ptr_t>(), std::declval<std::size_t>()}));
|
|
JSON_TEST_DETECT(sax_parse_init_list, J::sax_parse({std::declval<ptr_t>(), std::declval<std::size_t>()}, std::declval<nlohmann::json_sax<J>*>()));
|
|
JSON_TEST_DETECT(parse_ptr_ptr, J::parse(std::declval<ptr_t>(), std::declval<ptr_t>()));
|
|
|
|
JSON_TEST_DETECT(iterator_wrapper, J::iterator_wrapper(std::declval<J&>()));
|
|
JSON_TEST_DETECT(iterator_wrapper_const, J::iterator_wrapper(std::declval<const J&>()));
|
|
JSON_TEST_DETECT(items, std::declval<J&>().items());
|
|
|
|
JSON_TEST_DETECT(json_ltlt_istream, std::declval<J&>() << std::declval<std::istream&>());
|
|
JSON_TEST_DETECT(istream_gtgt_json, std::declval<std::istream&>() >> std::declval<J&>());
|
|
JSON_TEST_DETECT(json_gtgt_ostream, std::declval<const J&>() >> std::declval<std::ostream&>());
|
|
JSON_TEST_DETECT(ostream_ltlt_json, std::declval<std::ostream&>() << std::declval<const J&>());
|
|
|
|
JSON_TEST_DETECT(ptr_eq_ptr, std::declval<const typename J::json_pointer&>() == std::declval<const typename J::json_pointer&>());
|
|
JSON_TEST_DETECT(ptr_ne_ptr, std::declval<const typename J::json_pointer&>() != std::declval<const typename J::json_pointer&>());
|
|
JSON_TEST_DETECT(ptr_eq_string, std::declval<const typename J::json_pointer&>() == std::declval<const std::string&>());
|
|
JSON_TEST_DETECT(string_eq_ptr, std::declval<const std::string&>() == std::declval<const typename J::json_pointer&>());
|
|
JSON_TEST_DETECT(ptr_eq_c_string, std::declval<const typename J::json_pointer&>() == std::declval<const char*>());
|
|
JSON_TEST_DETECT(c_string_eq_ptr, std::declval<const char*>() == std::declval<const typename J::json_pointer&>());
|
|
JSON_TEST_DETECT(ptr_ne_string, std::declval<const typename J::json_pointer&>() != std::declval<const std::string&>());
|
|
JSON_TEST_DETECT(string_ne_ptr, std::declval<const std::string&>() != std::declval<const typename J::json_pointer&>());
|
|
JSON_TEST_DETECT(ptr_to_string, std::declval<const typename J::json_pointer&>().to_string());
|
|
|
|
// json_pointer with a basic_json type as template argument
|
|
template<typename J>
|
|
using legacy_ptr_t = const nlohmann::json_pointer<J>& ;
|
|
JSON_TEST_DETECT(legacy_ptr_value, std::declval<const J&>().value(std::declval<legacy_ptr_t<J>>(), 0));
|
|
JSON_TEST_DETECT(legacy_ptr_value_rvalue, std::declval<const J&>().value(std::declval<legacy_ptr_t<J>>(), std::string()));
|
|
JSON_TEST_DETECT(legacy_ptr_contains, std::declval<const J&>().contains(std::declval<legacy_ptr_t<J>>()));
|
|
JSON_TEST_DETECT(legacy_ptr_subscript, std::declval<J&>()[std::declval<legacy_ptr_t<J>>()]);
|
|
JSON_TEST_DETECT(legacy_ptr_subscript_const, std::declval<const J&>()[std::declval<legacy_ptr_t<J>>()]);
|
|
JSON_TEST_DETECT(legacy_ptr_at, std::declval<J&>().at(std::declval<legacy_ptr_t<J>>()));
|
|
JSON_TEST_DETECT(legacy_ptr_at_const, std::declval<const J&>().at(std::declval<legacy_ptr_t<J>>()));
|
|
JSON_TEST_DETECT(ptr_value, std::declval<const J&>().value(std::declval<const typename J::json_pointer&>(), 0));
|
|
JSON_TEST_DETECT(ptr_at, std::declval<J&>().at(std::declval<const typename J::json_pointer&>()));
|
|
|
|
#undef JSON_TEST_DETECT
|
|
} // namespace
|
|
|
|
TEST_CASE("JSON_DELETE_DEPRECATED_FUNCTIONS")
|
|
{
|
|
// MSVC 2015 does not treat selecting a deleted function in decltype as a
|
|
// substitution failure, so the traits cannot tell deleted functions apart
|
|
// there; calling them still fails to compile
|
|
#if !(defined(_MSC_VER) && _MSC_VER < 1910)
|
|
SECTION("from_* with a pointer and a length")
|
|
{
|
|
// the overloads are deleted rather than removed, so the length cannot
|
|
// silently bind to the strict parameter of from_*(InputType&&, bool)
|
|
CHECK_FALSE(from_cbor_ptr_len<json>::value);
|
|
CHECK_FALSE(from_msgpack_ptr_len<json>::value);
|
|
CHECK_FALSE(from_ubjson_ptr_len<json>::value);
|
|
CHECK_FALSE(from_bjdata_ptr_len<json>::value);
|
|
CHECK_FALSE(from_bon8_ptr_len<json>::value);
|
|
CHECK_FALSE(from_bson_ptr_len<json>::value);
|
|
|
|
// the replacement
|
|
CHECK(from_cbor_ptr_ptr<json>::value);
|
|
CHECK(from_msgpack_ptr_ptr<json>::value);
|
|
CHECK(from_ubjson_ptr_ptr<json>::value);
|
|
CHECK(from_bjdata_ptr_ptr<json>::value);
|
|
CHECK(from_bon8_ptr_ptr<json>::value);
|
|
CHECK(from_bson_ptr_ptr<json>::value);
|
|
}
|
|
|
|
SECTION("initializer lists of a pointer and a length")
|
|
{
|
|
CHECK_FALSE(from_cbor_init_list<json>::value);
|
|
CHECK_FALSE(from_msgpack_init_list<json>::value);
|
|
CHECK_FALSE(from_ubjson_init_list<json>::value);
|
|
CHECK_FALSE(from_bson_init_list<json>::value);
|
|
CHECK_FALSE(parse_init_list<json>::value);
|
|
CHECK_FALSE(accept_init_list<json>::value);
|
|
CHECK_FALSE(sax_parse_init_list<json>::value);
|
|
|
|
// the replacement
|
|
CHECK(parse_ptr_ptr<json>::value);
|
|
}
|
|
|
|
SECTION("iterator_wrapper")
|
|
{
|
|
CHECK_FALSE(iterator_wrapper<json>::value);
|
|
CHECK_FALSE(iterator_wrapper_const<json>::value);
|
|
|
|
// the replacement
|
|
CHECK(items<json>::value);
|
|
}
|
|
|
|
SECTION("stream operators with reversed operands")
|
|
{
|
|
CHECK_FALSE(json_ltlt_istream<json>::value);
|
|
CHECK_FALSE(json_gtgt_ostream<json>::value);
|
|
|
|
// the replacement
|
|
CHECK(istream_gtgt_json<json>::value);
|
|
CHECK(ostream_ltlt_json<json>::value);
|
|
}
|
|
|
|
SECTION("json_pointer")
|
|
{
|
|
// conversion to a string
|
|
CHECK_FALSE(std::is_constructible<std::string, json::json_pointer>::value);
|
|
CHECK_FALSE(std::is_convertible<json::json_pointer, std::string>::value);
|
|
|
|
// comparison with a string
|
|
CHECK_FALSE(ptr_eq_string<json>::value);
|
|
CHECK_FALSE(string_eq_ptr<json>::value);
|
|
CHECK_FALSE(ptr_eq_c_string<json>::value);
|
|
CHECK_FALSE(c_string_eq_ptr<json>::value);
|
|
CHECK_FALSE(ptr_ne_string<json>::value);
|
|
CHECK_FALSE(string_ne_ptr<json>::value);
|
|
|
|
// the replacement
|
|
CHECK(ptr_to_string<json>::value);
|
|
CHECK(ptr_eq_ptr<json>::value);
|
|
CHECK(ptr_ne_ptr<json>::value);
|
|
}
|
|
|
|
SECTION("json_pointer with a basic_json type as template argument")
|
|
{
|
|
CHECK_FALSE(legacy_ptr_value<json>::value);
|
|
CHECK_FALSE(legacy_ptr_value_rvalue<json>::value);
|
|
CHECK_FALSE(legacy_ptr_contains<json>::value);
|
|
CHECK_FALSE(legacy_ptr_subscript<json>::value);
|
|
CHECK_FALSE(legacy_ptr_subscript_const<json>::value);
|
|
CHECK_FALSE(legacy_ptr_at<json>::value);
|
|
CHECK_FALSE(legacy_ptr_at_const<json>::value);
|
|
|
|
// the replacement
|
|
CHECK(ptr_value<json>::value);
|
|
CHECK(ptr_at<json>::value);
|
|
}
|
|
|
|
#endif
|
|
|
|
SECTION("the non-deprecated functions still work")
|
|
{
|
|
const json j = {{"a", {1, 2}}};
|
|
const auto cbor = json::to_cbor(j);
|
|
CHECK(json::from_cbor(cbor.data(), cbor.data() + cbor.size()) == j);
|
|
|
|
const json::json_pointer ptr("/a/1");
|
|
CHECK(ptr == json::json_pointer("/a/1"));
|
|
CHECK(ptr.to_string() == "/a/1");
|
|
CHECK(j[ptr] == 2);
|
|
CHECK(j.at(ptr) == 2);
|
|
CHECK(j.value(ptr, 0) == 2);
|
|
CHECK(j.contains(ptr));
|
|
|
|
std::ostringstream os;
|
|
os << j;
|
|
CHECK(os.str() == R"({"a":[1,2]})");
|
|
std::istringstream is(os.str());
|
|
json parsed;
|
|
is >> parsed;
|
|
CHECK(parsed == j);
|
|
}
|
|
}
|