Fix C++20/gcc-12 issues (Part 2) (#3446)

* Add C++20 3-way comparison operator and fix broken comparisons

Fixes #3207.
Fixes #3409.

* Fix iterators to meet (more) std::ranges requirements

Fixes #3130.
Related discussion: #3408

* Add note about CMake standard version selection to unit tests

Document how CMake chooses which C++ standard version to use when
building tests.

* Update documentation

* CI: add legacy discarded value comparison

* Fix internal linkage errors when building a module
This commit is contained in:
Florian Albrechtskirchinger 2022-05-29 13:08:06 +02:00 committed by GitHub
parent ede6667858
commit 6b97599a27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1963 additions and 690 deletions

View File

@ -48,7 +48,7 @@ jobs:
container: ghcr.io/nlohmann/json-ci:v2.3.0
strategy:
matrix:
target: [ci_test_diagnostics, ci_test_noexceptions, ci_test_noimplicitconversions]
target: [ci_test_diagnostics, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison]
steps:
- uses: actions/checkout@v3
- name: cmake

View File

@ -37,13 +37,14 @@ if(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION}
else()
set(JSON_BuildTests_INIT OFF)
endif()
option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${JSON_BuildTests_INIT})
option(JSON_CI "Enable CI build targets." OFF)
option(JSON_Diagnostics "Use extended diagnostic messages." OFF)
option(JSON_ImplicitConversions "Enable implicit conversions." ON)
option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT})
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF)
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${JSON_BuildTests_INIT})
option(JSON_CI "Enable CI build targets." OFF)
option(JSON_Diagnostics "Use extended diagnostic messages." OFF)
option(JSON_ImplicitConversions "Enable implicit conversions." ON)
option(JSON_LegacyDiscardedValueComparison "Enable legacy discarded value comparison." OFF)
option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT})
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF)
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
if (JSON_CI)
include(ci)
@ -77,6 +78,10 @@ if (NOT JSON_ImplicitConversions)
message(STATUS "Implicit conversions are disabled")
endif()
if (JSON_LegacyDiscardedValueComparison)
message(STATUS "Legacy discarded value comparison enabled")
endif()
if (JSON_Diagnostics)
message(STATUS "Diagnostics enabled")
endif()
@ -100,8 +105,9 @@ endif()
target_compile_definitions(
${NLOHMANN_JSON_TARGET_NAME}
INTERFACE
JSON_USE_IMPLICIT_CONVERSIONS=$<BOOL:${JSON_ImplicitConversions}>
JSON_DIAGNOSTICS=$<BOOL:${JSON_Diagnostics}>
$<$<NOT:$<BOOL:${JSON_ImplicitConversions}>>:JSON_USE_IMPLICIT_CONVERSIONS=0>
$<$<BOOL:${JSON_Diagnostics}>:JSON_DIAGNOSTICS=1>
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
)
target_include_directories(

View File

@ -498,6 +498,20 @@ add_custom_target(ci_test_diagnostics
COMMENT "Compile and test with improved diagnostics enabled"
)
###############################################################################
# Enable legacy discarded value comparison.
###############################################################################
add_custom_target(ci_test_legacycomparison
COMMAND CXX=${CLANG_TOOL} ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja
-DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON -DJSON_LegacyDiscardedValueComparison=ON
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_legacycomparison
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_legacycomparison
COMMAND cd ${PROJECT_BINARY_DIR}/build_legacycomparison && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
COMMENT "Compile and test with legacy discarded value comparison enabled"
)
###############################################################################
# Coverage.
###############################################################################
@ -797,8 +811,9 @@ endfunction()
ci_get_cmake(3.1.0 CMAKE_3_1_0_BINARY)
ci_get_cmake(3.13.0 CMAKE_3_13_0_BINARY)
set(JSON_CMAKE_FLAGS_3_1_0 "JSON_Install;JSON_MultipleHeaders;JSON_ImplicitConversions;JSON_Valgrind;JSON_Diagnostics;JSON_SystemInclude")
set(JSON_CMAKE_FLAGS_3_13_0 "JSON_BuildTests")
set(JSON_CMAKE_FLAGS_3_1_0 JSON_Diagnostics JSON_ImplicitConversions JSON_LegacyDiscardedValueComparison
JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind)
set(JSON_CMAKE_FLAGS_3_13_0 JSON_BuildTests)
function(ci_add_cmake_flags_targets flag min_version)
string(TOLOWER "ci_cmake_flag_${flag}" flag_target)

View File

@ -156,6 +156,7 @@ endfunction()
#############################################################################
# json_test_add_test_for(
# <file>
# [NAME <name>]
# MAIN <main>
# [CXX_STANDARDS <version_number>...] [FORCE])
#
@ -165,6 +166,7 @@ endfunction()
#
# if C++ standard <version_number> is supported by the compiler and the
# source file contains JSON_HAS_CPP_<version_number>.
# Use NAME <name> to override the filename-derived test name.
# Use FORCE to create the test regardless of the file containing
# JSON_HAS_CPP_<version_number>.
# Test targets are linked against <main>.
@ -172,15 +174,22 @@ endfunction()
#############################################################################
function(json_test_add_test_for file)
cmake_parse_arguments(args "FORCE" "MAIN" "CXX_STANDARDS" ${ARGN})
get_filename_component(file_basename ${file} NAME_WE)
string(REGEX REPLACE "unit-([^$]+)" "test-\\1" test_name ${file_basename})
cmake_parse_arguments(args "FORCE" "MAIN;NAME" "CXX_STANDARDS" ${ARGN})
if("${args_MAIN}" STREQUAL "")
message(FATAL_ERROR "Required argument MAIN <main> missing.")
endif()
if("${args_NAME}" STREQUAL "")
get_filename_component(file_basename ${file} NAME_WE)
string(REGEX REPLACE "unit-([^$]+)" "test-\\1" test_name ${file_basename})
else()
set(test_name ${args_NAME})
if(NOT test_name MATCHES "test-[^$]+")
message(FATAL_ERROR "Test name must start with 'test-'.")
endif()
endif()
if("${args_CXX_STANDARDS}" STREQUAL "")
set(args_CXX_STANDARDS 11)
endif()

View File

@ -233,9 +233,10 @@ Access to the JSON value
- [**operator==**](operator_eq.md) - comparison: equal
- [**operator!=**](operator_ne.md) - comparison: not equal
- [**operator<**](operator_lt.md) - comparison: less than
- [**operator<=**](operator_le.md) - comparison: less than or equal
- [**operator>**](operator_gt.md) - comparison: greater than
- [**operator<=**](operator_le.md) - comparison: less than or equal
- [**operator>=**](operator_ge.md) - comparison: greater than or equal
- [**operator<=>**](operator_spaceship.md) - comparison: 3-way
### Serialization / Dumping

View File

@ -1,21 +1,31 @@
# <small>nlohmann::basic_json::</small>operator==
```cpp
bool operator==(const_reference lhs, const_reference rhs) noexcept;
// until C++20
bool operator==(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator==(const_reference lhs, const ScalarType rhs) noexcept;
bool operator==(const_reference lhs, const ScalarType rhs) noexcept; // (2)
template<typename ScalarType>
bool operator==(ScalarType lhs, const const_reference rhs) noexcept;
bool operator==(ScalarType lhs, const const_reference rhs) noexcept; // (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)
};
```
Compares two JSON values for equality according to the following rules:
1. Compares two JSON values for equality according to the following rules:
- Two JSON values are equal if (1) neither value is discarded, or (2) they are of the same
type and their stored values are the same according to their respective `operator==`.
- Integer and floating-point numbers are automatically converted before comparison.
- Two JSON values are equal if (1) they are not discarded, (2) they are from the same type, and (3) their stored values
are the same according to their respective `operator==`.
- Integer and floating-point numbers are automatically converted before comparison. Note that two NaN values are always
treated as unequal.
2. Compares a JSON value and a scalar or a scalar and a JSON value for equality by converting the
scalar to a JSON value and comparing both JSON values according to 1.
## Template parameters
@ -32,7 +42,7 @@ Compares two JSON values for equality according to the following rules:
## Return value
whether the values `lhs` and `rhs` are equal
whether the values `lhs`/`*this` and `rhs` are equal
## Exception safety
@ -46,7 +56,11 @@ Linear.
!!! note "Comparing special values"
- NaN values never compare equal to themselves or to other NaN values.
- `NaN` values are unordered within the domain of numbers.
The following comparisons all yield `#!cpp false`:
1. Comparing a `NaN` with itself.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
- JSON `#!cpp null` values are all equal.
- Discarded values never compare equal to themselves.
@ -117,4 +131,5 @@ Linear.
## Version history
- Added in version 1.0.0.
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.

View File

@ -1,17 +1,25 @@
# <small>nlohmann::basic_json::</small>operator>=
```cpp
bool operator>=(const_reference lhs, const_reference rhs) noexcept,
// until C++20
bool operator>=(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator>=(const_reference lhs, const ScalarType rhs) noexcept;
bool operator>=(const_reference lhs, const ScalarType rhs) noexcept; // (2)
template<typename ScalarType>
bool operator>=(ScalarType lhs, const const_reference rhs) noexcept;
bool operator>=(ScalarType lhs, const const_reference rhs) noexcept; // (2)
```
Compares whether one JSON value `lhs` is greater than or equal to another JSON value `rhs` by calculating
`#!cpp !(lhs < rhs)`.
1. Compares whether one JSON value `lhs` is greater than or equal to another JSON value `rhs`
according to the following rules:
- The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either
operand is `NaN` and the other operand is either `NaN` or any other number.
- Otherwise, returns the result of `#!cpp !(lhs < rhs)`.
2. Compares wether a JSON value is greater than or equal to a scalar or a scalar is greater than or
equal to a JSON value by converting the scalar to a JSON value and comparing both JSON values
according to 1.
## Template parameters
@ -38,6 +46,21 @@ No-throw guarantee: this function never throws exceptions.
Linear.
## Notes
!!! note "Comparing `NaN`"
`NaN` values are unordered within the domain of numbers.
The following comparisons all yield `#!cpp false`:
1. Comparing a `NaN` with itself.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
[`operator<=>`](operator_spaceship.md).
## Examples
??? example
@ -54,6 +77,11 @@ Linear.
--8<-- "examples/operator__greaterequal.output"
```
## See also
- [**operator<=>**](operator_spaceship.md) comparison: 3-way
## Version history
- Added in version 1.0.0.
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.

View File

@ -1,16 +1,24 @@
# <small>nlohmann::basic_json::</small>operator>
```cpp
bool operator>(const_reference lhs, const_reference rhs) noexcept,
// until C++20
bool operator>(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator>(const_reference lhs, const ScalarType rhs) noexcept;
bool operator>(const_reference lhs, const ScalarType rhs) noexcept; // (2)
template<typename ScalarType>
bool operator>(ScalarType lhs, const const_reference rhs) noexcept;
bool operator>(ScalarType lhs, const const_reference rhs) noexcept; // (2)
```
Compares whether one JSON value `lhs` is greater than another JSON value `rhs` by calculating `#!cpp !(lhs <= rhs)`.
1. Compares whether one JSON value `lhs` is greater than another JSON value `rhs` according to the
following rules:
- The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either
operand is `NaN` and the other operand is either `NaN` or any other number.
- Otherwise, returns the result of `#!cpp !(lhs <= rhs)`.
2. Compares wether a JSON value is greater than a scalar or a scalar is greater than a JSON value by
converting the scalar to a JSON value and comparing both JSON values according to 1.
## Template parameters
@ -37,6 +45,21 @@ No-throw guarantee: this function never throws exceptions.
Linear.
## Notes
!!! note "Comparing `NaN`"
`NaN` values are unordered within the domain of numbers.
The following comparisons all yield `#!cpp false`:
1. Comparing a `NaN` with itself.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
[`operator<=>`](operator_spaceship.md).
## Examples
??? example
@ -53,6 +76,11 @@ Linear.
--8<-- "examples/operator__greater.output"
```
## See also
- [**operator<=>**](operator_spaceship.md) comparison: 3-way
## Version history
- Added in version 1.0.0.
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.

View File

@ -1,17 +1,25 @@
# <small>nlohmann::basic_json::</small>operator<=
```cpp
bool operator<=(const_reference lhs, const_reference rhs) noexcept,
// until C++20
bool operator<=(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator<=(const_reference lhs, const ScalarType rhs) noexcept;
bool operator<=(const_reference lhs, const ScalarType rhs) noexcept; // (2)
template<typename ScalarType>
bool operator<=(ScalarType lhs, const const_reference rhs) noexcept;
bool operator<=(ScalarType lhs, const const_reference rhs) noexcept; // (2)
```
Compares whether one JSON value `lhs` is less than or equal to another JSON value `rhs` by calculating
`#cpp !(rhs < lhs)`.
1. Compares whether one JSON value `lhs` is less than or equal to another JSON value `rhs`
according to the following rules:
- The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either
operand is `NaN` and the other operand is either `NaN` or any other number.
- Otherwise, returns the result of `#!cpp !(rhs < lhs)`.
1. Compares wether a JSON value is less than or equal to a scalar or a scalar is less than or equal
to a JSON value by converting the scalar to a JSON value and comparing both JSON values according
to 1.
## Template parameters
@ -38,6 +46,21 @@ No-throw guarantee: this function never throws exceptions.
Linear.
## Notes
!!! note "Comparing `NaN`"
`NaN` values are unordered within the domain of numbers.
The following comparisons all yield `#!cpp false`:
1. Comparing a `NaN` with itself.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
[`operator<=>`](operator_spaceship.md).
## Examples
??? example
@ -54,6 +77,11 @@ Linear.
--8<-- "examples/operator__lessequal.output"
```
## See also
- [**operator<=>**](operator_spaceship.md) comparison: 3-way
## Version history
- Added in version 1.0.0.
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.

View File

@ -1,31 +1,34 @@
# <small>nlohmann::basic_json::</small>operator<
```cpp
bool operator<(const_reference lhs, const_reference rhs) noexcept;
// until C++20
bool operator<(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator<(const_reference lhs, const ScalarType rhs) noexcept;
bool operator<(const_reference lhs, const ScalarType rhs) noexcept; // (2)
template<typename ScalarType>
bool operator<(ScalarType lhs, const const_reference rhs) noexcept;
bool operator<(ScalarType lhs, const const_reference rhs) noexcept; // (2)
```
Compares whether one JSON value `lhs` is less than another JSON value `rhs` according to the following rules:
1. Compares whether one JSON value `lhs` is less than another JSON value `rhs` according to the
following rules:
- If either operand is discarded, the comparison yields `#!cpp false`.
- If both operands have the same type, the values are compared using their respective `operator<`.
- Integer and floating-point numbers are automatically converted before comparison.
- In case `lhs` and `rhs` have different types, the values are ignored and the order of the types
is considered, which is:
1. null
2. boolean
3. number (all types)
4. object
5. array
6. string
7. binary
For instance, any boolean value is considered less than any string.
- If `lhs` and `rhs` have the same type, the values are compared using the default `<` operator.
- Integer and floating-point numbers are automatically converted before comparison
- Discarded values a
- In case `lhs` and `rhs` have different types, the values are ignored and the order of the types is considered, which
is:
1. null
2. boolean
3. number (all types)
4. object
5. array
6. string
7. binary
For instance, any boolean value is considered less than any string.
2. Compares wether a JSON value is less than a scalar or a scalar is less than a JSON value by converting
the scalar to a JSON value and comparing both JSON values according to 1.
## Template parameters
@ -52,6 +55,21 @@ No-throw guarantee: this function never throws exceptions.
Linear.
## Notes
!!! note "Comparing `NaN`"
`NaN` values are unordered within the domain of numbers.
The following comparisons all yield `#!cpp false`:
1. Comparing a `NaN` with itself.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
[`operator<=>`](operator_spaceship.md).
## Examples
??? example
@ -68,6 +86,11 @@ Linear.
--8<-- "examples/operator__less.output"
```
## See also
- [**operator<=>**](operator_spaceship.md) comparison: 3-way
## Version history
- Added in version 1.0.0.
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.

View File

@ -1,16 +1,32 @@
# <small>nlohmann::basic_json::</small>operator!=
```cpp
bool operator!=(const_reference lhs, const_reference rhs) noexcept;
// until C++20
bool operator!=(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator!=(const_reference lhs, const ScalarType rhs) noexcept;
bool operator!=(const_reference lhs, const ScalarType rhs) noexcept; // (2)
template<typename ScalarType>
bool operator!=(ScalarType lhs, const const_reference rhs) noexcept;
bool operator!=(ScalarType lhs, const const_reference rhs) noexcept; // (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)
};
```
Compares two JSON values for inequality by calculating `#!cpp !(lhs == rhs)`.
1. Compares two JSON values for inequality according to the following rules:
- The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either
operand is `NaN` and the other operand is either `NaN` or any other number.
- Otherwise, returns the result of `#!cpp !(lhs == rhs)` (until C++20) or
`#!cpp !(*this == rhs)` (since C++20).
2. Compares a JSON value and a scalar or a scalar and a JSON value for inequality by converting the
scalar to a JSON value and comparing both JSON values according to 1.
## Template parameters
@ -27,7 +43,7 @@ Compares two JSON values for inequality by calculating `#!cpp !(lhs == rhs)`.
## Return value
whether the values `lhs` and `rhs` are not equal
whether the values `lhs`/`*this` and `rhs` are not equal
## Exception safety
@ -37,6 +53,16 @@ No-throw guarantee: this function never throws exceptions.
Linear.
## Notes
!!! note "Comparing `NaN`"
`NaN` values are unordered within the domain of numbers.
The following comparisons all yield `#!cpp false`:
1. Comparing a `NaN` with itself.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
## Examples
??? example
@ -69,4 +95,5 @@ Linear.
## Version history
- Added in version 1.0.0.
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.

View File

@ -0,0 +1,70 @@
# <small>nlohmann::basic_json::</small>operator<=>
```cpp
// since C++20
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)
};
```
1. 3-way compares two JSON values producing a result of type `std::partial_ordering` according to the following rules:
- Two JSON values compare with a result of `std::partial_ordering::unordered` if either value is discarded.
- If both JSON values are of the same type, the result is produced by 3-way comparing their stored values using their
respective `operator<=>`.
- Integer and floating-point numbers are converted to their common type and then 3-way compared using their respective
`operator<=>`.
For instance, comparing an integer and a floating-point value will 3-way compare the first value convertered to
floating-point with the second value.
- Otherwise, yields a result by comparing the type (see [`value_t`](value_t.md)).
2. 3-way compares a JSON value and a scalar or a scalar and a JSON value by converting the scalar to a JSON value and 3-way
comparing both JSON values (see 1).
## Template parameters
`ScalarType`
: a scalar type according to `std::is_scalar<ScalarType>::value`
## Parameters
`rhs` (in)
: second value to consider
## Return value
the `std::partial_ordering` of the 3-way comparison of `*this` and `rhs`
## Exception safety
No-throw guarantee: this function never throws exceptions.
## Complexity
Linear.
## Notes
!!! note "Comparing `NaN`"
- `NaN` values are unordered within the domain of numbers.
The following comparisons all yield `std::partial_ordering::unordered`:
1. Comparing a `NaN` with itself.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
## See also
- [**operator==**](operator_eq.md) - comparison: equal
- [**operator!=**](operator_ne.md) - comparison: not equal
- [**operator<**](operator_lt.md) - comparison: less than
- [**operator<=**](operator_le.md) - comparison: less than or equal
- [**operator>**](operator_gt.md) - comparison: greater than
- [**operator>=**](operator_ge.md) - comparison: greater than or equal
## Version history
1. Added in version 3.11.0.
2. Added in version 3.11.0.

View File

@ -24,10 +24,41 @@ functions [`is_null`](is_null.md), [`is_object`](is_object.md), [`is_array`](is_
## Notes
There are three enumeration entries (number_integer, number_unsigned, and number_float), because the library
distinguishes these three types for numbers: [`number_unsigned_t`](number_unsigned_t.md) is used for unsigned integers,
[`number_integer_t`](number_integer_t.md) is used for signed integers, and [`number_float_t`](number_float_t.md) is used
for floating-point numbers or to approximate integers which do not fit in the limits of their respective type.
!!! note "Ordering"
The order of types is as follows:
1. `null`
2. `boolean`
3. `number_integer`, `number_unsigned`, `number_float`
4. `object`
5. `array`
6. `string`
7. `binary`
`discarded` is unordered.
!!! note "Types of numbers"
There are three enumerators for numbers (`number_integer`, `number_unsigned`, and `number_float`) to distinguish
between different types of numbers:
- [`number_unsigned_t`](number_unsigned_t.md) for unsigned integers
- [`number_integer_t`](number_integer_t.md) for signed integers
- [`number_float_t`](number_float_t.md) for floating-point numbers or to approximate integers which do not fit
into the limits of their respective type
!!! warning "Comparison operators"
`operator<` and `operator<=>` (since C++20) are overloaded and compare according to the ordering described above.
Until C++20 all other relational and equality operators yield results according to the integer value of each
enumerator.
Since C++20 some compilers consider the _rewritten candidates_ generated from `operator<=>` during overload
resolution, while others do not.
For predictable and portable behavior use:
- `operator<` or `operator<=>` when wanting to compare according to the order described above
- `operator==` or `operator!=` when wanting to compare according to each enumerators integer value
## Examples

View File

@ -17,6 +17,8 @@ header. See also the [macro overview page](../../features/macros.md).
- [**JSON_HAS_CPP_11**<br>**JSON_HAS_CPP_14**<br>**JSON_HAS_CPP_17**<br>**JSON_HAS_CPP_20**](json_has_cpp_11.md) - set supported C++ standard
- [**JSON_HAS_FILESYSTEM**<br>**JSON_HAS_EXPERIMENTAL_FILESYSTEM**](json_has_filesystem.md) - control `std::filesystem` support
- [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support
- [**JSON_HAS_THREE_WAY_COMPARISON**](json_has_three_way_comparison.md) - control 3-way comparison support
- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
@ -29,6 +31,12 @@ header. See also the [macro overview page](../../features/macros.md).
- [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions
<!-- comment-->
## Comparison behavior
- [**JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON**](json_use_legacy_discarded_value_comparison.md) -
control comparison of discarded values
## Serialization/deserialization macros
- [**NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)**<br>**NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)**](nlohmann_define_type_intrusive.md) - serialization/deserialization of types _with_ access to private variables

View File

@ -0,0 +1,18 @@
# JSON_HAS_RANGES
```cpp
#define JSON_HAS_RANGES /* value */
```
This macro indicates whether the standard library has any support for ranges. Implies support for concepts.
Possible values are `1` when supported or `0` when unsupported.
## Default definition
The default value is detected based on the preprocessor macro `#!cpp __cpp_lib_ranges`.
When the macro is not defined, the library will define it to its default value.
## Version history
- Added in version 3.11.0.

View File

@ -0,0 +1,19 @@
# JSON_HAS_THREE_WAY_COMPARISON
```cpp
#define JSON_HAS_THREE_WAY_COMPARISON /* value */
```
This macro indicates whether the compiler and standard library support 3-way comparison.
Possible values are `1` when supported or `0` when unsupported.
## Default definition
The default value is detected based on the preprocessor macros `#!cpp __cpp_impl_three_way_comparison`
and `#!cpp __cpp_lib_three_way_comparison`.
When the macro is not defined, the library will define it to its default value.
## Version history
- Added in version 3.11.0.

View File

@ -0,0 +1,61 @@
# JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
```cpp
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON /* value */
```
This macro enables the (incorrect) legacy comparison behavior of discarded JSON values.
Possible values are `1` to enable or `0` to disable (default).
When enabled, comparisons involving at least one discarded JSON value yield results as follows:
| **Operator** | **Result** |
|--------------|---------------|
| `==` | `#!cpp false` |
| `!=` | `#!cpp true` |
| `<` | `#!cpp false` |
| `<=` | `#!cpp true` |
| `>=` | `#!cpp true` |
| `>` | `#!cpp false` |
Otherwise, comparisons involving at least one discarded JSON value always yield `#!cpp false`.
## Default definition
The default value is `0`.
```cpp
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
```
When the macro is not defined, the library will define it to its default value.
## Notes
!!! warning "Inconsistent behavior in C++20 and beyond"
When targeting C++20 or above, enabling the legacy comparison behavior is _strongly_
discouraged.
- The 3-way comparison operator (`<=>`) will always give the correct result
(`#!cpp std::partial_ordering::unordered`) regardless of the value of
`JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`.
- Overloads for the equality and relational operators emulate the legacy behavior.
Code outside your control may use either 3-way comparison or the equality and
relational operators, resulting in inconsistent and unpredictable behavior.
See [`operator<=>`](../basic_json/operator_spaceship.md) for more information on 3-way
comparison.
!!! warning "Deprecation"
The legacy comparison behavior is deprecated and may be removed in a future major
version release.
New code should not depend on it and existing code should try to remove or rewrite
expressions relying on it.
## Version history
- Added in version 3.11.0.

View File

@ -0,0 +1,4 @@
/* disable ligatures in code and preformatted blocks */
code, pre {
font-variant-ligatures: none;
}

View File

@ -154,15 +154,16 @@ nav:
- 'operator value_t': api/basic_json/operator_value_t.md
- 'operator[]': api/basic_json/operator[].md
- 'operator=': api/basic_json/operator=.md
- 'operator+=': api/basic_json/operator+=.md
- 'operator==': api/basic_json/operator_eq.md
- 'operator!=': api/basic_json/operator_ne.md
- 'operator<': api/basic_json/operator_lt.md
- 'operator<<': api/basic_json/operator_ltlt.md
- 'operator<=': api/basic_json/operator_le.md
- 'operator>': api/basic_json/operator_gt.md
- 'operator>>': api/basic_json/operator_gtgt.md
- 'operator<=': api/basic_json/operator_le.md
- 'operator>=': api/basic_json/operator_ge.md
- 'operator+=': api/basic_json/operator+=.md
- 'operator<=>': api/basic_json/operator_spaceship.md
- 'operator<<': api/basic_json/operator_ltlt.md
- 'operator>>': api/basic_json/operator_gtgt.md
- 'operator""_json': api/basic_json/operator_literal_json.md
- 'operator""_json_pointer': api/basic_json/operator_literal_json_pointer.md
- 'out_of_range': api/basic_json/out_of_range.md
@ -246,6 +247,8 @@ nav:
- 'JSON_HAS_CPP_20': api/macros/json_has_cpp_11.md
- 'JSON_HAS_EXPERIMENTAL_FILESYSTEM': api/macros/json_has_filesystem.md
- 'JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md
- 'JSON_HAS_RANGES': api/macros/json_has_ranges.md
- 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
- 'JSON_NO_IO': api/macros/json_no_io.md
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
@ -253,6 +256,7 @@ nav:
- 'JSON_THROW_USER': api/macros/json_throw_user.md
- 'JSON_TRY_USER': api/macros/json_throw_user.md
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
- 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md
- 'NLOHMANN_DEFINE_TYPE_INTRUSIVE': api/macros/nlohmann_define_type_intrusive.md
- 'NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT': api/macros/nlohmann_define_type_intrusive.md
- 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE': api/macros/nlohmann_define_type_non_intrusive.md
@ -319,5 +323,8 @@ plugins:
minify_html: true
- git-revision-date-localized
extra_css:
- css/custom.css
extra_javascript:
- https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML

View File

@ -39,7 +39,7 @@ namespace nlohmann
namespace detail
{
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{
@ -86,7 +86,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{
@ -96,7 +96,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
@ -111,7 +111,7 @@ template <
std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
&& !std::is_same<typename BasicJsonType::string_t, StringType>::value
&& !is_json_ref<StringType>::value, int > = 0 >
void from_json(const BasicJsonType& j, StringType& s)
inline void from_json(const BasicJsonType& j, StringType& s)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
@ -122,26 +122,26 @@ void from_json(const BasicJsonType& j, StringType& s)
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType, typename EnumType,
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
void from_json(const BasicJsonType& j, EnumType& e)
inline void from_json(const BasicJsonType& j, EnumType& e)
{
typename std::underlying_type<EnumType>::type val;
get_arithmetic_value(j, val);
@ -151,7 +151,7 @@ void from_json(const BasicJsonType& j, EnumType& e)
// forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
@ -168,7 +168,7 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
// valarray doesn't have an insert method
template<typename BasicJsonType, typename T,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::valarray<T>& l)
inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
@ -193,7 +193,7 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines
}
template<typename BasicJsonType>
void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
{
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
}
@ -237,8 +237,8 @@ template<typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t<
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
int> = 0>
void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
priority_tag<0> /*unused*/)
inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
priority_tag<0> /*unused*/)
{
using std::end;
@ -295,7 +295,7 @@ auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
}
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{
@ -307,7 +307,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
template<typename BasicJsonType, typename ConstructibleObjectType,
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{
@ -339,7 +339,7 @@ template < typename BasicJsonType, typename ArithmeticType,
!std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
int > = 0 >
void from_json(const BasicJsonType& j, ArithmeticType& val)
inline void from_json(const BasicJsonType& j, ArithmeticType& val)
{
switch (static_cast<value_t>(j))
{
@ -389,7 +389,7 @@ std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair
}
template<typename BasicJsonType, typename A1, typename A2>
void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
{
p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
}
@ -401,7 +401,7 @@ std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tu
}
template<typename BasicJsonType, typename... Args>
void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
{
t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
}
@ -421,7 +421,7 @@ auto from_json(BasicJsonType&& j, TupleRelated&& t)
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
@ -441,7 +441,7 @@ void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>&
template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{
@ -460,7 +460,7 @@ void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyE
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
template<typename BasicJsonType>
void from_json(const BasicJsonType& j, std_fs::path& p)
inline void from_json(const BasicJsonType& j, std_fs::path& p)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
@ -482,11 +482,16 @@ struct from_json_fn
};
} // namespace detail
#ifndef JSON_HAS_CPP_17
/// namespace to hold default `from_json` function
/// to see why this is required:
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
{
constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; // NOLINT(misc-definitions-in-headers)
#endif
JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
detail::static_const<detail::from_json_fn>::value;
#ifndef JSON_HAS_CPP_17
} // namespace
#endif
} // namespace nlohmann

View File

@ -267,55 +267,55 @@ struct external_constructor<value_t::object>
template<typename BasicJsonType, typename T,
enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
void to_json(BasicJsonType& j, T b) noexcept
inline void to_json(BasicJsonType& j, T b) noexcept
{
external_constructor<value_t::boolean>::construct(j, b);
}
template<typename BasicJsonType, typename CompatibleString,
enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
void to_json(BasicJsonType& j, const CompatibleString& s)
inline void to_json(BasicJsonType& j, const CompatibleString& s)
{
external_constructor<value_t::string>::construct(j, s);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
inline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
{
external_constructor<value_t::string>::construct(j, std::move(s));
}
template<typename BasicJsonType, typename FloatType,
enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
void to_json(BasicJsonType& j, FloatType val) noexcept
inline void to_json(BasicJsonType& j, FloatType val) noexcept
{
external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
}
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
{
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
}
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
{
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
}
template<typename BasicJsonType, typename EnumType,
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
void to_json(BasicJsonType& j, EnumType e) noexcept
inline void to_json(BasicJsonType& j, EnumType e) noexcept
{
using underlying_type = typename std::underlying_type<EnumType>::type;
external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, const std::vector<bool>& e)
inline void to_json(BasicJsonType& j, const std::vector<bool>& e)
{
external_constructor<value_t::array>::construct(j, e);
}
@ -328,39 +328,39 @@ template < typename BasicJsonType, typename CompatibleArrayType,
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
!is_basic_json<CompatibleArrayType>::value,
int > = 0 >
void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
{
external_constructor<value_t::array>::construct(j, arr);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
{
external_constructor<value_t::binary>::construct(j, bin);
}
template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
void to_json(BasicJsonType& j, const std::valarray<T>& arr)
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
{
external_constructor<value_t::array>::construct(j, std::move(arr));
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
{
external_constructor<value_t::array>::construct(j, std::move(arr));
}
template < typename BasicJsonType, typename CompatibleObjectType,
enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
{
external_constructor<value_t::object>::construct(j, obj);
}
template<typename BasicJsonType>
void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
{
external_constructor<value_t::object>::construct(j, std::move(obj));
}
@ -370,13 +370,13 @@ template <
enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
int > = 0 >
void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
inline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
{
external_constructor<value_t::array>::construct(j, arr);
}
template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
inline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
{
j = { p.first, p.second };
}
@ -384,26 +384,26 @@ void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
// for https://github.com/nlohmann/json/pull/1134
template<typename BasicJsonType, typename T,
enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
void to_json(BasicJsonType& j, const T& b)
inline void to_json(BasicJsonType& j, const T& b)
{
j = { {b.key(), b.value()} };
}
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
{
j = { std::get<Idx>(t)... };
}
template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
void to_json(BasicJsonType& j, const T& t)
inline void to_json(BasicJsonType& j, const T& t)
{
to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
}
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
template<typename BasicJsonType>
void to_json(BasicJsonType& j, const std_fs::path& p)
inline void to_json(BasicJsonType& j, const std_fs::path& p)
{
j = p.string();
}
@ -420,11 +420,16 @@ struct to_json_fn
};
} // namespace detail
#ifndef JSON_HAS_CPP_17
/// namespace to hold default `to_json` function
/// to see why this is required:
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
{
constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; // NOLINT(misc-definitions-in-headers)
#endif
JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)
detail::static_const<detail::to_json_fn>::value;
#ifndef JSON_HAS_CPP_17
} // namespace
#endif
} // namespace nlohmann

View File

@ -51,9 +51,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// make sure BasicJsonType is basic_json or const basic_json
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
"iter_impl only accepts (const) basic_json");
// superficial check for the LegacyBidirectionalIterator named requirement
static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
&& std::is_base_of<std::bidirectional_iterator_tag, typename array_t::iterator::iterator_category>::value,
"basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
public:
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
/// A user-defined iterator should provide publicly accessible typedefs named

View File

@ -6,6 +6,10 @@
#include <tuple> // tuple_size, get, tuple_element
#include <utility> // move
#if JSON_HAS_RANGES
#include <ranges> // enable_borrowed_range
#endif
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>
@ -25,14 +29,14 @@ template<typename IteratorType> class iteration_proxy_value
public:
using difference_type = std::ptrdiff_t;
using value_type = iteration_proxy_value;
using pointer = value_type * ;
using reference = value_type & ;
using pointer = value_type *;
using reference = value_type &;
using iterator_category = std::input_iterator_tag;
using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
private:
/// the iterator
IteratorType anchor;
IteratorType anchor{};
/// an index for arrays (used to create key names)
std::size_t array_index = 0;
/// last stringified array index
@ -40,15 +44,30 @@ template<typename IteratorType> class iteration_proxy_value
/// a string representation of the array index
mutable string_type array_index_str = "0";
/// an empty string (to return a reference for primitive values)
const string_type empty_str{};
string_type empty_str{};
public:
explicit iteration_proxy_value(IteratorType it) noexcept
explicit iteration_proxy_value() = default;
explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)
noexcept(std::is_nothrow_move_constructible<IteratorType>::value
&& std::is_nothrow_default_constructible<string_type>::value)
: anchor(std::move(it))
, array_index(array_index_)
{}
iteration_proxy_value(iteration_proxy_value const&) = default;
iteration_proxy_value& operator=(iteration_proxy_value const&) = default;
// older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions
iteration_proxy_value(iteration_proxy_value&&)
noexcept(std::is_nothrow_move_constructible<IteratorType>::value
&& std::is_nothrow_move_constructible<string_type>::value) = default;
iteration_proxy_value& operator=(iteration_proxy_value&&)
noexcept(std::is_nothrow_move_assignable<IteratorType>::value
&& std::is_nothrow_move_assignable<string_type>::value) = default;
~iteration_proxy_value() = default;
/// dereference operator (needed for range-based for)
iteration_proxy_value& operator*()
const iteration_proxy_value& operator*() const
{
return *this;
}
@ -62,6 +81,14 @@ template<typename IteratorType> class iteration_proxy_value
return *this;
}
iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)
{
auto tmp = iteration_proxy_value(anchor, array_index);
++anchor;
++array_index;
return tmp;
}
/// equality operator (needed for InputIterator)
bool operator==(const iteration_proxy_value& o) const
{
@ -122,25 +149,34 @@ template<typename IteratorType> class iteration_proxy
{
private:
/// the container to iterate
typename IteratorType::reference container;
typename IteratorType::pointer container = nullptr;
public:
explicit iteration_proxy() = default;
/// construct iteration proxy from a container
explicit iteration_proxy(typename IteratorType::reference cont) noexcept
: container(cont) {}
: container(&cont) {}
iteration_proxy(iteration_proxy const&) = default;
iteration_proxy& operator=(iteration_proxy const&) = default;
iteration_proxy(iteration_proxy&&) noexcept = default;
iteration_proxy& operator=(iteration_proxy&&) noexcept = default;
~iteration_proxy() = default;
/// return iterator begin (needed for range-based for)
iteration_proxy_value<IteratorType> begin() noexcept
iteration_proxy_value<IteratorType> begin() const noexcept
{
return iteration_proxy_value<IteratorType>(container.begin());
return iteration_proxy_value<IteratorType>(container->begin());
}
/// return iterator end (needed for range-based for)
iteration_proxy_value<IteratorType> end() noexcept
iteration_proxy_value<IteratorType> end() const noexcept
{
return iteration_proxy_value<IteratorType>(container.end());
return iteration_proxy_value<IteratorType>(container->end());
}
};
// Structured Bindings Support
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
// And see https://github.com/nlohmann/json/pull/1391
@ -187,3 +223,8 @@ class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
#pragma clang diagnostic pop
#endif
} // namespace std
#if JSON_HAS_RANGES
template <typename IteratorType>
inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;
#endif

View File

@ -37,6 +37,12 @@
#define JSON_HAS_CPP_11
#endif
#ifdef __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif
#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
#ifdef JSON_HAS_CPP_17
#if defined(__cpp_lib_filesystem)
@ -98,14 +104,31 @@
#endif
#ifndef JSON_HAS_THREE_WAY_COMPARISON
#if defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L \
&& defined(__cpp_impl_three_way_comparison)&& __cpp_impl_three_way_comparison >= 201907L
#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
&& defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
#define JSON_HAS_THREE_WAY_COMPARISON 1
#else
#define JSON_HAS_THREE_WAY_COMPARISON 0
#endif
#endif
#ifndef JSON_HAS_RANGES
// ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
#if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
#define JSON_HAS_RANGES 0
#elif defined(__cpp_lib_ranges)
#define JSON_HAS_RANGES 1
#else
#define JSON_HAS_RANGES 0
#endif
#endif
#ifdef JSON_HAS_CPP_17
#define JSON_INLINE_VARIABLE inline
#else
#define JSON_INLINE_VARIABLE
#endif
#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
#else
@ -429,3 +452,7 @@
#ifndef JSON_DIAGNOSTICS
#define JSON_DIAGNOSTICS 0
#endif
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
#endif

View File

@ -14,6 +14,7 @@
#undef NLOHMANN_BASIC_JSON_TPL
#undef JSON_EXPLICIT
#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL
#undef JSON_INLINE_VARIABLE
#undef JSON_NO_UNIQUE_ADDRESS
#ifndef JSON_TEST_KEEP_MACROS
@ -26,6 +27,8 @@
#undef JSON_HAS_FILESYSTEM
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
#undef JSON_HAS_THREE_WAY_COMPARISON
#undef JSON_HAS_RANGES
#undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#endif
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>

View File

@ -5,6 +5,11 @@
#include <cstdint> // uint8_t
#include <string> // string
#include <nlohmann/detail/macro_scope.hpp>
#if JSON_HAS_THREE_WAY_COMPARISON
#include <compare> // partial_ordering
#endif
namespace nlohmann
{
namespace detail
@ -64,7 +69,11 @@ Returns an ordering that is similar to Python:
@since version 1.0.0
*/
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
#if JSON_HAS_THREE_WAY_COMPARISON
inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*
#else
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
#endif
{
static constexpr std::array<std::uint8_t, 9> order = {{
0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
@ -75,7 +84,26 @@ inline bool operator<(const value_t lhs, const value_t rhs) noexcept
const auto l_index = static_cast<std::size_t>(lhs);
const auto r_index = static_cast<std::size_t>(rhs);
#if JSON_HAS_THREE_WAY_COMPARISON
if (l_index < order.size() && r_index < order.size())
{
return order[l_index] <=> order[r_index]; // *NOPAD*
}
return std::partial_ordering::unordered;
#else
return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
#endif
}
// GCC selects the built-in operator< over an operator rewritten from
// a user-defined spaceship operator
// Clang, MSVC, and ICC select the rewritten candidate
// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
{
return std::is_lt(lhs <=> rhs); // *NOPAD*
}
#endif
} // namespace detail
} // namespace nlohmann

View File

@ -1905,7 +1905,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
detail::negation<std::is_same<ValueType, typename string_t::value_type>>,
detail::negation<detail::is_basic_json<ValueType>>,
detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,
#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))
detail::negation<std::is_same<ValueType, std::string_view>>,
#endif
@ -3538,7 +3537,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @}
public:
//////////////////////////////////////////
// lexicographical comparison operators //
//////////////////////////////////////////
@ -3546,6 +3544,212 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @name lexicographical comparison operators
/// @{
// note parentheses around operands are necessary; see
// https://github.com/nlohmann/json/issues/1530
#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result) \
const auto lhs_type = lhs.type(); \
const auto rhs_type = rhs.type(); \
\
if (lhs_type == rhs_type) /* NOLINT(readability/braces) */ \
{ \
switch (lhs_type) \
{ \
case value_t::array: \
return (*lhs.m_value.array) op (*rhs.m_value.array); \
\
case value_t::object: \
return (*lhs.m_value.object) op (*rhs.m_value.object); \
\
case value_t::null: \
return (null_result); \
\
case value_t::string: \
return (*lhs.m_value.string) op (*rhs.m_value.string); \
\
case value_t::boolean: \
return (lhs.m_value.boolean) op (rhs.m_value.boolean); \
\
case value_t::number_integer: \
return (lhs.m_value.number_integer) op (rhs.m_value.number_integer); \
\
case value_t::number_unsigned: \
return (lhs.m_value.number_unsigned) op (rhs.m_value.number_unsigned); \
\
case value_t::number_float: \
return (lhs.m_value.number_float) op (rhs.m_value.number_float); \
\
case value_t::binary: \
return (*lhs.m_value.binary) op (*rhs.m_value.binary); \
\
case value_t::discarded: \
default: \
return (unordered_result); \
} \
} \
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) \
{ \
return static_cast<number_float_t>(lhs.m_value.number_integer) op rhs.m_value.number_float; \
} \
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) \
{ \
return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_integer); \
} \
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) \
{ \
return static_cast<number_float_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_float; \
} \
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) \
{ \
return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_unsigned); \
} \
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) \
{ \
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_integer; \
} \
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) \
{ \
return lhs.m_value.number_integer op static_cast<number_integer_t>(rhs.m_value.number_unsigned); \
} \
else if(compares_unordered(lhs, rhs))\
{\
return (unordered_result);\
}\
\
return (default_result);
JSON_PRIVATE_UNLESS_TESTED:
// returns true if:
// - any operand is NaN and the other operand is of number type
// - any operand is discarded
// in legacy mode, discarded values are considered ordered if
// an operation is computed as an odd number of inverses of others
static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept
{
if ((lhs.is_number_float() && std::isnan(lhs.m_value.number_float) && rhs.is_number())
|| (rhs.is_number_float() && std::isnan(rhs.m_value.number_float) && lhs.is_number()))
{
return true;
}
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;
#else
static_cast<void>(inverse);
return lhs.is_discarded() || rhs.is_discarded();
#endif
}
private:
bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept
{
return compares_unordered(*this, rhs, inverse);
}
public:
#if JSON_HAS_THREE_WAY_COMPARISON
/// @brief comparison: equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
bool operator==(const_reference rhs) const noexcept
{
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
const_reference lhs = *this;
JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
/// @brief comparison: equal
/// @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
{
return *this == basic_json(rhs);
}
/// @brief comparison: not equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
bool operator!=(const_reference rhs) const noexcept
{
if (compares_unordered(rhs, true))
{
return false;
}
return !operator==(rhs);
}
/// @brief comparison: 3-way
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
{
const_reference lhs = *this;
// default_result is used if we cannot compare values. In that case,
// we compare types.
JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
std::partial_ordering::equivalent,
std::partial_ordering::unordered,
lhs_type <=> rhs_type) // *NOPAD*
}
/// @brief comparison: 3-way
/// @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*
{
return *this <=> basic_json(rhs); // *NOPAD*
}
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
// all operators that are computed as an odd number of inverses of others
// need to be overloaded to emulate the legacy comparison behavior
/// @brief comparison: less than or equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
bool operator<=(const_reference rhs) const noexcept
{
if (compares_unordered(rhs, true))
{
return false;
}
return !(rhs < *this);
}
/// @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>
bool operator<=(ScalarType rhs) const noexcept
{
return *this <= basic_json(rhs);
}
/// @brief comparison: greater than or equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
bool operator>=(const_reference rhs) const noexcept
{
if (compares_unordered(rhs, true))
{
return false;
}
return !(*this < 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>
bool operator>=(ScalarType rhs) const noexcept
{
return *this >= basic_json(rhs);
}
#endif
#else
/// @brief comparison: equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
friend bool operator==(const_reference lhs, const_reference rhs) noexcept
@ -3554,71 +3758,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
const auto lhs_type = lhs.type();
const auto rhs_type = rhs.type();
if (lhs_type == rhs_type)
{
switch (lhs_type)
{
case value_t::array:
return *lhs.m_value.array == *rhs.m_value.array;
case value_t::object:
return *lhs.m_value.object == *rhs.m_value.object;
case value_t::null:
return true;
case value_t::string:
return *lhs.m_value.string == *rhs.m_value.string;
case value_t::boolean:
return lhs.m_value.boolean == rhs.m_value.boolean;
case value_t::number_integer:
return lhs.m_value.number_integer == rhs.m_value.number_integer;
case value_t::number_unsigned:
return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;
case value_t::number_float:
return lhs.m_value.number_float == rhs.m_value.number_float;
case value_t::binary:
return *lhs.m_value.binary == *rhs.m_value.binary;
case value_t::discarded:
default:
return false;
}
}
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;
}
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
{
return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
}
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;
}
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);
}
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
{
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;
}
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);
}
return false;
JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
@ -3646,6 +3786,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
{
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(lhs == rhs);
}
@ -3671,76 +3815,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
friend bool operator<(const_reference lhs, const_reference rhs) noexcept
{
const auto lhs_type = lhs.type();
const auto rhs_type = rhs.type();
if (lhs_type == rhs_type)
{
switch (lhs_type)
{
case value_t::array:
// note parentheses are necessary, see
// https://github.com/nlohmann/json/issues/1530
return (*lhs.m_value.array) < (*rhs.m_value.array);
case value_t::object:
return (*lhs.m_value.object) < (*rhs.m_value.object);
case value_t::null:
return false;
case value_t::string:
return (*lhs.m_value.string) < (*rhs.m_value.string);
case value_t::boolean:
return (lhs.m_value.boolean) < (rhs.m_value.boolean);
case value_t::number_integer:
return (lhs.m_value.number_integer) < (rhs.m_value.number_integer);
case value_t::number_unsigned:
return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned);
case value_t::number_float:
return (lhs.m_value.number_float) < (rhs.m_value.number_float);
case value_t::binary:
return (*lhs.m_value.binary) < (*rhs.m_value.binary);
case value_t::discarded:
default:
return false;
}
}
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;
}
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
{
return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);
}
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
}
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);
}
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);
}
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
{
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
}
// We only reach this line if we cannot compare values. In that case,
// default_result is used if we cannot compare values. In that case,
// we compare types. Note we have to call the operator explicitly,
// because MSVC has problems otherwise.
return operator<(lhs_type, rhs_type);
JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
}
/// @brief comparison: less than
@ -3765,6 +3843,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
{
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(rhs < lhs);
}
@ -3790,6 +3872,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
friend bool operator>(const_reference lhs, const_reference rhs) noexcept
{
// double inverse
if (compares_unordered(lhs, rhs))
{
return false;
}
return !(lhs <= rhs);
}
@ -3815,6 +3902,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
{
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(lhs < rhs);
}
@ -3835,6 +3926,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
return basic_json(lhs) >= rhs;
}
#endif
#undef JSON_IMPLEMENT_OPERATOR
/// @}
@ -5031,10 +5125,14 @@ struct less< ::nlohmann::detail::value_t> // do not remove the space after '<',
@brief compare two value_t enum values
@since version 3.0.0
*/
bool operator()(nlohmann::detail::value_t lhs,
nlohmann::detail::value_t rhs) const noexcept
bool operator()(::nlohmann::detail::value_t lhs,
::nlohmann::detail::value_t rhs) const noexcept
{
return nlohmann::detail::operator<(lhs, rhs);
#if JSON_HAS_THREE_WAY_COMPARISON
return std::is_lt(lhs <=> rhs); // *NOPAD*
#else
return ::nlohmann::detail::operator<(lhs, rhs);
#endif
}
};

File diff suppressed because it is too large Load Diff

View File

@ -123,6 +123,15 @@ foreach(file ${files})
json_test_add_test_for(${file} MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force})
endforeach()
# test legacy comparison of discarded values
json_test_set_test_options(test-comparison_legacy
COMPILE_DEFINITIONS JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1
)
json_test_add_test_for(src/unit-comparison.cpp
NAME test-comparison_legacy
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
)
# *DO NOT* use json_test_set_test_options() below this line
#############################################################################

View File

@ -1454,17 +1454,17 @@ TEST_CASE("parser class")
SECTION("filter specific element")
{
json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept
{
// filter all number(2) elements
return j != json(2);
return event != json::parse_event_t::value || j != json(2);
});
CHECK (j_object == json({{"bar", {{"baz", 1}}}}));
json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept
{
return j != json(2);
return event != json::parse_event_t::value || j != json(2);
});
CHECK (j_array == json({1, {3, 4, 5}, 4, 5}));

View File

@ -27,11 +27,49 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// cmake/test.cmake selects the C++ standard versions with which to build a
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
// When using macros that are only defined for particular versions of the standard
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
// version macro in a comment close by, like this:
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using nlohmann::json;
#if JSON_HAS_THREE_WAY_COMPARISON
// this can be replaced with the doctest stl extension header in version 2.5
namespace doctest
{
template<> struct StringMaker<std::partial_ordering>
{
static String convert(const std::partial_ordering& order)
{
if (order == std::partial_ordering::less)
{
return "std::partial_ordering::less";
}
if (order == std::partial_ordering::equivalent)
{
return "std::partial_ordering::equivalent";
}
if (order == std::partial_ordering::greater)
{
return "std::partial_ordering::greater";
}
if (order == std::partial_ordering::unordered)
{
return "std::partial_ordering::unordered";
}
return "{?}";
}
};
} // namespace doctest
#endif
namespace
{
// helper function to check std::less<json::value_t>
@ -45,6 +83,27 @@ bool f(A a, B b, U u = U())
TEST_CASE("lexicographical comparison operators")
{
constexpr auto f_ = false;
constexpr auto _t = true;
constexpr auto nan = std::numeric_limits<json::number_float_t>::quiet_NaN();
#if JSON_HAS_THREE_WAY_COMPARISON
constexpr auto lt = std::partial_ordering::less;
constexpr auto gt = std::partial_ordering::greater;
constexpr auto eq = std::partial_ordering::equivalent;
constexpr auto un = std::partial_ordering::unordered;
#endif
#if JSON_HAS_THREE_WAY_COMPARISON
INFO("using 3-way comparison");
#endif
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
INFO("using legacy comparison");
#endif
//REQUIRE(std::numeric_limits<json::number_float_t>::has_quiet_NaN);
REQUIRE(std::isnan(nan));
SECTION("types")
{
std::vector<json::value_t> j_types =
@ -57,97 +116,268 @@ TEST_CASE("lexicographical comparison operators")
json::value_t::object,
json::value_t::array,
json::value_t::string,
json::value_t::binary
json::value_t::binary,
json::value_t::discarded
};
std::vector<std::vector<bool>> expected_lt =
{
//0 1 2 3 4 5 6 7 8 9
{f_, _t, _t, _t, _t, _t, _t, _t, _t, f_}, // 0
{f_, f_, _t, _t, _t, _t, _t, _t, _t, f_}, // 1
{f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, // 2
{f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, // 3
{f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, // 4
{f_, f_, f_, f_, f_, f_, _t, _t, _t, f_}, // 5
{f_, f_, f_, f_, f_, f_, f_, _t, _t, f_}, // 6
{f_, f_, f_, f_, f_, f_, f_, f_, _t, f_}, // 7
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 8
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 9
};
SECTION("comparison: less")
{
std::vector<std::vector<bool>> expected =
{
{false, true, true, true, true, true, true, true, true},
{false, false, true, true, true, true, true, true, true},
{false, false, false, false, false, true, true, true, true},
{false, false, false, false, false, true, true, true, true},
{false, false, false, false, false, true, true, true, true},
{false, false, false, false, false, false, true, true, true},
{false, false, false, false, false, false, false, true, true},
{false, false, false, false, false, false, false, false, true},
{false, false, false, false, false, false, false, false, false}
};
REQUIRE(expected_lt.size() == j_types.size());
for (size_t i = 0; i < j_types.size(); ++i)
{
REQUIRE(expected_lt[i].size() == j_types.size());
for (size_t j = 0; j < j_types.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
// check precomputed values
CHECK(operator<(j_types[i], j_types[j]) == expected[i][j]);
CHECK(f(j_types[i], j_types[j]) == expected[i][j]);
#if JSON_HAS_THREE_WAY_COMPARISON
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
CHECK((j_types[i] < j_types[j]) == expected_lt[i][j]);
#else
CHECK(operator<(j_types[i], j_types[j]) == expected_lt[i][j]);
#endif
CHECK(f(j_types[i], j_types[j]) == expected_lt[i][j]);
}
}
}
#if JSON_HAS_THREE_WAY_COMPARISON
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
SECTION("comparison: 3-way")
{
std::vector<std::vector<std::partial_ordering>> expected =
{
//0 1 2 3 4 5 6 7 8 9
{eq, lt, lt, lt, lt, lt, lt, lt, lt, un}, // 0
{gt, eq, lt, lt, lt, lt, lt, lt, lt, un}, // 1
{gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, // 2
{gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, // 3
{gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, // 4
{gt, gt, gt, gt, gt, eq, lt, lt, lt, un}, // 5
{gt, gt, gt, gt, gt, gt, eq, lt, lt, un}, // 6
{gt, gt, gt, gt, gt, gt, gt, eq, lt, un}, // 7
{gt, gt, gt, gt, gt, gt, gt, gt, eq, un}, // 8
{un, un, un, un, un, un, un, un, un, un}, // 9
};
// check expected partial_ordering against expected boolean
REQUIRE(expected.size() == expected_lt.size());
for (size_t i = 0; i < expected.size(); ++i)
{
REQUIRE(expected[i].size() == expected_lt[i].size());
for (size_t j = 0; j < expected[i].size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK(std::is_lt(expected[i][j]) == expected_lt[i][j]);
}
}
// check 3-way comparison against expected partial_ordering
REQUIRE(expected.size() == j_types.size());
for (size_t i = 0; i < j_types.size(); ++i)
{
REQUIRE(expected[i].size() == j_types.size());
for (size_t j = 0; j < j_types.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK((j_types[i] <=> j_types[j]) == expected[i][j]); // *NOPAD*
}
}
}
#endif
}
SECTION("values")
{
json j_values =
{
nullptr, nullptr,
-17, 42,
8u, 13u,
3.14159, 23.42,
"foo", "bar",
true, false,
{1, 2, 3}, {"one", "two", "three"},
{{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}},
json::binary({1, 2, 3}), json::binary({1, 2, 4})
nullptr, nullptr, // 0 1
-17, 42, // 2 3
8u, 13u, // 4 5
3.14159, 23.42, // 6 7
nan, nan, // 8 9
"foo", "bar", // 10 11
true, false, // 12 13
{1, 2, 3}, {"one", "two", "three"}, // 14 15
{{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}, // 16 17
json::binary({1, 2, 3}), json::binary({1, 2, 4}), // 18 19
json(json::value_t::discarded), json(json::value_t::discarded) // 20 21
};
SECTION("comparison: equal")
std::vector<std::vector<bool>> expected_eq =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{_t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 0
{_t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 1
{f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 2
{f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 3
{f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 4
{f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 5
{f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 6
{f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 7
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 8
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 11
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 12
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_}, // 19
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
};
std::vector<std::vector<bool>> expected_lt =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_}, // 0
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_}, // 1
{f_, f_, f_, _t, _t, _t, _t, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 2
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 3
{f_, f_, f_, _t, f_, _t, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 4
{f_, f_, f_, _t, f_, f_, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 5
{f_, f_, f_, _t, _t, _t, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 6
{f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 7
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 8
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 11
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 12
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, _t, f_, f_, _t, _t, f_, f_}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, f_, f_, _t, _t, f_, f_}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, f_, _t, _t, f_, f_}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 19
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
};
SECTION("compares unordered")
{
std::vector<std::vector<bool>> expected =
{
{true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true}
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 0
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 1
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 2
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 3
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 4
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 5
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 6
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 7
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 8
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 11
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 12
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 19
{_t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t}, // 20
{_t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t}, // 21
};
// check if two values compare unordered as expected
REQUIRE(expected.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK(json::compares_unordered(j_values[i], j_values[j]) == expected[i][j]);
}
}
}
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
SECTION("compares unordered (inverse)")
{
std::vector<std::vector<bool>> expected =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 0
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 1
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 2
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 3
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 4
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 5
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 6
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 7
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 8
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 11
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 12
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 19
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
};
// check that two values compare unordered as expected (with legacy-mode enabled)
REQUIRE(expected.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CAPTURE(j_values[i])
CAPTURE(j_values[j])
// check precomputed values
CHECK( (j_values[i] == j_values[j]) == expected[i][j] );
CHECK(json::compares_unordered(j_values[i], j_values[j], true) == expected[i][j]);
}
}
}
#endif
// comparison with discarded elements
json j_discarded(json::value_t::discarded);
for (const auto& v : j_values)
SECTION("comparison: equal")
{
// check that two values compare equal
REQUIRE(expected_eq.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
CHECK( (v == j_discarded) == false);
CHECK( (j_discarded == v) == false);
CHECK( (j_discarded == j_discarded) == false);
REQUIRE(expected_eq[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK((j_values[i] == j_values[j]) == expected_eq[i][j]);
}
}
// compare with null pointer
@ -158,121 +388,229 @@ TEST_CASE("lexicographical comparison operators")
SECTION("comparison: not equal")
{
// check that two values compare unequal as expected
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
// check definition
CHECK( (j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]) );
if (json::compares_unordered(j_values[i], j_values[j], true))
{
// if two values compare unordered,
// check that the boolean comparison result is always false
CHECK_FALSE(j_values[i] != j_values[j]);
}
else
{
// otherwise, check that they compare according to their definition
// as the inverse of equal
CHECK((j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]));
}
}
}
// compare with null pointer
json j_null;
CHECK( (j_null != nullptr) == false);
CHECK( (nullptr != j_null) == false);
CHECK( (j_null != nullptr) == !(j_null == nullptr));
CHECK( (nullptr != j_null) == !(nullptr == j_null));
CHECK((j_null != nullptr) == false);
CHECK((nullptr != j_null) == false);
CHECK((j_null != nullptr) == !(j_null == nullptr));
CHECK((nullptr != j_null) == !(nullptr == j_null));
}
SECTION("comparison: less")
{
std::vector<std::vector<bool>> expected =
{
{false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
{false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
{false, false, false, true, true, true, true, true, true, true, false, false, true, true, true, true, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true, true, true},
{false, false, false, true, false, true, false, true, true, true, false, false, true, true, true, true, true, true},
{false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true, true, true},
{false, false, false, true, true, true, false, true, true, true, false, false, true, true, true, true, true, true},
{false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true, true, true},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true},
{false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, true},
{false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true, true, true},
{false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false, true, true},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
};
// check that two values compare less than as expected
REQUIRE(expected_lt.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected_lt[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
// Skip comparing indicies 12 and 13, and 13 and 12 in C++20 pending fix
// See issue #3207
#if defined(JSON_HAS_CPP_20) || JSON_HAS_THREE_WAY_COMPARISON
if ((i == 12 && j == 13) || (i == 13 && j == 12))
{
continue;
}
#endif
CAPTURE(i)
CAPTURE(j)
CAPTURE(j_values[i])
CAPTURE(j_values[j])
// check precomputed values
CHECK( (j_values[i] < j_values[j]) == expected[i][j] );
CHECK((j_values[i] < j_values[j]) == expected_lt[i][j]);
}
}
// comparison with discarded elements
json j_discarded(json::value_t::discarded);
for (size_t i = 0; i < j_values.size(); ++i)
{
CAPTURE(i)
CHECK( (j_values[i] < j_discarded) == false);
CHECK( (j_discarded < j_values[i]) == false);
CHECK( (j_discarded < j_discarded) == false);
}
}
SECTION("comparison: less than or equal equal")
{
// check that two values compare less than or equal as expected
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
// check definition
CHECK( (j_values[i] <= j_values[j]) == !(j_values[j] < j_values[i]) );
if (json::compares_unordered(j_values[i], j_values[j], true))
{
// if two values compare unordered,
// check that the boolean comparison result is always false
CHECK_FALSE(j_values[i] <= j_values[j]);
}
else
{
// otherwise, check that they compare according to their definition
// as the inverse of less than with the operand order reversed
CHECK((j_values[i] <= j_values[j]) == !(j_values[j] < j_values[i]));
}
}
}
}
SECTION("comparison: greater than")
{
// check that two values compare greater than as expected
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
// check definition
CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) );
if (json::compares_unordered(j_values[i], j_values[j]))
{
// if two values compare unordered,
// check that the boolean comparison result is always false
CHECK_FALSE(j_values[i] > j_values[j]);
}
else
{
// otherwise, check that they compare according to their definition
// as the inverse of less than or equal which is defined as
// the inverse of less than with the operand order reversed
CHECK((j_values[i] > j_values[j]) == !(j_values[i] <= j_values[j]));
CHECK((j_values[i] > j_values[j]) == !!(j_values[j] < j_values[i]));
}
}
}
}
SECTION("comparison: greater than or equal")
{
// check that two values compare greater than or equal as expected
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
// check definition
CHECK( (j_values[i] >= j_values[j]) == !(j_values[i] < j_values[j]) );
if (json::compares_unordered(j_values[i], j_values[j], true))
{
// if two values compare unordered,
// check that the boolean result is always false
CHECK_FALSE(j_values[i] >= j_values[j]);
}
else
{
// otherwise, check that they compare according to their definition
// as the inverse of less than
CHECK((j_values[i] >= j_values[j]) == !(j_values[i] < j_values[j]));
}
}
}
}
#if JSON_HAS_THREE_WAY_COMPARISON
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
SECTION("comparison: 3-way")
{
std::vector<std::vector<std::partial_ordering>> expected =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{eq, eq, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, un, un}, // 0
{eq, eq, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, un, un}, // 1
{gt, gt, eq, lt, lt, lt, lt, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 2
{gt, gt, gt, eq, gt, gt, gt, gt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 3
{gt, gt, gt, lt, eq, lt, gt, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 4
{gt, gt, gt, lt, gt, eq, gt, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 5
{gt, gt, gt, lt, lt, lt, eq, lt, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 6
{gt, gt, gt, lt, gt, gt, gt, eq, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 7
{gt, gt, un, un, un, un, un, un, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 8
{gt, gt, un, un, un, un, un, un, un, un, lt, lt, gt, gt, lt, lt, lt, lt, lt, lt, un, un}, // 9
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, eq, gt, gt, gt, gt, gt, gt, gt, lt, lt, un, un}, // 10
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, eq, gt, gt, gt, gt, gt, gt, lt, lt, un, un}, // 11
{gt, gt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, eq, gt, lt, lt, lt, lt, lt, lt, un, un}, // 12
{gt, gt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, lt, eq, lt, lt, lt, lt, lt, lt, un, un}, // 13
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, eq, lt, gt, gt, lt, lt, un, un}, // 14
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, gt, eq, gt, gt, lt, lt, un, un}, // 15
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, lt, lt, eq, gt, lt, lt, un, un}, // 16
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, lt, lt, gt, gt, lt, lt, lt, eq, lt, lt, un, un}, // 17
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, eq, lt, un, un}, // 18
{gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, gt, eq, un, un}, // 19
{un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un}, // 20
{un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un, un}, // 21
};
// check expected partial_ordering against expected booleans
REQUIRE(expected.size() == expected_eq.size());
REQUIRE(expected.size() == expected_lt.size());
for (size_t i = 0; i < expected.size(); ++i)
{
REQUIRE(expected[i].size() == expected_eq[i].size());
REQUIRE(expected[i].size() == expected_lt[i].size());
for (size_t j = 0; j < expected[i].size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK(std::is_eq(expected[i][j]) == expected_eq[i][j]);
CHECK(std::is_lt(expected[i][j]) == expected_lt[i][j]);
if (std::is_gt(expected[i][j]))
{
CHECK((!expected_eq[i][j] && !expected_lt[i][j]));
}
}
}
// check that two values compare according to their expected ordering
REQUIRE(expected.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK((j_values[i] <=> j_values[j]) == expected[i][j]); // *NOPAD*
}
}
}
#endif
}
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
SECTION("parser callback regression")
{
SECTION("filter specific element")
{
const auto* s_object = R"(
{
"foo": 2,
"bar": {
"baz": 1
}
}
)";
const auto* s_array = R"(
[1,2,[3,4,5],4,5]
)";
json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
{
// filter all number(2) elements
return j != json(2);
});
CHECK (j_object == json({{"bar", {{"baz", 1}}}}));
json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
{
return j != json(2);
});
CHECK (j_array == json({1, {3, 4, 5}, 4, 5}));
}
}
#endif
}

View File

@ -27,6 +27,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// cmake/test.cmake selects the C++ standard versions with which to build a
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
// When using macros that are only defined for particular versions of the standard
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
// version macro in a comment close by, like this:
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
@ -1582,12 +1589,4 @@ TEST_CASE("JSON to enum mapping")
}
}
#ifdef JSON_HAS_CPP_17
#undef JSON_HAS_CPP_17
#endif
#ifdef JSON_HAS_CPP_14
#undef JSON_HAS_CPP_14
#endif
DOCTEST_CLANG_SUPPRESS_WARNING_POP

View File

@ -80,7 +80,7 @@ TEST_CASE("iterator_wrapper")
json j = { {"A", 1}, {"B", 2} };
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{
switch (counter++)
{
@ -226,7 +226,7 @@ TEST_CASE("iterator_wrapper")
const json j = { {"A", 1}, {"B", 2} };
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{
switch (counter++)
{
@ -361,7 +361,7 @@ TEST_CASE("iterator_wrapper")
json j = { "A", "B" };
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{
switch (counter++)
{
@ -507,7 +507,7 @@ TEST_CASE("iterator_wrapper")
const json j = { "A", "B" };
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{
switch (counter++)
{
@ -624,7 +624,7 @@ TEST_CASE("iterator_wrapper")
json j = 1;
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{
++counter;
CHECK(i.key() == "");
@ -693,7 +693,7 @@ TEST_CASE("iterator_wrapper")
const json j = 1;
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{
++counter;
CHECK(i.key() == "");
@ -777,7 +777,7 @@ TEST_CASE("items()")
json j = { {"A", 1}, {"B", 2} };
int counter = 1;
for (auto& i : j.items())
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{
switch (counter++)
{
@ -939,7 +939,7 @@ TEST_CASE("items()")
const json j = { {"A", 1}, {"B", 2} };
int counter = 1;
for (auto& i : j.items())
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{
switch (counter++)
{
@ -1074,7 +1074,7 @@ TEST_CASE("items()")
json j = { "A", "B" };
int counter = 1;
for (auto& i : j.items())
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{
switch (counter++)
{
@ -1220,7 +1220,7 @@ TEST_CASE("items()")
const json j = { "A", "B" };
int counter = 1;
for (auto& i : j.items())
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{
switch (counter++)
{
@ -1337,7 +1337,7 @@ TEST_CASE("items()")
json j = 1;
int counter = 1;
for (auto& i : j.items())
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{
++counter;
CHECK(i.key() == "");
@ -1406,7 +1406,7 @@ TEST_CASE("items()")
const json j = 1;
int counter = 1;
for (auto& i : j.items())
for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{
++counter;
CHECK(i.key() == "");
@ -1448,13 +1448,5 @@ TEST_CASE("items()")
}
}
#ifdef JSON_HAS_CPP_17
#undef JSON_HAS_CPP_17
#endif
#ifdef JSON_HAS_CPP_14
#undef JSON_HAS_CPP_14
#endif
DOCTEST_GCC_SUPPRESS_WARNING_POP
DOCTEST_CLANG_SUPPRESS_WARNING_POP

View File

@ -27,11 +27,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// cmake/test.cmake selects the C++ standard versions with which to build a
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
// When using macros that are only defined for particular versions of the standard
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
// version macro in a comment close by, like this:
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
#include "doctest_compatibility.h"
#include <nlohmann/json.hpp>
using nlohmann::json;
#if JSON_HAS_RANGES
#include <algorithm>
#include <ranges>
#endif
TEST_CASE("iterators 2")
{
SECTION("iterator comparisons")
@ -881,4 +893,101 @@ TEST_CASE("iterators 2")
}
}
}
#if JSON_HAS_RANGES
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
SECTION("ranges")
{
SECTION("concepts")
{
using nlohmann::detail::iteration_proxy_value;
CHECK(std::bidirectional_iterator<json::iterator>);
CHECK(std::input_iterator<iteration_proxy_value<json::iterator>>);
CHECK(std::is_same<json::iterator, std::ranges::iterator_t<json>>::value);
CHECK(std::ranges::bidirectional_range<json>);
using nlohmann::detail::iteration_proxy;
using items_type = decltype(std::declval<json&>().items());
CHECK(std::is_same<items_type, iteration_proxy<json::iterator>>::value);
CHECK(std::is_same<iteration_proxy_value<json::iterator>, std::ranges::iterator_t<items_type>>::value);
CHECK(std::ranges::input_range<items_type>);
}
// libstdc++ algorithms don't work with Clang 15 (04/2022)
#if !defined(__clang__) || (defined(__clang__) && defined(__GLIBCXX__))
SECTION("algorithms")
{
SECTION("copy")
{
json j{"foo", "bar"};
auto j_copied = json::array();
std::ranges::copy(j, std::back_inserter(j_copied));
CHECK(j == j_copied);
}
SECTION("find_if")
{
json j{1, 3, 2, 4};
auto j_even = json::array();
#if JSON_USE_IMPLICIT_CONVERSIONS
auto it = std::ranges::find_if(j, [](int v) noexcept
{
return (v % 2) == 0;
});
#else
auto it = std::ranges::find_if(j, [](const json & j) noexcept
{
int v;
j.get_to(v);
return (v % 2) == 0;
});
#endif
CHECK(*it == 2);
}
}
#endif
// libstdc++ views don't work with Clang 15 (04/2022)
// libc++ hides limited ranges implementation behind guard macro
#if !(defined(__clang__) && (defined(__GLIBCXX__) || defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)))
SECTION("views")
{
SECTION("reverse")
{
json j{1, 2, 3, 4, 5};
json j_expected{5, 4, 3, 2, 1};
auto reversed = j | std::views::reverse;
CHECK(reversed == j_expected);
}
SECTION("transform")
{
json j
{
{ "a_key", "a_value"},
{ "b_key", "b_value"},
{ "c_key", "c_value"},
};
json j_expected{"a_key", "b_key", "c_key"};
auto transformed = j.items() | std::views::transform([](const auto & item)
{
return item.key();
});
auto j_transformed = json::array();
std::ranges::copy(transformed, std::back_inserter(j_transformed));
CHECK(j_transformed == j_expected);
}
}
#endif
}
#endif
}

View File

@ -27,6 +27,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// cmake/test.cmake selects the C++ standard versions with which to build a
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
// When using macros that are only defined for particular versions of the standard
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
// version macro in a comment close by, like this:
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
#include "doctest_compatibility.h"
// for some reason including this after the json header leads to linker errors with VS 2017...
@ -48,7 +55,6 @@ using ordered_json = nlohmann::ordered_json;
#endif
#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
// JSON_HAS_CPP_17 (magic keyword; do not remove)
#include <experimental/filesystem>
namespace nlohmann::detail
{
@ -788,6 +794,7 @@ TEST_CASE("regression tests 2")
}
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
// JSON_HAS_CPP_17 (do not remove; see note at top of file)
SECTION("issue #3070 - Version 3.10.3 breaks backward-compatibility with 3.10.2 ")
{
nlohmann::detail::std_fs::path text_path("/tmp/text.txt");