mirror of
https://github.com/nlohmann/json.git
synced 2026-07-10 04:25:11 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d8e7a7210 | |||
| 75e8fbac32 | |||
| 631e667fe5 | |||
| d0a43141ea | |||
| ecff144b3a | |||
| 855f511db4 |
@@ -5,8 +5,11 @@
|
|||||||
# -Wno-extra-semi-stmt The library uses assert which triggers this warning.
|
# -Wno-extra-semi-stmt The library uses assert which triggers this warning.
|
||||||
# -Wno-padded We do not care about padding warnings.
|
# -Wno-padded We do not care about padding warnings.
|
||||||
# -Wno-covered-switch-default All switches list all cases and a default case.
|
# -Wno-covered-switch-default All switches list all cases and a default case.
|
||||||
# -Wno-unsafe-buffer-usage Otherwise Doctest would not compile.
|
# -Wno-unsafe-buffer-usage Pervasive: the library's own low-level numeric/buffer code
|
||||||
# -Wno-missing-noreturn We found no way to silence this warning otherwise, see PR #4871
|
# (to_chars, serializer, lexer, binary reader/writer, input
|
||||||
|
# adapters, json_pointer) plus vendored Doctest itself (~208
|
||||||
|
# distinct sites measured 2026-07-08 on clang trunk) all use
|
||||||
|
# raw pointer arithmetic / libc string calls by necessity.
|
||||||
|
|
||||||
set(CLANG_CXXFLAGS
|
set(CLANG_CXXFLAGS
|
||||||
-Werror
|
-Werror
|
||||||
@@ -18,5 +21,4 @@ set(CLANG_CXXFLAGS
|
|||||||
-Wno-padded
|
-Wno-padded
|
||||||
-Wno-covered-switch-default
|
-Wno-covered-switch-default
|
||||||
-Wno-unsafe-buffer-usage
|
-Wno-unsafe-buffer-usage
|
||||||
-Wno-missing-noreturn
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"archive": "JSON_for_Modern_C++.tgz",
|
"archive": "JSON_for_Modern_C++.tgz",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Niels Lohmann",
|
"name": "Niels Lohmann",
|
||||||
"link": "https://twitter.com/nlohmann"
|
"link": "https://nlohmann.me"
|
||||||
},
|
},
|
||||||
"aliases": ["nlohmann/json"]
|
"aliases": ["nlohmann/json"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ behavior:
|
|||||||
object will agree on the name-value mappings.
|
object will agree on the name-value mappings.
|
||||||
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be
|
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be
|
||||||
chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or
|
chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or
|
||||||
`#!json {"key": 2}`.
|
`#!json {"key": 2}`. To reject duplicate keys instead of silently resolving them one way or another, see
|
||||||
|
[this parsing recipe](../../features/parsing/parser_callbacks.md#recipe-rejecting-duplicate-object-keys).
|
||||||
- Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see
|
- Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see
|
||||||
[`dump`](dump.md)) in this order. For instance, `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be stored
|
[`dump`](dump.md)) in this order. For instance, `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be stored
|
||||||
and serialized as `#!json {"a": 2, "b": 1}`.
|
and serialized as `#!json {"a": 2, "b": 1}`.
|
||||||
|
|||||||
@@ -19,10 +19,8 @@ class basic_json {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Compares two JSON values for inequality according to the following rules:
|
1. Compares two JSON values for inequality. Returns `#!cpp !(lhs == rhs)` (until C++20) or `#!cpp !(*this == rhs)` (since C++20).
|
||||||
- The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either operand is `NaN` and
|
- This means the comparison is simply the logical negation of `operator==`, including for special values like `NaN` and `discarded`.
|
||||||
the other operand is either `NaN` or any other number.
|
|
||||||
- Otherwise, returns the result of `#!cpp !(lhs == rhs)` (until C++20) or `#!cpp !(*this == rhs)` (since C++20).
|
|
||||||
|
|
||||||
2. Compares a JSON value and a scalar or a scalar and a JSON value for inequality by converting the scalar to a JSON
|
2. Compares a JSON value and a scalar or a scalar and a JSON value for inequality by converting the scalar to a JSON
|
||||||
value and comparing both JSON values according to 1.
|
value and comparing both JSON values according to 1.
|
||||||
@@ -54,13 +52,12 @@ Linear.
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
!!! note "Comparing `NaN`"
|
!!! note "Comparing `NaN` and `discarded`"
|
||||||
|
|
||||||
`NaN` values are unordered within the domain of numbers.
|
Since `operator!=` is defined as `!(a == b)`, the behavior for special values follows that of `operator==`:
|
||||||
The following comparisons all yield `#!cpp false`:
|
|
||||||
1. Comparing a `NaN` with itself.
|
- For `NaN` values: `NaN == NaN` yields `#!cpp false`, so `NaN != NaN` yields `#!cpp true`.
|
||||||
2. Comparing a `NaN` with another `NaN`.
|
- For `discarded` values: `discarded == x` yields `#!cpp false` for any `x`, so `discarded != x` yields `#!cpp true`.
|
||||||
3. Comparing a `NaN` and any other number.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@@ -94,5 +91,7 @@ Linear.
|
|||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
|
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. Changed in version 3.13.0 to remove
|
||||||
2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
|
special-casing for `NaN` and `discarded` values; `operator!=` now consistently means `!(a == b)`.
|
||||||
|
2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. Changed in version 3.13.0 to remove
|
||||||
|
special-casing for `NaN` and `discarded` values; `operator!=` now consistently means `!(a == b)`.
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ When the macro is not defined, the library will define it to its default value.
|
|||||||
- **libstdc++ < 11** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
|
- **libstdc++ < 11** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
|
||||||
- **Clang < 16 with libstdc++** — disabled (incomplete ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
|
- **Clang < 16 with libstdc++** — disabled (incomplete ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
|
||||||
- **libc++ < 160000** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
|
- **libc++ < 160000** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
|
||||||
|
- **nvcc (CUDA) 12.0.x and 12.1.x** — disabled (the `enable_borrowed_range` variable-template syntax triggers a parse error
|
||||||
|
under these two toolkit versions; fixed in CUDA 12.2; [issue #3907](https://github.com/nlohmann/json/issues/3907))
|
||||||
|
|
||||||
If `JSON_HAS_RANGES` is `0` despite `__cpp_lib_ranges` being defined, one of the exclusions above likely applies to your toolchain.
|
If `JSON_HAS_RANGES` is `0` despite `__cpp_lib_ranges` being defined, one of the exclusions above likely applies to your toolchain.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
json parse_strict(const std::string& input)
|
||||||
|
{
|
||||||
|
// one key set per nesting depth, reused across sibling objects
|
||||||
|
std::vector<std::unordered_set<std::string>> keys;
|
||||||
|
|
||||||
|
auto reject_duplicate_keys = [&](int depth, json::parse_event_t event, json & parsed)
|
||||||
|
{
|
||||||
|
if (event == json::parse_event_t::object_start)
|
||||||
|
{
|
||||||
|
// keys of this object are reported at depth+1 (see the event table above)
|
||||||
|
const auto child_depth = static_cast<std::size_t>(depth) + 1;
|
||||||
|
if (keys.size() <= child_depth)
|
||||||
|
{
|
||||||
|
keys.resize(child_depth + 1);
|
||||||
|
}
|
||||||
|
keys[child_depth].clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event == json::parse_event_t::key)
|
||||||
|
{
|
||||||
|
auto& seen = keys[static_cast<std::size_t>(depth)];
|
||||||
|
const auto& key = parsed.get_ref<const std::string&>();
|
||||||
|
if (!seen.insert(key).second)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("duplicate JSON object key: " + key);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return json::parse(input, reject_duplicate_keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// parsing succeeds when all keys are unique
|
||||||
|
json j = parse_strict(R"({"one": 1, "two": 2})");
|
||||||
|
std::cout << j << '\n';
|
||||||
|
|
||||||
|
// parsing throws when a key is repeated
|
||||||
|
try
|
||||||
|
{
|
||||||
|
parse_strict(R"({"one": 1, "one": 2})");
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
{"one":1,"two":2}
|
||||||
|
duplicate JSON object key: one
|
||||||
@@ -81,3 +81,34 @@ was called:
|
|||||||
```json
|
```json
|
||||||
--8<-- "examples/parse__string__parser_callback_t.output"
|
--8<-- "examples/parse__string__parser_callback_t.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Recipe: rejecting duplicate object keys
|
||||||
|
|
||||||
|
The JSON specification leaves the handling of objects with repeated keys up to the implementation. As described in
|
||||||
|
[`object_t`](../../api/basic_json/object_t.md#behavior), it is unspecified which value for a repeated key ends up in
|
||||||
|
the resulting `#!c json` value -- once parsing has produced that value, the duplicate is already gone, because object
|
||||||
|
storage maps each key to a single value. If duplicate keys should instead be treated as an error, a parser callback
|
||||||
|
can detect them while the object is still being read, before that ambiguity ever applies.
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/reject_duplicate_keys.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/reject_duplicate_keys.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
This approach has two limitations:
|
||||||
|
|
||||||
|
- The depth-indexed bookkeeping must account for the fact that `object_start` reports the depth of the *parent* of
|
||||||
|
the object, while the `key` events inside that object are reported one depth deeper (see the event table above);
|
||||||
|
it is easy to get this off by one for nested objects.
|
||||||
|
- The thrown exception cannot carry a `parse_error`-style byte offset, because position tracking only exists inside
|
||||||
|
the parser and lexer, not at the callback layer.
|
||||||
|
|
||||||
|
For strict validation with precise error positions, implementing a [SAX interface](sax_interface.md) instead gives
|
||||||
|
access to the parser's position information directly.
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ std::map<
|
|||||||
The choice of `object_t` influences the behavior of the JSON class. With the default type, objects have the following behavior:
|
The choice of `object_t` influences the behavior of the JSON class. With the default type, objects have the following behavior:
|
||||||
|
|
||||||
- When all names are unique, objects will be interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings.
|
- When all names are unique, objects will be interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings.
|
||||||
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or `#!json {"key": 2}`.
|
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or `#!json {"key": 2}`. To reject duplicate keys instead of silently resolving them one way or another, see [this parsing recipe](../parsing/parser_callbacks.md#recipe-rejecting-duplicate-object-keys).
|
||||||
- Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see `dump`) in this order. For instance, both `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be stored and serialized as `#!json {"a": 2, "b": 1}`.
|
- Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see `dump`) in this order. For instance, both `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be stored and serialized as `#!json {"a": 2, "b": 1}`.
|
||||||
- When comparing objects, the order of the name/value pairs is irrelevant. This makes objects interoperable in the sense that they will not be affected by these differences. For instance, `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be treated as equal.
|
- When comparing objects, the order of the name/value pairs is irrelevant. This makes objects interoperable in the sense that they will not be affected by these differences. For instance, `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be treated as equal.
|
||||||
|
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ class wide_string_input_adapter
|
|||||||
|
|
||||||
// parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here
|
// parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here
|
||||||
template<class T>
|
template<class T>
|
||||||
std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
||||||
{
|
{
|
||||||
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
|
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3776,17 +3776,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return *this == basic_json(rhs);
|
return *this == basic_json(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief comparison: not equal
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
|
||||||
bool operator!=(const_reference rhs) const noexcept
|
|
||||||
{
|
|
||||||
if (compares_unordered(rhs, true))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !operator==(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief comparison: 3-way
|
/// @brief comparison: 3-way
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
|
||||||
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
|
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
|
||||||
@@ -3892,10 +3881,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
||||||
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
|
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
|
||||||
{
|
{
|
||||||
if (compares_unordered(lhs, rhs, true))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7267,7 +7267,7 @@ class wide_string_input_adapter
|
|||||||
|
|
||||||
// parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here
|
// parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here
|
||||||
template<class T>
|
template<class T>
|
||||||
std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
||||||
{
|
{
|
||||||
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
|
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
|
||||||
}
|
}
|
||||||
@@ -24627,17 +24627,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return *this == basic_json(rhs);
|
return *this == basic_json(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief comparison: not equal
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
|
||||||
bool operator!=(const_reference rhs) const noexcept
|
|
||||||
{
|
|
||||||
if (compares_unordered(rhs, true))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !operator==(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief comparison: 3-way
|
/// @brief comparison: 3-way
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
|
||||||
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
|
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
|
||||||
@@ -24743,10 +24732,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
||||||
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
|
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
|
||||||
{
|
{
|
||||||
if (compares_unordered(lhs, rhs, true))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,11 @@ target_compile_options(test_main PUBLIC
|
|||||||
# Disable warning C4566: character represented by universal-character-name '\uFF01'
|
# Disable warning C4566: character represented by universal-character-name '\uFF01'
|
||||||
# cannot be represented in the current code page (1252)
|
# cannot be represented in the current code page (1252)
|
||||||
# Disable warning C4996: 'nlohmann::basic_json<...>::operator <<': was declared deprecated
|
# Disable warning C4996: 'nlohmann::basic_json<...>::operator <<': was declared deprecated
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/W4;/wd4566;/wd4996;$<$<CONFIG:Release>:/wd4702>>
|
# Disable warning C4702: unreachable code; wide_string_input_adapter::get_elements()
|
||||||
|
# is annotated JSON_HEDLEY_NO_RETURN (it always throws), which
|
||||||
|
# makes MSVC flag the code following its call in binary_reader.hpp
|
||||||
|
# as unreachable for that instantiation, in both Debug and Release
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:/W4;/wd4566;/wd4996;/wd4702>
|
||||||
# https://github.com/nlohmann/json/issues/1114
|
# https://github.com/nlohmann/json/issues/1114
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/bigobj> $<$<BOOL:${MINGW}>:-Wa,-mbig-obj>
|
$<$<CXX_COMPILER_ID:MSVC>:/bigobj> $<$<BOOL:${MINGW}>:-Wa,-mbig-obj>
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ struct bad_allocator : std::allocator<T>
|
|||||||
template<class U> bad_allocator(const bad_allocator<U>& /*unused*/) { }
|
template<class U> bad_allocator(const bad_allocator<U>& /*unused*/) { }
|
||||||
|
|
||||||
template<class... Args>
|
template<class... Args>
|
||||||
void construct(T* /*unused*/, Args&& ... /*unused*/) // NOLINT(cppcoreguidelines-missing-std-forward)
|
[[noreturn]] void construct(T* /*unused*/, Args&& ... /*unused*/) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
{
|
{
|
||||||
throw std::bad_alloc();
|
throw std::bad_alloc();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ TEST_CASE("lexicographical comparison operators")
|
|||||||
SECTION("comparison: not equal")
|
SECTION("comparison: not equal")
|
||||||
{
|
{
|
||||||
// check that two values compare unequal as expected
|
// check that two values compare unequal as expected
|
||||||
|
// operator!= now means exactly !(a==b) without special cases for NaN/discarded
|
||||||
for (size_t i = 0; i < j_values.size(); ++i)
|
for (size_t i = 0; i < j_values.size(); ++i)
|
||||||
{
|
{
|
||||||
for (size_t j = 0; j < j_values.size(); ++j)
|
for (size_t j = 0; j < j_values.size(); ++j)
|
||||||
@@ -376,25 +377,12 @@ TEST_CASE("lexicographical comparison operators")
|
|||||||
CAPTURE(i)
|
CAPTURE(i)
|
||||||
CAPTURE(j)
|
CAPTURE(j)
|
||||||
|
|
||||||
if (json::compares_unordered(j_values[i], j_values[j], true))
|
CHECK((j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]));
|
||||||
{
|
|
||||||
// if two values compare unordered,
|
|
||||||
// check that the boolean comparison result is always false
|
|
||||||
CHECK_FALSE(j_values[i] != j_values[j]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// otherwise, check that they compare according to their definition
|
|
||||||
// as the inverse of equal
|
|
||||||
CHECK((j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare with null pointer
|
// compare with null pointer
|
||||||
const json j_null;
|
const json j_null;
|
||||||
CHECK((j_null != nullptr) == false);
|
|
||||||
CHECK((nullptr != j_null) == false);
|
|
||||||
CHECK((j_null != nullptr) == !(j_null == nullptr));
|
CHECK((j_null != nullptr) == !(j_null == nullptr));
|
||||||
CHECK((nullptr != j_null) == !(nullptr == j_null));
|
CHECK((nullptr != j_null) == !(nullptr == j_null));
|
||||||
}
|
}
|
||||||
@@ -594,3 +582,34 @@ TEST_CASE("lexicographical comparison operators")
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||||
|
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
|
||||||
|
|
||||||
|
TEST_CASE("regression #3868 - heterogeneous comparisons compile under C++20 (P2468R2)")
|
||||||
|
{
|
||||||
|
// Issue #3868: operator!= was preventing compiler from synthesizing reversed
|
||||||
|
// operator== candidates under C++20's P2468R2 rewritten candidate rules.
|
||||||
|
// Verify that heterogeneous comparisons now work.
|
||||||
|
|
||||||
|
SECTION("string vs json")
|
||||||
|
{
|
||||||
|
std::string s = "string";
|
||||||
|
json j = "string";
|
||||||
|
CHECK(s == j);
|
||||||
|
CHECK(j == s);
|
||||||
|
CHECK_FALSE(s != j);
|
||||||
|
CHECK_FALSE(j != s);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("other heterogeneous types")
|
||||||
|
{
|
||||||
|
int i = 42;
|
||||||
|
json j = 42;
|
||||||
|
CHECK(i == j);
|
||||||
|
CHECK(j == i);
|
||||||
|
CHECK_FALSE(i != j);
|
||||||
|
CHECK_FALSE(j != i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ TEST_CASE("constructors")
|
|||||||
const auto t = j.get<std::tuple<int, float, std::string>>();
|
const auto t = j.get<std::tuple<int, float, std::string>>();
|
||||||
CHECK(std::get<0>(t) == j[0]);
|
CHECK(std::get<0>(t) == j[0]);
|
||||||
CHECK(std::get<1>(t) == j[1]);
|
CHECK(std::get<1>(t) == j[1]);
|
||||||
// CHECK(std::get<2>(t) == j[2]); // commented out due to CI issue, see https://github.com/nlohmann/json/pull/3985 and https://github.com/nlohmann/json/issues/4025
|
CHECK(std::get<2>(t) == j[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("std::tuple tie")
|
SECTION("std::tuple tie")
|
||||||
|
|||||||
@@ -942,7 +942,7 @@ TEST_CASE("iterators 2")
|
|||||||
json j_expected{5, 4, 3, 2, 1};
|
json j_expected{5, 4, 3, 2, 1};
|
||||||
|
|
||||||
auto reversed = j | std::views::reverse;
|
auto reversed = j | std::views::reverse;
|
||||||
CHECK(std::ranges::equal(reversed, j_expected));
|
CHECK(reversed == j_expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("transform")
|
SECTION("transform")
|
||||||
|
|||||||
Reference in New Issue
Block a user