Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

# Conflicts:
#	tests/src/unit-alt-string.cpp
This commit is contained in:
Niels Lohmann
2026-10-04 11:50:33 +02:00
39 changed files with 1491 additions and 185 deletions
+3 -1
View File
@@ -238,5 +238,7 @@ Strong exception safety: if an exception occurs, the original value stays intact
1. Added in version 1.0.0.
2. Added in version 1.0.0.
3. Added in version 3.11.0.
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`operator[]`](operator[].md), [`value`](value.md), [`find`](find.md), and other lookup
functions.
4. Added in version 2.0.0.
+11 -1
View File
@@ -293,6 +293,15 @@ basic_json(basic_json&& other) noexcept;
When used without parentheses around an empty initializer list, `basic_json()` is called instead of this
function, yielding the JSON `#!json null` value.
- Overload 4:
!!! info "Implicit conversion"
The conversion is implicit unless [`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md)
is defined to `0` and `BasicJsonType::string_t` differs from `string_t`. In that case, the constructor is
`explicit`, so a JSON value with a different string type is no longer silently converted, for example when it is
passed to a function taking `#!cpp const json&`. Write `#!cpp json(other)` or `#!cpp other.get<json>()` instead.
- Overload 7:
!!! info "Preconditions"
@@ -466,7 +475,8 @@ basic_json(basic_json&& other) noexcept;
1. Since version 1.0.0.
2. Since version 1.0.0.
3. Since version 2.1.0.
4. Since version 3.2.0.
4. Since version 3.2.0. Explicit for different string types if `JSON_USE_IMPLICIT_CONVERSIONS` is `0` since
version 3.13.0.
5. Since version 1.0.0.
6. Since version 1.0.0.
7. Since version 1.0.0. Fixed in version 3.13.0 to also check the iterator range for binary values; before, a range
+3 -1
View File
@@ -131,7 +131,9 @@ Logarithmic in the size of the JSON object.
## Version history
1. Added in version 3.11.0.
2. Added in version 3.6.0. Extended template `KeyType` to support comparable types in version 3.11.0.
2. Added in version 3.6.0. Extended template `KeyType` to support comparable types in version 3.11.0. Fixed in
version 3.13.0 to consistently accept `std::string_view`-convertible keys, as already supported by
[`operator[]`](operator[].md), [`at`](at.md), [`value`](value.md), and other lookup functions.
3. Added in version 3.7.0.
4. Deleted overloads for integral key types added in version 3.13.0 to reject such calls at compile time instead of
causing undefined behavior at runtime.
+3 -1
View File
@@ -84,6 +84,8 @@ Logarithmic in the size of the JSON object.
## Version history
1. Added in version 3.11.0.
2. Added in version 1.0.0. Changed parameter `key` type to `KeyType&&` in version 3.11.0.
2. Added in version 1.0.0. Changed parameter `key` type to `KeyType&&` in version 3.11.0. Fixed in version 3.13.0 to
consistently accept `std::string_view`-convertible keys, as already supported by [`operator[]`](operator[].md),
[`at`](at.md), [`value`](value.md), and other lookup functions.
3. Deleted overload for integral key types added in version 3.13.0 to reject such calls at compile time instead of
causing undefined behavior at runtime.
@@ -70,3 +70,5 @@ Logarithmic in the size of the container, O(log(`size()`)).
## Version history
- Since version 2.0.8.
- Fixed in version 3.13.0: for [`ordered_json`](../ordered_json.md), the value could previously only be passed as an
rvalue; it can now also be passed as an lvalue or a `#!cpp const` lvalue, matching the behavior of `json`.
+3 -1
View File
@@ -213,5 +213,7 @@ Strong exception safety: if an exception occurs, the original value stays intact
1. Added in version 1.0.0. Added support for binary types in version 3.8.0.
2. Added in version 1.0.0. Added support for binary types in version 3.8.0.
3. Added in version 1.0.0.
4. Added in version 3.11.0.
4. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`operator[]`](operator[].md), [`at`](at.md), [`value`](value.md), and other lookup
functions.
5. Added in version 1.0.0.
+3 -1
View File
@@ -88,6 +88,8 @@ Logarithmic in the size of the JSON object.
## Version history
1. Added in version 3.11.0.
2. Added in version 1.0.0. Changed to support comparable types in version 3.11.0.
2. Added in version 1.0.0. Changed to support comparable types in version 3.11.0. Fixed in version 3.13.0 to
consistently accept `std::string_view`-convertible keys, as already supported by [`operator[]`](operator[].md),
[`at`](at.md), [`value`](value.md), and other lookup functions.
3. Deleted overloads for integral key types added in version 3.13.0 to reject such calls at compile time instead of
causing undefined behavior at runtime.
+11 -3
View File
@@ -89,6 +89,9 @@ Strong exception safety: if an exception occurs, the original value stays intact
- Throws [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) if an array index in the passed
JSON pointer `ptr` exceeds the range of `size_type` (e.g., on 32-bit platforms).
For the **const** version, an object key or array index in `ptr` that does not exist is not reported by an
exception, but is undefined behavior (see the notes below). Use [`at`](at.md) for checked access.
## Complexity
1. Constant if `idx` is in the range of the array. Otherwise, linear in `idx - size()`.
@@ -103,9 +106,12 @@ Strong exception safety: if an exception occurs, the original value stays intact
The following cases apply to the **const** overloads; the non-const overloads instead insert the missing element
(see the notes below).
1. If the element at index `idx` does not exist, the behavior is undefined.
1. If the element at index `idx` does not exist, the behavior is undefined and is **guarded by a
[runtime assertion](../../features/assertions.md)**!
2. If the element with key `key` does not exist, the behavior is undefined and is **guarded by a
[runtime assertion](../../features/assertions.md)**!
3. If the JSON pointer `ptr` refers to an object key or an array index that does not exist, the behavior is
undefined and is **guarded by a [runtime assertion](../../features/assertions.md)**!
1. The non-const version may add values: If `idx` is beyond the range of the array (i.e., `idx >= size()`), then the
array is silently filled up with `#!json null` values to make `idx` a valid reference to the last stored element. In
@@ -273,9 +279,11 @@ Strong exception safety: if an exception occurs, the original value stays intact
## Version history
1. Added in version 1.0.0. Fixed in version 3.13.0 to throw `#!cpp std::length_error` instead of emptying the array and
accessing it out of bounds when `idx` equals the maximum value of `size_type`.
accessing it out of bounds when `idx` equals the maximum value of `size_type`. A missing index in the const version
is guarded by a runtime assertion since version 3.13.0.
2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3)
in version 3.11.0.
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions.
4. Added in version 2.0.0.
4. Added in version 2.0.0. A missing array index in the const version is guarded by a runtime assertion since
version 3.13.0.
+10 -4
View File
@@ -5,17 +5,17 @@
bool operator==(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator==(const_reference lhs, const ScalarType rhs) noexcept; // (2)
bool operator==(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2)
template<typename ScalarType>
bool operator==(ScalarType lhs, const const_reference rhs) noexcept; // (2)
bool operator==(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2)
// since C++20
class basic_json {
bool operator==(const_reference rhs) const noexcept; // (1)
template<typename ScalarType>
bool operator==(ScalarType rhs) const noexcept; // (2)
bool operator==(ScalarType rhs) const noexcept(/* see below */); // (2)
};
```
@@ -46,7 +46,12 @@ whether the values `lhs`/`*this` and `rhs` are equal
## Exception safety
No-throw guarantee: this function never throws exceptions.
1. No-throw guarantee: this function never throws exceptions.
2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and
`#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion
throws, for example `std::bad_alloc` when converting a string, or
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by
[`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md).
## Complexity
@@ -171,3 +176,4 @@ Linear.
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`.
@@ -5,10 +5,10 @@
bool operator>=(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator>=(const_reference lhs, const ScalarType rhs) noexcept; // (2)
bool operator>=(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2)
template<typename ScalarType>
bool operator>=(ScalarType lhs, const const_reference rhs) noexcept; // (2)
bool operator>=(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2)
```
1. Compares whether one JSON value `lhs` is greater than or equal to another JSON value `rhs` according to the following
@@ -39,7 +39,12 @@ whether `lhs` is greater than or equal to `rhs`
## Exception safety
No-throw guarantee: this function never throws exceptions.
1. No-throw guarantee: this function never throws exceptions.
2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and
`#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion
throws, for example `std::bad_alloc` when converting a string, or
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by
[`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md).
## Complexity
@@ -94,3 +99,4 @@ Linear.
1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`.
@@ -5,10 +5,10 @@
bool operator>(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator>(const_reference lhs, const ScalarType rhs) noexcept; // (2)
bool operator>(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2)
template<typename ScalarType>
bool operator>(ScalarType lhs, const const_reference rhs) noexcept; // (2)
bool operator>(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2)
```
1. Compares whether one JSON value `lhs` is greater than another JSON value `rhs` according to the
@@ -39,7 +39,12 @@ whether `lhs` is greater than `rhs`
## Exception safety
No-throw guarantee: this function never throws exceptions.
1. No-throw guarantee: this function never throws exceptions.
2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and
`#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion
throws, for example `std::bad_alloc` when converting a string, or
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by
[`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md).
## Complexity
@@ -84,3 +89,4 @@ Linear.
1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`.
@@ -5,10 +5,10 @@
bool operator<=(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator<=(const_reference lhs, const ScalarType rhs) noexcept; // (2)
bool operator<=(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2)
template<typename ScalarType>
bool operator<=(ScalarType lhs, const const_reference rhs) noexcept; // (2)
bool operator<=(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2)
```
1. Compares whether one JSON value `lhs` is less than or equal to another JSON value `rhs`
@@ -40,7 +40,12 @@ whether `lhs` is less than or equal to `rhs`
## Exception safety
No-throw guarantee: this function never throws exceptions.
1. No-throw guarantee: this function never throws exceptions.
2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and
`#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion
throws, for example `std::bad_alloc` when converting a string, or
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by
[`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md).
## Complexity
@@ -95,3 +100,4 @@ Linear.
1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`.
@@ -5,10 +5,10 @@
bool operator<(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator<(const_reference lhs, const ScalarType rhs) noexcept; // (2)
bool operator<(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2)
template<typename ScalarType>
bool operator<(ScalarType lhs, const const_reference rhs) noexcept; // (2)
bool operator<(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2)
```
1. Compares whether one JSON value `lhs` is less than another JSON value `rhs` according to the
@@ -49,7 +49,12 @@ whether `lhs` is less than `rhs`
## Exception safety
No-throw guarantee: this function never throws exceptions.
1. No-throw guarantee: this function never throws exceptions.
2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and
`#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion
throws, for example `std::bad_alloc` when converting a string, or
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by
[`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md).
## Complexity
@@ -94,3 +99,4 @@ Linear.
1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0.
Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`.
@@ -5,10 +5,10 @@
bool operator!=(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator!=(const_reference lhs, const ScalarType rhs) noexcept; // (2)
bool operator!=(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2)
template<typename ScalarType>
bool operator!=(ScalarType lhs, const const_reference rhs) noexcept; // (2)
bool operator!=(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2)
```
1. Compares two JSON values for inequality. Returns `#!cpp !(lhs == rhs)`.
@@ -36,7 +36,12 @@ whether the values `lhs`/`*this` and `rhs` are not equal
## Exception safety
No-throw guarantee: this function never throws exceptions.
1. No-throw guarantee: this function never throws exceptions.
2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and
`#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion
throws, for example `std::bad_alloc` when converting a string, or
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by
[`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md).
## Complexity
@@ -98,3 +103,4 @@ Linear.
member function in version 3.13.0; since C++20, the compiler rewrites `a != b` using `operator==`.
2. Added in version 1.0.0. Changed in version 3.13.0 to remove special-casing for `NaN` and `discarded` values;
`operator!=` now consistently means `!(a == b)`. Since C++20, the compiler rewrites `a != b` using `operator==`.
Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`.
@@ -6,7 +6,7 @@ class basic_json {
std::partial_ordering operator<=>(const_reference rhs) const noexcept; // (1)
template<typename ScalarType>
std::partial_ordering operator<=>(const ScalarType rhs) const noexcept; // (2)
std::partial_ordering operator<=>(const ScalarType rhs) const noexcept(/* see below */); // (2)
};
```
@@ -39,7 +39,12 @@ the `std::partial_ordering` of the 3-way comparison of `*this` and `rhs`
## Exception safety
No-throw guarantee: this function never throws exceptions.
1. No-throw guarantee: this function never throws exceptions.
2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and
`#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion
throws, for example `std::bad_alloc` when converting a string, or
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by
[`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md).
## Complexity
@@ -98,3 +103,4 @@ Linear.
1. Added in version 3.11.0.
2. Added in version 3.11.0.
Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`.
+3 -1
View File
@@ -222,7 +222,9 @@ changes to any JSON value.
1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version
3.11.0. Deleted overload for integral key types added in version 3.13.0 to reject such calls at compile time
instead of causing undefined behavior at runtime.
2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2.
2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2. Fixed in version 3.13.0
to consistently accept `std::string_view`-convertible keys, as already supported by
[`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md), and other lookup functions.
3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving
`ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
`default_value`, as documented).
@@ -5,7 +5,9 @@
```
When defined to `0`, implicit conversions are switched off. By default, implicit conversions are switched on. The
value directly affects [`operator ValueType`](../basic_json/operator_ValueType.md).
value directly affects [`operator ValueType`](../basic_json/operator_ValueType.md) and the
[converting constructor](../basic_json/basic_json.md) from a `basic_json` specialization with a different string
type (overload 4).
## Default definition
@@ -59,6 +61,25 @@ By default, implicit conversions are enabled.
auto s = j.get<std::string>();
```
??? example "Conversion between `basic_json` specializations"
A `basic_json` specialization with a different string type is also no longer converted implicitly when
`JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`:
```cpp
using wjson = nlohmann::basic_json<std::map, std::vector, std::wstring>;
void load(const nlohmann::json& j);
wjson wj = /* ... */;
load(wj); // error: no implicit conversion
load(nlohmann::json(wj)); // OK: explicit conversion
load(wj.get<nlohmann::json>()); // OK: explicit conversion
```
Specializations that share the same string type, such as `json` and `ordered_json`, remain implicitly
convertible.
## See also
- [**operator ValueType**](../basic_json/operator_ValueType.md) - get a value (implicit)
@@ -68,3 +89,4 @@ By default, implicit conversions are enabled.
## Version history
- Added in version 3.9.0.
- Also affects the conversion between `basic_json` specializations with different string types since version 3.13.0.
@@ -81,3 +81,6 @@ When the macro is not defined, the library will define it to its default value.
## Version history
- Added in version 3.11.0.
- Fixed in version 3.13.0 so `<=` and `>=` also emulate the legacy behavior in C++20 when the JSON value is the
right-hand operand of a scalar comparison; before, only the 3-way-comparison-rewritten candidate was found, which
yielded `#!cpp false` instead of `#!cpp true`.
@@ -79,6 +79,7 @@ Some important things:
* When using `get<your_type>()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.)
* In function `from_json`, use function [`at()`](../api/basic_json/at.md) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior.
* You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these.
* If you control the type, consider defining `to_json`/`from_json` as `friend` functions inside the class ("hidden friends"). Argument-dependent lookup then only finds them for your type, which also avoids a [GCC < 11 compilation error](../home/faq.md#incomplete-detector-type-with-gcc-11).
??? example "Example: serialize a `person` to JSON with `to_json`"
+31 -7
View File
@@ -16,14 +16,15 @@ before including the `json.hpp` header.
## Function with runtime assertions
### Unchecked object access to a const value
### Unchecked access to a const value
Function [`operator[]`](../api/basic_json/operator%5B%5D.md) implements unchecked access for objects. Whereas a missing
key is added in the case of non-const objects, accessing a const object with a missing key is undefined behavior (think
of a dereferenced null pointer) and yields a runtime assertion.
Function [`operator[]`](../api/basic_json/operator%5B%5D.md) implements unchecked access for arrays and objects. Whereas
a missing element is added in the case of non-const values, accessing a const value with a missing object key or an
invalid array index is undefined behavior (think of a dereferenced null pointer) and yields a runtime assertion. This
also applies to a [JSON pointer](json_pointer.md) that refers to a missing key or an invalid index.
If you are not sure whether an element in an object exists, use checked access with the
[`at` function](../api/basic_json/at.md) or call the [`contains` function](../api/basic_json/contains.md) before.
If you are not sure whether an element exists, use checked access with the [`at` function](../api/basic_json/at.md)
or call the [`contains` function](../api/basic_json/contains.md) before.
See also the documentation on [element access](element_access/index.md).
@@ -46,7 +47,30 @@ See also the documentation on [element access](element_access/index.md).
Output:
```
Assertion failed: (m_value.object->find(key) != m_value.object->end()), function operator[], file json.hpp, line 2144.
Assertion failed: (it != m_data.m_value.object->end()), function operator[], file json.hpp, line 28795.
```
??? example "Example 2: Invalid array index in a JSON pointer"
The following code will trigger an assertion at runtime:
```cpp
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using namespace nlohmann::literals;
int main()
{
const json j = {{"array", {1, 2, 3}}};
auto v = j["/array/5"_json_pointer];
}
```
Output:
```
Assertion failed: (idx < m_data.m_value.array->size()), function operator[], file json.hpp, line 28758.
```
### Constructing from an uninitialized iterator range
+45
View File
@@ -305,6 +305,51 @@ Only very old NDKs (before r18), which defaulted to GCC and `gnustl`, lacked C++
`std::to_string`. If you run into this, update to a current NDK.
### Incomplete `detector` type with GCC < 11
!!! question
Why does GCC 10 or older fail with `invalid use of incomplete type 'struct nlohmann::detail::detector<..., to_json_function, ...>'` for a type that holds an `optional` member?
This happens with GCC 10 and older in C++11/C++14 mode when all of these hold:
- a class `Holder` has an `optional<Dummy>` member (e.g., `boost::optional`),
- `Dummy` has a constructor taking a `json` value, and
- `to_json` for `Holder` is a free function in the namespace of `Dummy`.
```cpp
class Dummy {
public:
explicit Dummy(const nlohmann::json& j);
};
class Holder {
boost::optional<Dummy> d;
};
void to_json(nlohmann::json& j, const Holder& h); // triggers the error
```
To decide whether `Dummy` is copyable, the compiler checks whether a `Dummy` can be converted to `json`. That check
looks up `to_json` via argument-dependent lookup, finds the unrelated `to_json` for `Holder`, and eventually asks again
whether `Dummy` is copyable. GCC before version 11 turns this cycle into a hard error; GCC 11 and later, Clang, and
C++17 mode compile the code. The same error shows up without this library whenever a constrained converting constructor
is involved, so the library can't avoid it.
To work around this, define `to_json` (and `from_json`) as a *hidden friend* inside the class. That way,
argument-dependent lookup only finds it for `Holder`:
```cpp
class Holder {
boost::optional<Dummy> d;
friend void to_json(nlohmann::json& j, const Holder& h) { /* ... */ }
};
```
The [`NLOHMANN_DEFINE_TYPE_INTRUSIVE`](../api/macros/nlohmann_define_type_intrusive.md) macros define hidden friends as
well. See [#3669](https://github.com/nlohmann/json/issues/3669) for details.
### Missing STL function
!!! question "Questions"
@@ -294,7 +294,9 @@ void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(std::is_not
{
if (opt.has_value())
{
j = *opt;
// explicit construction, as the conversion from a basic_json with a different
// string type is explicit if JSON_USE_IMPLICIT_CONVERSIONS is 0 (#2649)
j = BasicJsonType(*opt);
}
else
{
+6 -1
View File
@@ -955,10 +955,15 @@ class lexer : public lexer_base<BasicJsonType>
case '\n':
case '\r':
case char_traits<char_type>::eof():
return true;
#if !JSON_STRICT_NUL_HANDLING
case '\0':
#endif
// a NUL byte is the end of the input (see scan()),
// so leave it for scan() to see
unget();
return true;
#endif
default:
break;
+26 -10
View File
@@ -536,6 +536,10 @@ class json_pointer
@return const reference to the JSON value pointed to by the JSON
pointer
@pre Every object key and array index the pointer refers to exists.
Like the const operator[] for keys and indices, a missing one is
undefined behavior, guarded by a runtime assertion.
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@@ -550,7 +554,8 @@ class json_pointer
{
case detail::value_t::object:
{
// use unchecked object access
// use unchecked object access; the const operator[]
// asserts that the key exists
ptr = &ptr->operator[](reference_token);
break;
}
@@ -563,7 +568,8 @@ class json_pointer
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
}
// use unchecked array access
// use unchecked array access; the const operator[]
// asserts that the index exists
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
break;
}
@@ -679,19 +685,29 @@ class json_pointer
return nullptr;
}
// may throw parse_error.106/109 for a malformed index; an
// index that is syntactically valid but cannot be
// represented (out_of_range.404/410) is treated like an
// out-of-range index below
typename BasicJsonType::size_type idx{};
JSON_TRY
// tokens that array_index() rejects with parse_error.106/109
// are passed on to it; all other tokens that it would reject
// with out_of_range.404/410 are detected here, so that this
// also works without exceptions
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 && !(reference_token[0] >= '1' && reference_token[0] <= '9')))
{
idx = array_index<BasicJsonType>(reference_token);
static_cast<void>(array_index<BasicJsonType>(reference_token)); // throws parse_error.106/109
}
JSON_INTERNAL_CATCH (detail::out_of_range&)
if (JSON_HEDLEY_UNLIKELY(reference_token.empty() || !std::all_of(reference_token.begin(), reference_token.end(), [](const char c)
{
return c >= '0' && c <= '9';
})))
{
return nullptr;
}
errno = 0; // strtoull() does not reset errno on success
char* p_end = nullptr; // NOLINT(misc-const-correctness)
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE || magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
{
return nullptr;
}
const auto idx = static_cast<typename BasicJsonType::size_type>(magnitude);
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
{
+56 -3
View File
@@ -189,6 +189,37 @@ struct actual_object_comparator
template<typename BasicJsonType>
using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;
template<typename T>
using detect_key_comp = decltype(std::declval<const T&>().key_comp());
// whether ObjectType can be constructed from a pair of Iterator together with
// a copy of its own comparator, the way std::map can: it needs a nested
// key_compare, a const key_comp() convertible to it, and a matching
// (Iterator, Iterator, const key_compare&) constructor.
//
// used to preserve a stateful comparator when a copy is built from a range
// past the iterative deep copy's nesting bound (see copy_object_level); an
// object type that does not satisfy this, such as nlohmann::ordered_map
// (which has key_compare for its std::map-like interface, but no key_comp()),
// keeps default-constructing its comparator, just as it always has
template<typename ObjectType, typename Iterator, typename = void>
struct is_comparator_constructible_object_type_impl : std::false_type {};
template<typename ObjectType, typename Iterator>
struct is_comparator_constructible_object_type_impl <
ObjectType, Iterator, enable_if_t<is_detected<detect_key_compare, ObjectType>::value >>
{
using key_compare = typename ObjectType::key_compare;
static constexpr bool value =
is_detected_convertible<key_compare, detect_key_comp, ObjectType>::value &&
std::is_constructible<ObjectType, Iterator, Iterator, const key_compare&>::value;
};
template<typename ObjectType, typename Iterator>
struct is_comparator_constructible_object_type
: is_comparator_constructible_object_type_impl<ObjectType, Iterator> {};
/////////////////
// char_traits //
/////////////////
@@ -760,6 +791,30 @@ using is_usable_as_key_type = typename std::conditional <
std::true_type,
std::false_type >::type;
#ifdef JSON_HAS_CPP_17
// type trait to check if KeyType can only be used as an object key after
// converting it to std::string_view: it is convertible to std::string_view, the
// object's comparator cannot compare it with object_t::key_type directly, but
// can compare a std::string_view. JSON pointers and JSON iterators are ruled out
// first, so that the conversion checks are never instantiated for them (a JSON
// pointer's deprecated conversion to string_t would be named otherwise).
template < typename BasicJsonType, typename KeyTypeCVRef, typename KeyType = uncvref_t<KeyTypeCVRef>,
bool = is_json_pointer<KeyType>::value || is_json_iterator_of<BasicJsonType, KeyType>::value >
struct is_string_view_convertible_key_type : std::false_type {};
template<typename BasicJsonType, typename KeyTypeCVRef, typename KeyType>
struct is_string_view_convertible_key_type<BasicJsonType, KeyTypeCVRef, KeyType, false>
: std::integral_constant < bool,
std::is_convertible<KeyTypeCVRef, std::string_view>::value
&& !is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
typename BasicJsonType::object_t::key_type, KeyTypeCVRef, true, false>::value
&& is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
typename BasicJsonType::object_t::key_type, std::string_view, true, false>::value > {};
#else
template<typename BasicJsonType, typename KeyTypeCVRef>
struct is_string_view_convertible_key_type : std::false_type {};
#endif
// type trait to check if KeyType can be used as an object key
// true if:
// - KeyType is comparable with BasicJsonType::object_t::key_type
@@ -773,9 +828,7 @@ using is_usable_as_basic_json_key_type = typename std::conditional <
typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
RequireTransparentComparator, ExcludeObjectKeyType>::value
&& !is_json_iterator_of<BasicJsonType, KeyType>::value)
#ifdef JSON_HAS_CPP_17
|| std::is_convertible<KeyType, std::string_view>::value
#endif
|| is_string_view_convertible_key_type<BasicJsonType, KeyTypeCVRef>::value
, std::true_type,
std::false_type >::type;
+224 -52
View File
@@ -813,6 +813,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return it;
}
/// @brief the key to look up an object member with: the key itself, or its
/// std::string_view if the object can only be searched with that
template < typename KeyType, detail::enable_if_t <
!detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value, int > = 0 >
static KeyType && lookup_key(KeyType && key) noexcept
{
return std::forward<KeyType>(key);
}
#ifdef JSON_HAS_CPP_17
template < typename KeyType, detail::enable_if_t <
detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value, int > = 0 >
static std::string_view lookup_key(KeyType && key)
{
return std::forward<KeyType>(key);
}
#endif
/// @brief erase an element from the object and return the following one
/// Not every map returns an iterator from erase(iterator): some containers
/// (e.g., Abseil's hash maps) return void to avoid computing a successor
@@ -1010,19 +1028,39 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using copy_scratch_value_t = std::pair<typename object_t::key_type, basic_json>;
using copy_scratch_t = std::vector<copy_scratch_value_t, AllocatorType<copy_scratch_value_t>>;
/// @brief copy everything of @a src into @a dst but its type and value
static void copy_metadata(const basic_json& src, basic_json& dst)
{
// a custom base class is only required to be copy-constructible and
// move-assignable, so the copy has to go through a temporary
static_cast<json_base_class_t&>(dst) = json_base_class_t(static_cast<const json_base_class_t&>(src));
/// @brief tag selecting the constructor below; used only to build the
/// elements of a deep copy (@ref copy_array_level, @ref copy_object_level)
struct copy_construct_tag {};
public:
/*!
@brief construct a null value whose base class - and, with @ref
JSON_DIAGNOSTIC_POSITIONS, positions - are copied from @a src
Copy-constructing @ref json_base_class_t here, rather than default-
constructing the element and assigning its base class afterwards, means
that copying a @ref basic_json only ever requires a copy-constructible
base class, and never a move-assignable one as well.
@note this constructor has to be public: @ref copy_array_level and
@ref copy_object_level reach it through @ref array_t's or @ref
object_t's own emplace_back(), which constructs the element from
outside @ref basic_json and so cannot call a private constructor.
@ref copy_construct_tag is private, though, and nothing in the
public interface hands out a value of it, so outside code can still
never name it to call this constructor itself.
*/
basic_json(copy_construct_tag /*unused*/, const basic_json& src)
: json_base_class_t(src)
#if JSON_DIAGNOSTIC_POSITIONS
dst.start_position = src.start_position;
dst.end_position = src.end_position;
, start_position(src.start_position)
, end_position(src.end_position)
#endif
{
}
private:
/*!
@brief copy the value of @a src into @a dst, which must not be structured
@@ -1085,8 +1123,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
/*!
@brief copy everything of @a src into the null value @a dst but the children
@brief finish the copy @a dst of @a src that a @ref copy_construct_tag
constructor started, other than the children of an object or array
@a dst already has @a src's base class and, with @ref
JSON_DIAGNOSTIC_POSITIONS, positions; only its value is still missing.
Objects and arrays are not copied here; they are appended to @a worklist to
be created later by @ref copy_iteratively. Until that happens, @a dst remains
a null value, so that a partially built copy can be destroyed at any point
@@ -1094,8 +1135,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
*/
static void copy_shallow(const basic_json& src, basic_json& dst, copy_worklist_t& worklist)
{
copy_metadata(src, dst);
if (src.m_data.m_type == value_t::object || src.m_data.m_type == value_t::array)
{
// defer: dst stays a null value until its container exists
@@ -1116,15 +1155,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
const array_t& src_array = *src.m_data.m_value.array;
// create all elements up front: growing the array afterwards could
// invalidate the pointers that are handed to the worklist; resize()
// rather than the fill constructor, because not every array type
// provides the latter (e.g., ones without a matching allocator-aware
// fill constructor)
dst.m_data.m_value.array = create<array_t>();
// only now that the array exists may dst stop being a null value
dst.m_data.m_type = value_t::array;
dst.m_data.m_value.array->resize(src_array.size());
// create every element - its base class already copy-constructed from
// its counterpart in src, via the copy_construct_tag constructor -
// before any of their addresses are handed to worklist below: growing
// the array while that is going on could reallocate it and invalidate
// addresses taken from an earlier iteration
for (const auto& src_element : src_array)
{
dst.m_data.m_value.array->emplace_back(copy_construct_tag{}, src_element);
}
auto dst_it = dst.m_data.m_value.array->begin();
for (auto src_it = src_array.cbegin(); src_it != src_array.cend(); ++src_it, ++dst_it)
@@ -1133,6 +1176,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
}
/// @brief create the object type from a range, preserving @a src_object's
/// comparator when the object type supports it
/// Enabled for object types that provide a key_comp() and a matching
/// range-plus-comparator constructor, such as std::map. Other object
/// types, such as nlohmann::ordered_map, fall back to the plain range
/// constructor and default-construct their comparator, just as they
/// always have (@ref detail::is_comparator_constructible_object_type).
template<typename Iterator, detail::enable_if_t<
detail::is_comparator_constructible_object_type<object_t, Iterator>::value, int> = 0>
static object_t* create_object_with_comparator(const object_t& src_object, Iterator first, Iterator last)
{
return create<object_t>(first, last, src_object.key_comp());
}
template<typename Iterator, detail::enable_if_t<
detail::negation<detail::is_comparator_constructible_object_type<object_t, Iterator>>::value, int> = 0>
static object_t* create_object_with_comparator(const object_t& /*src_object*/, Iterator first, Iterator last)
{
return create<object_t>(first, last);
}
/// @brief create the copy of the object @a src in @a dst
/// @note structured values are appended to @a worklist instead
static void copy_object_level(const basic_json& src, basic_json& dst,
@@ -1142,15 +1206,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// build the complete key skeleton and hand it to the object's range
// constructor: adding the keys one by one would be quadratic for object
// types that are backed by a vector, such as nlohmann::ordered_map
// types that are backed by a vector, such as nlohmann::ordered_map; each
// value's base class is already copy-constructed from its counterpart
// in src, via the copy_construct_tag constructor
scratch.clear();
scratch.reserve(src_object.size());
for (const auto& element : src_object)
{
scratch.emplace_back(element.first, basic_json());
scratch.emplace_back(element.first, basic_json(copy_construct_tag{}, element.second));
}
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
dst.m_data.m_value.object = create_object_with_comparator(src_object,
std::make_move_iterator(scratch.begin()),
std::make_move_iterator(scratch.end()));
// only now that the object exists may dst stop being a null value
dst.m_data.m_type = value_t::object;
@@ -1528,15 +1595,65 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
*/
template<bool Ordered>
static compare_result compare_leaves(const_reference lhs, const_reference rhs) noexcept
{
return compare_leaves(lhs, rhs, std::integral_constant<bool, Ordered> {});
}
/// @brief compare two leaves that are only being checked for equality
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::false_type /*ordered*/) noexcept
{
if (lhs == rhs)
{
return compare_result::equal;
}
return order_leaves(lhs, rhs, std::integral_constant<bool, Ordered> {});
return order_leaves(lhs, rhs, std::false_type {});
}
#if JSON_HAS_THREE_WAY_COMPARISON
/*!
@brief compare two leaves that are being ordered, for operator<=>
Reached only from operator<=>, so the leaves must be classified exactly
as operator<=> classifies them - which is not the same as asking
== and then order_leaves(), the way the other overload does it. The two
disagree on a binary value: == also compares the subtype, but <=> compares
only the bytes, through std::vector<std::uint8_t>::operator<=>. Using <=>
itself here keeps a leaf pair classified the same way regardless of how
deep it is nested - == first would again call operator<=> a level down
through order_leaves(), but call it after a mismatching == already ended
the comparison for a pair that <=> alone would still call equivalent.
*/
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
{
const std::partial_ordering order = lhs <=> rhs; // *NOPAD*
if (order == 0)
{
return compare_result::equal;
}
if (order < 0)
{
return compare_result::less;
}
if (order > 0)
{
return compare_result::greater;
}
return compare_result::unordered;
}
#else
/// @brief compare two leaves that are being ordered, for operator<
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
{
if (lhs == rhs)
{
return compare_result::equal;
}
return order_leaves(lhs, rhs, std::true_type {});
}
#endif
/*!
@brief compare two object keys
@@ -1833,12 +1950,42 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
assert_invariant();
}
private:
/// whether a basic_json specialization can be converted implicitly into this one;
/// with JSON_USE_IMPLICIT_CONVERSIONS set to 0, this is only the case if both share
/// the same string type (see https://github.com/nlohmann/json/issues/2649)
template<typename BasicJsonType>
using is_implicitly_convertible_basic_json = std::integral_constant < bool,
(JSON_USE_IMPLICIT_CONVERSIONS != 0)
|| std::is_same<typename BasicJsonType::string_t, string_t>::value >;
/// tag to select the constructor that performs the conversion from another basic_json specialization
struct convert_basic_json_tag {};
public:
/// @brief create a JSON value from an existing one
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template < typename BasicJsonType,
detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >
detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value
&& is_implicitly_convertible_basic_json<BasicJsonType>::value, int > = 0 >
basic_json(const BasicJsonType& val)
: basic_json(val, convert_basic_json_tag{})
{}
/// @brief create a JSON value from an existing one
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template < typename BasicJsonType,
detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value
&& !is_implicitly_convertible_basic_json<BasicJsonType>::value, int > = 0 >
explicit basic_json(const BasicJsonType& val)
: basic_json(val, convert_basic_json_tag{})
{}
private:
template<typename BasicJsonType>
basic_json(const BasicJsonType& val, convert_basic_json_tag /*unused*/)
#if JSON_DIAGNOSTIC_POSITIONS
: start_position(val.start_pos()),
end_position(val.end_pos())
@@ -1858,6 +2005,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
assert_invariant();
}
public:
/// @brief create a container (array or object) from an initializer list
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
basic_json(initializer_list_t init,
@@ -2624,7 +2772,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
int > = 0 >
BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const
{
return *this;
return BasicJsonType(*this);
}
/*!
@@ -2763,7 +2911,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
int> = 0>
ValueType & get_to(ValueType& v) const
{
v = *this;
v = ValueType(*this);
return v;
}
@@ -2958,7 +3106,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it == m_data.m_value.object->end())
{
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
@@ -2996,7 +3144,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it == m_data.m_value.object->end())
{
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
@@ -3065,6 +3213,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for arrays
if (JSON_HEDLEY_LIKELY(is_array()))
{
JSON_ASSERT(idx < m_data.m_value.array->size());
return m_data.m_value.array->operator[](idx);
}
@@ -3139,7 +3288,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
auto result = m_data.m_value.object->emplace(std::forward<KeyType>(key), nullptr);
auto result = m_data.m_value.object->emplace(lookup_key(std::forward<KeyType>(key)), nullptr);
return set_parent(result.first->second);
}
@@ -3155,7 +3304,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
JSON_ASSERT(it != m_data.m_value.object->end());
return it->second;
}
@@ -3165,8 +3314,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
private:
template<typename KeyType>
using is_comparable_with_object_key = detail::is_comparable <
object_comparator_t, const typename object_t::key_type&, KeyType >;
using is_comparable_with_object_key = std::integral_constant < bool,
detail::is_comparable <
object_comparator_t, const typename object_t::key_type&, KeyType >::value
|| detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value >;
template<typename ValueType>
using value_return_type = std::conditional <
@@ -3553,7 +3704,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
}
const auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
const auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it != m_data.m_value.object->end())
{
m_data.m_value.object->erase(it);
@@ -3580,7 +3731,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
size_type erase(KeyType && key)
{
return erase_internal(std::forward<KeyType>(key));
return erase_internal(lookup_key(std::forward<KeyType>(key)));
}
/// @brief remove element from a JSON array given an index
@@ -3660,7 +3811,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (is_object())
{
result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
result.m_it.object_iterator = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
}
return result;
@@ -3676,7 +3827,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (is_object())
{
result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
result.m_it.object_iterator = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
}
return result;
@@ -3699,7 +3850,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
size_type count(KeyType && key) const
{
// return 0 for all nonobject types
return is_object() ? m_data.m_value.object->count(std::forward<KeyType>(key)) : 0;
return is_object() ? m_data.m_value.object->count(lookup_key(std::forward<KeyType>(key))) : 0;
}
/// @brief check the existence of an element in a JSON object
@@ -3717,7 +3868,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_HEDLEY_WARN_UNUSED_RESULT
bool contains(KeyType && key) const
{
return is_object() && m_data.m_value.object->find(std::forward<KeyType>(key)) != m_data.m_value.object->end();
return is_object() && m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key))) != m_data.m_value.object->end();
}
/// @brief check the existence of an element in a JSON object given a JSON pointer
@@ -4865,7 +5016,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
bool operator==(ScalarType rhs) const noexcept
bool operator==(ScalarType rhs) const noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return *this == basic_json(rhs);
}
@@ -4888,7 +5039,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*
std::partial_ordering operator<=>(ScalarType rhs) const noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value) // *NOPAD*
{
return *this <=> basic_json(rhs); // *NOPAD*
}
@@ -4913,7 +5064,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
bool operator<=(ScalarType rhs) const noexcept
bool operator<=(ScalarType rhs) const noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return *this <= basic_json(rhs);
}
@@ -4934,10 +5085,31 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
bool operator>=(ScalarType rhs) const noexcept
bool operator>=(ScalarType rhs) const noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return *this >= basic_json(rhs);
}
// a scalar on the left-hand side would otherwise select the candidate
// rewritten from operator<=>, which does not emulate the legacy behavior
/// @brief comparison: less than or equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
{
return basic_json(lhs) <= rhs;
}
/// @brief comparison: greater than or equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
{
return basic_json(lhs) >= rhs;
}
#endif
#else
/// @brief comparison: equal
@@ -4959,7 +5131,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator==(const_reference lhs, ScalarType rhs) noexcept
friend bool operator==(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs == basic_json(rhs);
}
@@ -4968,7 +5140,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator==(ScalarType lhs, const_reference rhs) noexcept
friend bool operator==(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) == rhs;
}
@@ -4984,7 +5156,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept
friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs != basic_json(rhs);
}
@@ -4993,7 +5165,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept
friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) != rhs;
}
@@ -5013,7 +5185,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator<(const_reference lhs, ScalarType rhs) noexcept
friend bool operator<(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs < basic_json(rhs);
}
@@ -5022,7 +5194,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator<(ScalarType lhs, const_reference rhs) noexcept
friend bool operator<(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) < rhs;
}
@@ -5042,7 +5214,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept
friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs <= basic_json(rhs);
}
@@ -5051,7 +5223,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) <= rhs;
}
@@ -5072,7 +5244,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator>(const_reference lhs, ScalarType rhs) noexcept
friend bool operator>(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs > basic_json(rhs);
}
@@ -5081,7 +5253,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator>(ScalarType lhs, const_reference rhs) noexcept
friend bool operator>(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) > rhs;
}
@@ -5101,7 +5273,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept
friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs >= basic_json(rhs);
}
@@ -5110,7 +5282,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) >= rhs;
}
+9 -6
View File
@@ -74,7 +74,9 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return *this;
}
std::pair<iterator, bool> emplace(const key_type& key, T&& t)
template<class V, detail::enable_if_t<
detail::is_constructible<T, V>::value, int> = 0>
std::pair<iterator, bool> emplace(const key_type& key, V && t)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
@@ -83,13 +85,14 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {it, false};
}
}
append(key, std::forward<T>(t));
append(key, std::forward<V>(t));
return {std::prev(this->end()), true};
}
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
std::pair<iterator, bool> emplace(KeyType && key, T && t)
template<class KeyType, class V, detail::enable_if_t<
detail::conjunction<detail::is_usable_as_key_type<key_compare, key_type, KeyType>,
detail::is_constructible<T, V>>::value, int> = 0>
std::pair<iterator, bool> emplace(KeyType && key, V && t)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
@@ -98,7 +101,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {it, false};
}
}
append(std::forward<KeyType>(key), std::forward<T>(t));
append(std::forward<KeyType>(key), std::forward<V>(t));
return {std::prev(this->end()), true};
}
+324 -73
View File
@@ -4189,6 +4189,37 @@ struct actual_object_comparator
template<typename BasicJsonType>
using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;
template<typename T>
using detect_key_comp = decltype(std::declval<const T&>().key_comp());
// whether ObjectType can be constructed from a pair of Iterator together with
// a copy of its own comparator, the way std::map can: it needs a nested
// key_compare, a const key_comp() convertible to it, and a matching
// (Iterator, Iterator, const key_compare&) constructor.
//
// used to preserve a stateful comparator when a copy is built from a range
// past the iterative deep copy's nesting bound (see copy_object_level); an
// object type that does not satisfy this, such as nlohmann::ordered_map
// (which has key_compare for its std::map-like interface, but no key_comp()),
// keeps default-constructing its comparator, just as it always has
template<typename ObjectType, typename Iterator, typename = void>
struct is_comparator_constructible_object_type_impl : std::false_type {};
template<typename ObjectType, typename Iterator>
struct is_comparator_constructible_object_type_impl <
ObjectType, Iterator, enable_if_t<is_detected<detect_key_compare, ObjectType>::value >>
{
using key_compare = typename ObjectType::key_compare;
static constexpr bool value =
is_detected_convertible<key_compare, detect_key_comp, ObjectType>::value &&
std::is_constructible<ObjectType, Iterator, Iterator, const key_compare&>::value;
};
template<typename ObjectType, typename Iterator>
struct is_comparator_constructible_object_type
: is_comparator_constructible_object_type_impl<ObjectType, Iterator> {};
/////////////////
// char_traits //
/////////////////
@@ -4760,6 +4791,30 @@ using is_usable_as_key_type = typename std::conditional <
std::true_type,
std::false_type >::type;
#ifdef JSON_HAS_CPP_17
// type trait to check if KeyType can only be used as an object key after
// converting it to std::string_view: it is convertible to std::string_view, the
// object's comparator cannot compare it with object_t::key_type directly, but
// can compare a std::string_view. JSON pointers and JSON iterators are ruled out
// first, so that the conversion checks are never instantiated for them (a JSON
// pointer's deprecated conversion to string_t would be named otherwise).
template < typename BasicJsonType, typename KeyTypeCVRef, typename KeyType = uncvref_t<KeyTypeCVRef>,
bool = is_json_pointer<KeyType>::value || is_json_iterator_of<BasicJsonType, KeyType>::value >
struct is_string_view_convertible_key_type : std::false_type {};
template<typename BasicJsonType, typename KeyTypeCVRef, typename KeyType>
struct is_string_view_convertible_key_type<BasicJsonType, KeyTypeCVRef, KeyType, false>
: std::integral_constant < bool,
std::is_convertible<KeyTypeCVRef, std::string_view>::value
&& !is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
typename BasicJsonType::object_t::key_type, KeyTypeCVRef, true, false>::value
&& is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
typename BasicJsonType::object_t::key_type, std::string_view, true, false>::value > {};
#else
template<typename BasicJsonType, typename KeyTypeCVRef>
struct is_string_view_convertible_key_type : std::false_type {};
#endif
// type trait to check if KeyType can be used as an object key
// true if:
// - KeyType is comparable with BasicJsonType::object_t::key_type
@@ -4773,9 +4828,7 @@ using is_usable_as_basic_json_key_type = typename std::conditional <
typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
RequireTransparentComparator, ExcludeObjectKeyType>::value
&& !is_json_iterator_of<BasicJsonType, KeyType>::value)
#ifdef JSON_HAS_CPP_17
|| std::is_convertible<KeyType, std::string_view>::value
#endif
|| is_string_view_convertible_key_type<BasicJsonType, KeyTypeCVRef>::value
, std::true_type,
std::false_type >::type;
@@ -6981,7 +7034,9 @@ void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(std::is_not
{
if (opt.has_value())
{
j = *opt;
// explicit construction, as the conversion from a basic_json with a different
// string type is explicit if JSON_USE_IMPLICIT_CONVERSIONS is 0 (#2649)
j = BasicJsonType(*opt);
}
else
{
@@ -11093,10 +11148,15 @@ class lexer : public lexer_base<BasicJsonType>
case '\n':
case '\r':
case char_traits<char_type>::eof():
return true;
#if !JSON_STRICT_NUL_HANDLING
case '\0':
#endif
// a NUL byte is the end of the input (see scan()),
// so leave it for scan() to see
unget();
return true;
#endif
default:
break;
@@ -22379,6 +22439,10 @@ class json_pointer
@return const reference to the JSON value pointed to by the JSON
pointer
@pre Every object key and array index the pointer refers to exists.
Like the const operator[] for keys and indices, a missing one is
undefined behavior, guarded by a runtime assertion.
@throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if an array index was not a number
@throw out_of_range.402 if the array index '-' is used
@@ -22393,7 +22457,8 @@ class json_pointer
{
case detail::value_t::object:
{
// use unchecked object access
// use unchecked object access; the const operator[]
// asserts that the key exists
ptr = &ptr->operator[](reference_token);
break;
}
@@ -22406,7 +22471,8 @@ class json_pointer
JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
}
// use unchecked array access
// use unchecked array access; the const operator[]
// asserts that the index exists
ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
break;
}
@@ -22522,19 +22588,29 @@ class json_pointer
return nullptr;
}
// may throw parse_error.106/109 for a malformed index; an
// index that is syntactically valid but cannot be
// represented (out_of_range.404/410) is treated like an
// out-of-range index below
typename BasicJsonType::size_type idx{};
JSON_TRY
// tokens that array_index() rejects with parse_error.106/109
// are passed on to it; all other tokens that it would reject
// with out_of_range.404/410 are detected here, so that this
// also works without exceptions
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 && !(reference_token[0] >= '1' && reference_token[0] <= '9')))
{
idx = array_index<BasicJsonType>(reference_token);
static_cast<void>(array_index<BasicJsonType>(reference_token)); // throws parse_error.106/109
}
JSON_INTERNAL_CATCH (detail::out_of_range&)
if (JSON_HEDLEY_UNLIKELY(reference_token.empty() || !std::all_of(reference_token.begin(), reference_token.end(), [](const char c)
{
return c >= '0' && c <= '9';
})))
{
return nullptr;
}
errno = 0; // strtoull() does not reset errno on success
char* p_end = nullptr; // NOLINT(misc-const-correctness)
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE || magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
{
return nullptr;
}
const auto idx = static_cast<typename BasicJsonType::size_type>(magnitude);
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
{
@@ -28669,7 +28745,9 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return *this;
}
std::pair<iterator, bool> emplace(const key_type& key, T&& t)
template<class V, detail::enable_if_t<
detail::is_constructible<T, V>::value, int> = 0>
std::pair<iterator, bool> emplace(const key_type& key, V && t)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
@@ -28678,13 +28756,14 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {it, false};
}
}
append(key, std::forward<T>(t));
append(key, std::forward<V>(t));
return {std::prev(this->end()), true};
}
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
std::pair<iterator, bool> emplace(KeyType && key, T && t)
template<class KeyType, class V, detail::enable_if_t<
detail::conjunction<detail::is_usable_as_key_type<key_compare, key_type, KeyType>,
detail::is_constructible<T, V>>::value, int> = 0>
std::pair<iterator, bool> emplace(KeyType && key, V && t)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
@@ -28693,7 +28772,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {it, false};
}
}
append(std::forward<KeyType>(key), std::forward<T>(t));
append(std::forward<KeyType>(key), std::forward<V>(t));
return {std::prev(this->end()), true};
}
@@ -29786,6 +29865,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return it;
}
/// @brief the key to look up an object member with: the key itself, or its
/// std::string_view if the object can only be searched with that
template < typename KeyType, detail::enable_if_t <
!detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value, int > = 0 >
static KeyType && lookup_key(KeyType && key) noexcept
{
return std::forward<KeyType>(key);
}
#ifdef JSON_HAS_CPP_17
template < typename KeyType, detail::enable_if_t <
detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value, int > = 0 >
static std::string_view lookup_key(KeyType && key)
{
return std::forward<KeyType>(key);
}
#endif
/// @brief erase an element from the object and return the following one
/// Not every map returns an iterator from erase(iterator): some containers
/// (e.g., Abseil's hash maps) return void to avoid computing a successor
@@ -29983,19 +30080,39 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using copy_scratch_value_t = std::pair<typename object_t::key_type, basic_json>;
using copy_scratch_t = std::vector<copy_scratch_value_t, AllocatorType<copy_scratch_value_t>>;
/// @brief copy everything of @a src into @a dst but its type and value
static void copy_metadata(const basic_json& src, basic_json& dst)
{
// a custom base class is only required to be copy-constructible and
// move-assignable, so the copy has to go through a temporary
static_cast<json_base_class_t&>(dst) = json_base_class_t(static_cast<const json_base_class_t&>(src));
/// @brief tag selecting the constructor below; used only to build the
/// elements of a deep copy (@ref copy_array_level, @ref copy_object_level)
struct copy_construct_tag {};
public:
/*!
@brief construct a null value whose base class - and, with @ref
JSON_DIAGNOSTIC_POSITIONS, positions - are copied from @a src
Copy-constructing @ref json_base_class_t here, rather than default-
constructing the element and assigning its base class afterwards, means
that copying a @ref basic_json only ever requires a copy-constructible
base class, and never a move-assignable one as well.
@note this constructor has to be public: @ref copy_array_level and
@ref copy_object_level reach it through @ref array_t's or @ref
object_t's own emplace_back(), which constructs the element from
outside @ref basic_json and so cannot call a private constructor.
@ref copy_construct_tag is private, though, and nothing in the
public interface hands out a value of it, so outside code can still
never name it to call this constructor itself.
*/
basic_json(copy_construct_tag /*unused*/, const basic_json& src)
: json_base_class_t(src)
#if JSON_DIAGNOSTIC_POSITIONS
dst.start_position = src.start_position;
dst.end_position = src.end_position;
, start_position(src.start_position)
, end_position(src.end_position)
#endif
{
}
private:
/*!
@brief copy the value of @a src into @a dst, which must not be structured
@@ -30058,8 +30175,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
/*!
@brief copy everything of @a src into the null value @a dst but the children
@brief finish the copy @a dst of @a src that a @ref copy_construct_tag
constructor started, other than the children of an object or array
@a dst already has @a src's base class and, with @ref
JSON_DIAGNOSTIC_POSITIONS, positions; only its value is still missing.
Objects and arrays are not copied here; they are appended to @a worklist to
be created later by @ref copy_iteratively. Until that happens, @a dst remains
a null value, so that a partially built copy can be destroyed at any point
@@ -30067,8 +30187,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
*/
static void copy_shallow(const basic_json& src, basic_json& dst, copy_worklist_t& worklist)
{
copy_metadata(src, dst);
if (src.m_data.m_type == value_t::object || src.m_data.m_type == value_t::array)
{
// defer: dst stays a null value until its container exists
@@ -30089,15 +30207,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
const array_t& src_array = *src.m_data.m_value.array;
// create all elements up front: growing the array afterwards could
// invalidate the pointers that are handed to the worklist; resize()
// rather than the fill constructor, because not every array type
// provides the latter (e.g., ones without a matching allocator-aware
// fill constructor)
dst.m_data.m_value.array = create<array_t>();
// only now that the array exists may dst stop being a null value
dst.m_data.m_type = value_t::array;
dst.m_data.m_value.array->resize(src_array.size());
// create every element - its base class already copy-constructed from
// its counterpart in src, via the copy_construct_tag constructor -
// before any of their addresses are handed to worklist below: growing
// the array while that is going on could reallocate it and invalidate
// addresses taken from an earlier iteration
for (const auto& src_element : src_array)
{
dst.m_data.m_value.array->emplace_back(copy_construct_tag{}, src_element);
}
auto dst_it = dst.m_data.m_value.array->begin();
for (auto src_it = src_array.cbegin(); src_it != src_array.cend(); ++src_it, ++dst_it)
@@ -30106,6 +30228,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
}
/// @brief create the object type from a range, preserving @a src_object's
/// comparator when the object type supports it
/// Enabled for object types that provide a key_comp() and a matching
/// range-plus-comparator constructor, such as std::map. Other object
/// types, such as nlohmann::ordered_map, fall back to the plain range
/// constructor and default-construct their comparator, just as they
/// always have (@ref detail::is_comparator_constructible_object_type).
template<typename Iterator, detail::enable_if_t<
detail::is_comparator_constructible_object_type<object_t, Iterator>::value, int> = 0>
static object_t* create_object_with_comparator(const object_t& src_object, Iterator first, Iterator last)
{
return create<object_t>(first, last, src_object.key_comp());
}
template<typename Iterator, detail::enable_if_t<
detail::negation<detail::is_comparator_constructible_object_type<object_t, Iterator>>::value, int> = 0>
static object_t* create_object_with_comparator(const object_t& /*src_object*/, Iterator first, Iterator last)
{
return create<object_t>(first, last);
}
/// @brief create the copy of the object @a src in @a dst
/// @note structured values are appended to @a worklist instead
static void copy_object_level(const basic_json& src, basic_json& dst,
@@ -30115,15 +30258,18 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// build the complete key skeleton and hand it to the object's range
// constructor: adding the keys one by one would be quadratic for object
// types that are backed by a vector, such as nlohmann::ordered_map
// types that are backed by a vector, such as nlohmann::ordered_map; each
// value's base class is already copy-constructed from its counterpart
// in src, via the copy_construct_tag constructor
scratch.clear();
scratch.reserve(src_object.size());
for (const auto& element : src_object)
{
scratch.emplace_back(element.first, basic_json());
scratch.emplace_back(element.first, basic_json(copy_construct_tag{}, element.second));
}
dst.m_data.m_value.object = create<object_t>(std::make_move_iterator(scratch.begin()),
dst.m_data.m_value.object = create_object_with_comparator(src_object,
std::make_move_iterator(scratch.begin()),
std::make_move_iterator(scratch.end()));
// only now that the object exists may dst stop being a null value
dst.m_data.m_type = value_t::object;
@@ -30501,15 +30647,65 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
*/
template<bool Ordered>
static compare_result compare_leaves(const_reference lhs, const_reference rhs) noexcept
{
return compare_leaves(lhs, rhs, std::integral_constant<bool, Ordered> {});
}
/// @brief compare two leaves that are only being checked for equality
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::false_type /*ordered*/) noexcept
{
if (lhs == rhs)
{
return compare_result::equal;
}
return order_leaves(lhs, rhs, std::integral_constant<bool, Ordered> {});
return order_leaves(lhs, rhs, std::false_type {});
}
#if JSON_HAS_THREE_WAY_COMPARISON
/*!
@brief compare two leaves that are being ordered, for operator<=>
Reached only from operator<=>, so the leaves must be classified exactly
as operator<=> classifies them - which is not the same as asking
== and then order_leaves(), the way the other overload does it. The two
disagree on a binary value: == also compares the subtype, but <=> compares
only the bytes, through std::vector<std::uint8_t>::operator<=>. Using <=>
itself here keeps a leaf pair classified the same way regardless of how
deep it is nested - == first would again call operator<=> a level down
through order_leaves(), but call it after a mismatching == already ended
the comparison for a pair that <=> alone would still call equivalent.
*/
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
{
const std::partial_ordering order = lhs <=> rhs; // *NOPAD*
if (order == 0)
{
return compare_result::equal;
}
if (order < 0)
{
return compare_result::less;
}
if (order > 0)
{
return compare_result::greater;
}
return compare_result::unordered;
}
#else
/// @brief compare two leaves that are being ordered, for operator<
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
{
if (lhs == rhs)
{
return compare_result::equal;
}
return order_leaves(lhs, rhs, std::true_type {});
}
#endif
/*!
@brief compare two object keys
@@ -30806,12 +31002,42 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
assert_invariant();
}
private:
/// whether a basic_json specialization can be converted implicitly into this one;
/// with JSON_USE_IMPLICIT_CONVERSIONS set to 0, this is only the case if both share
/// the same string type (see https://github.com/nlohmann/json/issues/2649)
template<typename BasicJsonType>
using is_implicitly_convertible_basic_json = std::integral_constant < bool,
(JSON_USE_IMPLICIT_CONVERSIONS != 0)
|| std::is_same<typename BasicJsonType::string_t, string_t>::value >;
/// tag to select the constructor that performs the conversion from another basic_json specialization
struct convert_basic_json_tag {};
public:
/// @brief create a JSON value from an existing one
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template < typename BasicJsonType,
detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >
detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value
&& is_implicitly_convertible_basic_json<BasicJsonType>::value, int > = 0 >
basic_json(const BasicJsonType& val)
: basic_json(val, convert_basic_json_tag{})
{}
/// @brief create a JSON value from an existing one
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template < typename BasicJsonType,
detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value
&& !is_implicitly_convertible_basic_json<BasicJsonType>::value, int > = 0 >
explicit basic_json(const BasicJsonType& val)
: basic_json(val, convert_basic_json_tag{})
{}
private:
template<typename BasicJsonType>
basic_json(const BasicJsonType& val, convert_basic_json_tag /*unused*/)
#if JSON_DIAGNOSTIC_POSITIONS
: start_position(val.start_pos()),
end_position(val.end_pos())
@@ -30831,6 +31057,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
assert_invariant();
}
public:
/// @brief create a container (array or object) from an initializer list
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
basic_json(initializer_list_t init,
@@ -31597,7 +31824,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
int > = 0 >
BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const
{
return *this;
return BasicJsonType(*this);
}
/*!
@@ -31736,7 +31963,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
int> = 0>
ValueType & get_to(ValueType& v) const
{
v = *this;
v = ValueType(*this);
return v;
}
@@ -31931,7 +32158,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it == m_data.m_value.object->end())
{
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
@@ -31969,7 +32196,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
}
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it == m_data.m_value.object->end())
{
JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
@@ -32038,6 +32265,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for arrays
if (JSON_HEDLEY_LIKELY(is_array()))
{
JSON_ASSERT(idx < m_data.m_value.array->size());
return m_data.m_value.array->operator[](idx);
}
@@ -32112,7 +32340,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
auto result = m_data.m_value.object->emplace(std::forward<KeyType>(key), nullptr);
auto result = m_data.m_value.object->emplace(lookup_key(std::forward<KeyType>(key)), nullptr);
return set_parent(result.first->second);
}
@@ -32128,7 +32356,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// const operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
JSON_ASSERT(it != m_data.m_value.object->end());
return it->second;
}
@@ -32138,8 +32366,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
private:
template<typename KeyType>
using is_comparable_with_object_key = detail::is_comparable <
object_comparator_t, const typename object_t::key_type&, KeyType >;
using is_comparable_with_object_key = std::integral_constant < bool,
detail::is_comparable <
object_comparator_t, const typename object_t::key_type&, KeyType >::value
|| detail::is_string_view_convertible_key_type<basic_json_t, KeyType>::value >;
template<typename ValueType>
using value_return_type = std::conditional <
@@ -32526,7 +32756,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
}
const auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
const auto it = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
if (it != m_data.m_value.object->end())
{
m_data.m_value.object->erase(it);
@@ -32553,7 +32783,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
size_type erase(KeyType && key)
{
return erase_internal(std::forward<KeyType>(key));
return erase_internal(lookup_key(std::forward<KeyType>(key)));
}
/// @brief remove element from a JSON array given an index
@@ -32633,7 +32863,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (is_object())
{
result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
result.m_it.object_iterator = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
}
return result;
@@ -32649,7 +32879,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (is_object())
{
result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
result.m_it.object_iterator = m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key)));
}
return result;
@@ -32672,7 +32902,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
size_type count(KeyType && key) const
{
// return 0 for all nonobject types
return is_object() ? m_data.m_value.object->count(std::forward<KeyType>(key)) : 0;
return is_object() ? m_data.m_value.object->count(lookup_key(std::forward<KeyType>(key))) : 0;
}
/// @brief check the existence of an element in a JSON object
@@ -32690,7 +32920,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_HEDLEY_WARN_UNUSED_RESULT
bool contains(KeyType && key) const
{
return is_object() && m_data.m_value.object->find(std::forward<KeyType>(key)) != m_data.m_value.object->end();
return is_object() && m_data.m_value.object->find(lookup_key(std::forward<KeyType>(key))) != m_data.m_value.object->end();
}
/// @brief check the existence of an element in a JSON object given a JSON pointer
@@ -33838,7 +34068,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
bool operator==(ScalarType rhs) const noexcept
bool operator==(ScalarType rhs) const noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return *this == basic_json(rhs);
}
@@ -33861,7 +34091,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*
std::partial_ordering operator<=>(ScalarType rhs) const noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value) // *NOPAD*
{
return *this <=> basic_json(rhs); // *NOPAD*
}
@@ -33886,7 +34116,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
bool operator<=(ScalarType rhs) const noexcept
bool operator<=(ScalarType rhs) const noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return *this <= basic_json(rhs);
}
@@ -33907,10 +34137,31 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
bool operator>=(ScalarType rhs) const noexcept
bool operator>=(ScalarType rhs) const noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return *this >= basic_json(rhs);
}
// a scalar on the left-hand side would otherwise select the candidate
// rewritten from operator<=>, which does not emulate the legacy behavior
/// @brief comparison: less than or equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
{
return basic_json(lhs) <= rhs;
}
/// @brief comparison: greater than or equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
{
return basic_json(lhs) >= rhs;
}
#endif
#else
/// @brief comparison: equal
@@ -33932,7 +34183,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator==(const_reference lhs, ScalarType rhs) noexcept
friend bool operator==(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs == basic_json(rhs);
}
@@ -33941,7 +34192,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator==(ScalarType lhs, const_reference rhs) noexcept
friend bool operator==(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) == rhs;
}
@@ -33957,7 +34208,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept
friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs != basic_json(rhs);
}
@@ -33966,7 +34217,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept
friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) != rhs;
}
@@ -33986,7 +34237,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator<(const_reference lhs, ScalarType rhs) noexcept
friend bool operator<(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs < basic_json(rhs);
}
@@ -33995,7 +34246,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator<(ScalarType lhs, const_reference rhs) noexcept
friend bool operator<(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) < rhs;
}
@@ -34015,7 +34266,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept
friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs <= basic_json(rhs);
}
@@ -34024,7 +34275,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) <= rhs;
}
@@ -34045,7 +34296,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator>(const_reference lhs, ScalarType rhs) noexcept
friend bool operator>(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs > basic_json(rhs);
}
@@ -34054,7 +34305,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator>(ScalarType lhs, const_reference rhs) noexcept
friend bool operator>(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) > rhs;
}
@@ -34074,7 +34325,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept
friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return lhs >= basic_json(rhs);
}
@@ -34083,7 +34334,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
template<typename ScalarType, typename std::enable_if<
std::is_scalar<ScalarType>::value, int>::type = 0>
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
{
return basic_json(lhs) >= rhs;
}
+36
View File
@@ -13,6 +13,7 @@
#include <cstdint>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
@@ -463,6 +464,41 @@ TEST_CASE("alternative string type")
CHECK(c.dump() == R"({"a":3})");
}
SECTION("conversion between basic_json specializations (#2649)")
{
// explicit conversions are always possible
CHECK(std::is_constructible<nlohmann::json, alt_json>::value);
CHECK(std::is_constructible<alt_json, nlohmann::json>::value);
CHECK(std::is_constructible<nlohmann::json, nlohmann::ordered_json>::value);
CHECK(std::is_constructible<nlohmann::ordered_json, nlohmann::json>::value);
// specializations with the same string type are implicitly convertible
CHECK(std::is_convertible<nlohmann::ordered_json, nlohmann::json>::value);
CHECK(std::is_convertible<nlohmann::json, nlohmann::ordered_json>::value);
// specializations with different string types are only implicitly convertible
// if implicit conversions are enabled
#if JSON_USE_IMPLICIT_CONVERSIONS
CHECK(std::is_convertible<alt_json, nlohmann::json>::value);
CHECK(std::is_convertible<nlohmann::json, alt_json>::value);
#else
CHECK_FALSE(std::is_convertible<alt_json, nlohmann::json>::value);
CHECK_FALSE(std::is_convertible<nlohmann::json, alt_json>::value);
#endif
// get<BasicJsonType>() works in either case
const nlohmann::json j = {{"foo", 1}, {"bar", true}};
CHECK(j.get<nlohmann::ordered_json>() == nlohmann::ordered_json(j));
// (only a number is converted here, as objects and strings are affected by #3425)
CHECK(nlohmann::json(42).get<alt_json>() == 42);
CHECK(alt_json(nlohmann::json(42)) == 42);
// get_to() also works in either case
alt_json a;
nlohmann::json(42).get_to(a);
CHECK(a == 42);
}
SECTION("strict enum")
{
// regression test for #5667: NLOHMANN_JSON_SERIALIZE_ENUM_STRICT's from_json
+39
View File
@@ -594,6 +594,45 @@ TEST_CASE("parser class")
// parsing from a string literal is unaffected either way
CHECK(json::parse("123") == json(123));
// a NUL byte that ends a // comment ends the input just
// like a NUL byte anywhere else (issue #5659); before the
// fix, the NUL was consumed as part of the comment, and
// scanning continued with whatever followed it
{
// same as "//c" alone (real end of input after the
// comment), rather than continuing with "[1]"
std::string s1 = "//c";
s1.push_back('\0');
s1 += "[1]";
json _; // NOLINT(readability-identifier-naming)
CHECK_THROWS_WITH_AS(_ = json::parse(s1, nullptr, true, true),
"[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal",
json::parse_error&);
CHECK_FALSE(json::accept(s1, true, true));
}
{
// same as "[1, //c" alone, rather than continuing with " 2]"
std::string s2 = "[1, //c";
s2.push_back('\0');
s2 += " 2]";
json _; // NOLINT(readability-identifier-naming)
CHECK_THROWS_WITH_AS(_ = json::parse(s2, nullptr, true, true),
"[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal",
json::parse_error&);
CHECK_FALSE(json::accept(s2, true, true));
}
{
// same as "1 //c" alone: the comment (and the NUL that
// ends it) is ignored, and "x" is never reached
std::string s3 = "1 //c";
s3.push_back('\0');
s3 += "x";
CHECK(json::parse(s3, nullptr, true, true) == json(1));
CHECK(json::accept(s3, true, true));
}
}
#endif
+151
View File
@@ -750,6 +750,28 @@ TEST_CASE("regression #3868 - heterogeneous comparisons compile under C++20 (P24
CHECK_FALSE(j != i);
}
}
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
TEST_CASE("regression #5665 - scalar <= discarded and scalar >= discarded in C++20 legacy mode")
{
// Issue #5665: with a scalar on the left-hand side, <= and >= only had the
// candidate rewritten from operator<=>, which does not emulate the legacy
// discarded-value behavior. Check that scalar-on-the-left now matches the
// other three operand orders.
const json discarded(json::value_t::discarded);
const json one = 1;
CHECK(discarded <= 1);
CHECK(discarded >= 1);
CHECK(one <= discarded);
CHECK(one >= discarded);
CHECK(1 <= discarded);
CHECK(1 >= discarded);
CHECK(1.5 <= discarded);
CHECK(1.5 >= discarded);
}
#endif
#endif
namespace
@@ -827,6 +849,46 @@ Json nest(Json j, const std::size_t depth)
return j;
}
// a std::map comparator with state: case-insensitive, unless constructed
// case-sensitive. Used to check that copying an object copies the original's
// comparator rather than default-constructing a new one (see #5649).
struct key_case_less
{
key_case_less() = default;
explicit key_case_less(const bool cs) noexcept : case_sensitive(cs) {}
bool operator()(const std::string& a, const std::string& b) const
{
if (case_sensitive)
{
return a < b;
}
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),
[](unsigned char x, unsigned char y)
{
return std::tolower(x) < std::tolower(y);
});
}
bool case_sensitive = false;
};
template<class Key, class Value, class /*Compare*/, class Allocator>
using key_case_map = std::map<Key, Value, key_case_less, Allocator>;
using key_case_json = nlohmann::basic_json<key_case_map>;
// the innermost value of a chain of single-element arrays
template<typename Json>
const Json& innermost(const Json& j)
{
const Json* p = &j;
while (p->is_array())
{
p = &(*p)[0];
}
return *p;
}
// orders keys case-insensitively, so "key" and "KEY" compare equivalent
// (neither less than the other) although they are not equal
struct case_insensitive_less
@@ -891,6 +953,47 @@ TEST_CASE("equality of objects whose entries have no fixed order")
}
}
TEST_CASE("copying an object preserves its comparator's state")
{
// Past the iterative deep copy's nesting bound, an object copy used to be
// built with a default-constructed comparator instead of a copy of the
// original's. For an object type whose comparator carries state - here, a
// std::map that compares keys case-sensitively only when created that way
// - this reordered the copy's keys and could even drop entries that the
// original's comparator kept distinct (see #5649).
key_case_json object = key_case_json::object_t(key_case_less(true)); // case-sensitive
object["b"] = 1;
object["B"] = 2;
object["a"] = 3;
REQUIRE(object.dump() == R"({"B":2,"a":3,"b":1})");
for (const std::size_t depth : std::vector<std::size_t> {0, 127, 128, 200})
{
CAPTURE(depth);
key_case_json original = object;
for (std::size_t i = 0; i < depth; ++i)
{
original = key_case_json::array({std::move(original)});
}
{
const key_case_json copy = original; // NOLINT(performance-unnecessary-copy-initialization)
CHECK(innermost(copy).size() == 3);
CHECK(innermost(copy).dump() == R"({"B":2,"a":3,"b":1})");
CHECK(copy == original);
}
{
key_case_json copy = key_case_json::array();
copy = original;
CHECK(innermost(copy).size() == 3);
CHECK(innermost(copy).dump() == R"({"B":2,"a":3,"b":1})");
CHECK(copy == original);
}
}
}
TEST_CASE("equality of an object whose comparator treats different keys as equivalent")
{
// https://github.com/nlohmann/json/issues/5655: past the nesting bound,
@@ -998,3 +1101,51 @@ TEST_CASE("containers are compared element by element")
}
}
}
#if JSON_HAS_THREE_WAY_COMPARISON
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
TEST_CASE("operator<=> of binary values with a different subtype does not depend on nesting depth")
{
// #5654: std::vector<std::uint8_t>::operator<=>, which the binary type's
// own operator<=> uses, ignores the subtype that operator== checks. So a
// pair of binary values with the same bytes but a different subtype is
// unequal, yet <=>-equivalent - the same inconsistency between == and <=>
// that a NaN has. Within the nesting bound, an array compares itself
// with std::vector's own operator<=>, which treats an equivalent pair as
// undecided and lets the next element decide, same as
// std::lexicographical_compare_three_way does. Past the bound,
// compare_iteratively<true>() takes over and must classify the pair the
// same way, or the result of operator<=> - and of <, which C++20 derives
// from it - depends on how deeply the values are nested.
const json a = json::array({json::binary({1}, 1), 1});
const json b = json::array({json::binary({1}, 2), 2});
// the root inconsistency: unequal, yet <=>-equivalent
CHECK_FALSE(a[0] == b[0]);
CHECK((a[0] <=> b[0]) == std::partial_ordering::equivalent); // *NOPAD*
const auto deep = [](const json & j, const std::size_t depth)
{
json result = j;
for (std::size_t i = 0; i < depth; ++i)
{
result = json::array({std::move(result)});
}
return result;
};
// 127 levels stay within nesting_depth_limit() (128); 128 and 200 do not,
// and must still agree with the levels that do
for (const std::size_t depth : std::vector<std::size_t> {0, 127, 128, 200})
{
CAPTURE(depth);
const json x = deep(a, depth);
const json y = deep(b, depth);
CHECK((x <=> y) == std::partial_ordering::less); // *NOPAD*
CHECK((y <=> x) == std::partial_ordering::greater); // *NOPAD*
CHECK(x < y);
CHECK(y > x);
CHECK_FALSE(y < x);
}
}
#endif
+21
View File
@@ -1764,6 +1764,12 @@ TEST_CASE("Strict JSON to enum mapping")
CHECK(json("herz").get<strict_cards>() == strict_cards::herz);
CHECK(json("karo").get<strict_cards>() == strict_cards::karo);
// comparison of enum and json
CHECK(strict_cards::kreuz == json("kreuz"));
CHECK(strict_cards::pik == json("pik"));
CHECK(strict_cards::herz == json("herz"));
CHECK(strict_cards::karo == json("karo"));
// invalid json -> exception thrown
json _;
CHECK_THROWS_WITH_AS(_ = json("what?").get<strict_cards>(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"what?\"", json::out_of_range&);
@@ -1771,6 +1777,12 @@ TEST_CASE("Strict JSON to enum mapping")
// conversion of unmapped enum -> exception thrown
CHECK_THROWS_WITH_AS(json(strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&);
// comparing an unmapped enum with json throws the same exception
// (the scalar comparison operators used to be noexcept, so this
// called std::terminate)
CHECK_THROWS_WITH_AS(static_cast<void>(strict_cards::andere == json("andere")), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&);
CHECK_THROWS_WITH_AS(static_cast<void>(json("andere") != strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&);
// invalid UTF-8 -> out_of_range.410, not the type_error.316 thrown while building the
// message (regression test for #5667); such strings can reach get<Enum>() unvalidated,
// e.g. from from_cbor()/from_msgpack() (#5529)
@@ -1792,12 +1804,21 @@ TEST_CASE("Strict JSON to enum mapping")
CHECK(json("completed").get<StrictTaskState>() == STRICT_TS_COMPLETED);
CHECK(json().get<StrictTaskState>() == STRICT_TS_INVALID);
// comparison of enum and json
CHECK(STRICT_TS_STOPPED == json("stopped"));
CHECK(STRICT_TS_RUNNING == json("running"));
CHECK(STRICT_TS_COMPLETED == json("completed"));
CHECK(STRICT_TS_INVALID == json());
// invalid json -> exception thrown
json _;
CHECK_THROWS_WITH_AS(_ = json("what?").get<StrictTaskState>(), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState: \"what?\"", json::out_of_range&);
// conversion of unmapped enum -> exception thrown
CHECK_THROWS_WITH_AS(json(STRICT_TS_OTHER), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&);
// comparing an unmapped enum with json throws the same exception
CHECK_THROWS_WITH_AS(static_cast<void>(STRICT_TS_OTHER < json("x")), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&);
}
}
+70
View File
@@ -405,3 +405,73 @@ TEST_CASE("JSON Visit Node")
);
CHECK(expected.empty());
}
// A custom base class with a const member: copy-constructible (initializing a
// const member works fine), but not copy-/move-assignable (assigning one does
// not). Used to check that copy construction never requires more than that.
struct const_member_base
{
const int id = 7; // NOLINT(misc-non-private-member-variables-in-classes)
};
using json_with_const_base = nlohmann::basic_json <
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
const_member_base
>;
// build an array nested @a depth levels deep, with the innermost value 1;
// every level is constructed (never assigned), since const_member_base does
// not support assignment
static json_with_const_base make_nested_array(std::size_t depth)
{
if (depth == 0)
{
return json_with_const_base(1);
}
return json_with_const_base::array({make_nested_array(depth - 1)});
}
TEST_CASE("Regression test for issue #5674 - copy construction must not require an assignable base class")
{
SECTION("depth 0")
{
// as in the original bug report: copy construction only, no assignment
const json_with_const_base j = {1, 2};
const json_with_const_base copy = j; // NOLINT(performance-unnecessary-copy-initialization)
CHECK(copy.size() == 2);
CHECK(copy.id == 7);
}
SECTION("nested deeper than the copy constructor's descent bound")
{
// beyond nesting_depth_limit() (128) levels, the copy constructor
// copies without the call stack (copy_iteratively / copy_array_level),
// which used to assign the base class of every element it created
const std::size_t depth = 300;
const json_with_const_base j = make_nested_array(depth);
const json_with_const_base copy = j; // NOLINT(performance-unnecessary-copy-initialization)
const json_with_const_base* c = &copy;
for (std::size_t level = 0; level <= depth; ++level)
{
CAPTURE(level)
REQUIRE(c->id == 7);
if (level < depth)
{
c = &c->at(0);
}
}
CHECK(*c == 1);
}
}
+15
View File
@@ -47,6 +47,21 @@ TEST_CASE("Tests with disabled exceptions")
delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory)
}
SECTION("issue #5672 - value(json_pointer, default) must not abort for array tokens that are not a valid index")
{
const json j = {1, 2, 3};
// a syntactically valid index that is out of range for this array
CHECK(j.value("/7"_json_pointer, 42) == 42);
// a reference token that is not a number at all
CHECK(j.value("/1a"_json_pointer, 42) == 42);
// the empty reference token (JSON pointer "/")
CHECK(j.value("/"_json_pointer, 42) == 42);
// an index whose magnitude does not fit into size_type
CHECK(j.value("/99999999999999999999999"_json_pointer, 42) == 42);
CHECK(j.value("/18446744073709551615"_json_pointer, 42) == 42);
}
SECTION("growing an ordered_json object")
{
auto j = nlohmann::ordered_json::object();
+126
View File
@@ -516,6 +516,21 @@ TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_j
CHECK(j_array.value("/-"_json_pointer, 42) == 42);
CHECK(j_array_const.value("/-"_json_pointer, 42) == 42);
// Test an index with a non-digit after a valid leading digit; this is
// out_of_range (not parse_error) and must not throw (see #5672)
CHECK(j_array.value("/1a"_json_pointer, 42) == 42);
CHECK(j_array_const.value("/1a"_json_pointer, 42) == 42);
// Test the empty reference token (JSON pointer "/"); see #5672
CHECK(j_array.value("/"_json_pointer, 42) == 42);
CHECK(j_array_const.value("/"_json_pointer, 42) == 42);
// Test an index whose magnitude does not fit into size_type (see #5672)
CHECK(j_array.value("/99999999999999999999999"_json_pointer, 42) == 42);
CHECK(j_array_const.value("/99999999999999999999999"_json_pointer, 42) == 42);
CHECK(j_array.value("/18446744073709551615"_json_pointer, 42) == 42);
CHECK(j_array_const.value("/18446744073709551615"_json_pointer, 42) == 42);
#if !defined(JSON_NOEXCEPTION)
// Test malformed index (non-numeric) throws parse_error
CHECK_THROWS_WITH_AS(j_array.value("/foo"_json_pointer, 1), "[json.exception.parse_error.109] parse error: array index 'foo' is not a number", typename Json::parse_error&);
@@ -1973,4 +1988,115 @@ TEST_CASE("operator[] with user-defined std::string_view-convertible types")
}
}
}
TEST_CASE("keys convertible to std::string_view work with all lookup functions (regression test for #5663)")
{
// a key type convertible only to std::string_view: the case #4958 added
// support for, but only the non-const operator[] compiled with it
struct ViewKey
{
operator std::string_view() const
{
return "a";
}
};
// a key type convertible to both std::string and std::string_view: with
// 3.12.0, such a key worked with at, the const operator[], find, count and
// contains via the conversion to std::string; #4958 made the KeyType&&
// templates win overload resolution for it instead, and those then failed
struct DualKey
{
operator std::string() const
{
return "a";
}
operator std::string_view() const
{
return "a";
}
};
SECTION("nlohmann::json")
{
using json = nlohmann::json;
SECTION("ViewKey")
{
json j = {{"a", 1}};
const json& cj = j;
CHECK(j[ViewKey{}] == 1);
CHECK(cj[ViewKey{}] == 1);
CHECK(j.at(ViewKey{}) == 1);
CHECK(cj.at(ViewKey{}) == 1);
CHECK(j.find(ViewKey{}) != j.end());
CHECK(cj.find(ViewKey{}) != cj.end());
CHECK(j.count(ViewKey{}) == 1);
CHECK(j.contains(ViewKey{}));
CHECK(j.value(ViewKey{}, 0) == 1);
CHECK(j.erase(ViewKey{}) == 1);
CHECK(!j.contains("a"));
}
SECTION("DualKey")
{
json j = {{"a", 1}};
const json& cj = j;
CHECK(j[DualKey{}] == 1);
CHECK(cj[DualKey{}] == 1);
CHECK(j.at(DualKey{}) == 1);
CHECK(cj.at(DualKey{}) == 1);
CHECK(j.find(DualKey{}) != j.end());
CHECK(cj.find(DualKey{}) != cj.end());
CHECK(j.count(DualKey{}) == 1);
CHECK(j.contains(DualKey{}));
CHECK(j.value(DualKey{}, 0) == 1);
CHECK(j.erase(DualKey{}) == 1);
CHECK(!j.contains("a"));
}
}
SECTION("nlohmann::ordered_json")
{
using ordered_json = nlohmann::ordered_json;
SECTION("ViewKey")
{
ordered_json j = {{"a", 1}};
const ordered_json& cj = j;
CHECK(j[ViewKey{}] == 1);
CHECK(cj[ViewKey{}] == 1);
CHECK(j.at(ViewKey{}) == 1);
CHECK(cj.at(ViewKey{}) == 1);
CHECK(j.find(ViewKey{}) != j.end());
CHECK(cj.find(ViewKey{}) != cj.end());
CHECK(j.count(ViewKey{}) == 1);
CHECK(j.contains(ViewKey{}));
CHECK(j.value(ViewKey{}, 0) == 1);
CHECK(j.erase(ViewKey{}) == 1);
CHECK(!j.contains("a"));
}
SECTION("DualKey")
{
ordered_json j = {{"a", 1}};
const ordered_json& cj = j;
CHECK(j[DualKey{}] == 1);
CHECK(cj[DualKey{}] == 1);
CHECK(j.at(DualKey{}) == 1);
CHECK(cj.at(DualKey{}) == 1);
CHECK(j.find(DualKey{}) != j.end());
CHECK(cj.find(DualKey{}) != cj.end());
CHECK(j.count(DualKey{}) == 1);
CHECK(j.contains(DualKey{}));
CHECK(j.value(DualKey{}, 0) == 1);
CHECK(j.erase(DualKey{}) == 1);
CHECK(!j.contains("a"));
}
}
}
#endif
+9
View File
@@ -54,6 +54,15 @@ static_assert(noexcept(json(pod {})), "");
static_assert(noexcept(std::declval<json>().get<pod>()), "");
static_assert(!noexcept(std::declval<json>().get<pod_bis>()), "");
static_assert(noexcept(json(pod{})), "");
// comparing with a scalar is noexcept exactly when converting the scalar is
static_assert(noexcept(std::declval<const json&>() == 1), "");
static_assert(noexcept(1 != std::declval<const json&>()), "");
static_assert(noexcept(std::declval<const json&>() < 2.5), "");
static_assert(noexcept(nullptr == std::declval<const json&>()), "");
static_assert(!noexcept(std::declval<const json&>() == "foo"), "");
static_assert(!noexcept("foo" >= std::declval<const json&>()), "");
static_assert(noexcept(std::declval<const json&>() == std::declval<const json&>()), "");
} // namespace
TEST_CASE("noexcept")
+41
View File
@@ -196,3 +196,44 @@ TEST_CASE("regression test - diff() must account for ordered_json member order")
CHECK(a.patch(p) == b);
}
}
TEST_CASE("regression test for issue #5673 - ordered_json::emplace with a non-rvalue value")
{
SECTION("lvalue value")
{
ordered_json oj = ordered_json::object();
ordered_json value = 1;
auto res = oj.emplace("a", value);
CHECK(res.second == true);
CHECK(oj.dump() == "{\"a\":1}");
}
SECTION("const lvalue value")
{
ordered_json oj = ordered_json::object();
const ordered_json value = 1;
auto res = oj.emplace("a", value);
CHECK(res.second == true);
CHECK(oj.dump() == "{\"a\":1}");
}
SECTION("rvalue value")
{
ordered_json oj = ordered_json::object();
auto res = oj.emplace("a", ordered_json(1));
CHECK(res.second == true);
CHECK(oj.dump() == "{\"a\":1}");
}
SECTION("existing key is not overwritten (std::map-compatible semantics)")
{
ordered_json oj = ordered_json::object();
ordered_json value = 1;
oj.emplace("a", value);
ordered_json other_value = 2;
auto res = oj.emplace("a", other_value);
CHECK(res.second == false);
CHECK(oj.dump() == "{\"a\":1}");
}
}
+73
View File
@@ -403,6 +403,79 @@ TEST_CASE("ordered_map")
CHECK(om.size() == 4);
}
}
SECTION("emplace")
{
// regression test for issue #5673: the mapped-value parameter must
// accept lvalues and const lvalues, not just rvalues
ordered_map<std::string, std::string> om;
om["eins"] = "one";
om["zwei"] = "two";
om["drei"] = "three";
SECTION("with T&& (rvalue)")
{
auto res1 = om.emplace("eins", std::string("1"));
CHECK(res1.first == om.begin());
CHECK(res1.second == false);
CHECK(om.size() == 3);
CHECK(om.at("eins") == "one"); // existing key is not overwritten
auto res4 = om.emplace("vier", std::string("four"));
CHECK(res4.first == om.begin() + 3);
CHECK(res4.second == true);
CHECK(om.size() == 4);
CHECK(om.at("vier") == "four");
}
SECTION("with T& (lvalue)")
{
std::string one = "1";
std::string four = "four";
auto res1 = om.emplace("eins", one);
CHECK(res1.first == om.begin());
CHECK(res1.second == false);
CHECK(om.size() == 3);
CHECK(om.at("eins") == "one"); // existing key is not overwritten
auto res4 = om.emplace("vier", four);
CHECK(res4.first == om.begin() + 3);
CHECK(res4.second == true);
CHECK(om.size() == 4);
CHECK(om.at("vier") == "four");
CHECK(four == "four"); // source was copied, not moved from
}
SECTION("with const T&")
{
const std::string one = "1";
const std::string four = "four";
auto res1 = om.emplace("eins", one);
CHECK(res1.first == om.begin());
CHECK(res1.second == false);
CHECK(om.size() == 3);
auto res4 = om.emplace("vier", four);
CHECK(res4.first == om.begin() + 3);
CHECK(res4.second == true);
CHECK(om.size() == 4);
CHECK(om.at("vier") == "four");
}
SECTION("with key of key_type (non-template overload)")
{
const std::string key_vier{"vier"};
std::string four = "four";
auto res4 = om.emplace(key_vier, four);
CHECK(res4.first == om.begin() + 3);
CHECK(res4.second == true);
CHECK(om.size() == 4);
CHECK(om.at("vier") == "four");
}
}
}
TEST_CASE("ordered_map growth")
+54
View File
@@ -233,6 +233,52 @@ class my_allocator : public std::allocator<T>
};
};
/////////////////////////////////////////////////////////////////////
// for #3669
/////////////////////////////////////////////////////////////////////
// mimics boost::optional's converting constructor, whose SFINAE check asks
// whether T is constructible from const U&
template<class T, class Arg>
struct issue3669_is_constructible
{
template<class T2, class A2, class = decltype(T2(std::declval<A2>()))>
static char test(int);
template<class, class>
static long test(...);
static constexpr bool value = sizeof(test<T, Arg>(0)) == 1;
};
template<class T>
class issue3669_optional
{
public:
issue3669_optional() = default;
template<class U>
issue3669_optional(const issue3669_optional<U>& /*unused*/, // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
typename std::enable_if<issue3669_is_constructible<T, const U&>::value, bool>::type /*unused*/ = true) {}
};
class Issue3669Dummy
{
public:
explicit Issue3669Dummy(const json& /*unused*/) {}
};
class Issue3669Holder
{
issue3669_optional<Issue3669Dummy> d{};
// GCC < 11 (C++11/14) rejects a free to_json(json&, const Issue3669Holder&)
// here, because ADL for Issue3669Dummy finds it and closes an instantiation
// cycle; a hidden friend is only visible to ADL for Issue3669Holder
friend void to_json(json& j, const Issue3669Holder& h)
{
static_cast<void>(h.d); // silence -Wunused-private-field
j = "holder";
}
};
TEST_CASE("regression tests 2")
{
SECTION("issue #1001 - Fix memory leak during parser callback")
@@ -762,6 +808,14 @@ TEST_CASE("regression tests 2")
CHECK(j == k);
}
SECTION("issue #3669 - invalid use of incomplete type with optional member and to_json")
{
const Issue3669Holder h{};
const Issue3669Holder h2(h); // NOLINT(performance-unnecessary-copy-initialization)
const json j = h2;
CHECK(j == "holder");
}
}
TEST_CASE("regression test - parser callback must not lose a duplicate key's prior value")