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
pull/3514/head
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 container: ghcr.io/nlohmann/json-ci:v2.3.0
strategy: strategy:
matrix: 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: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: cmake - name: cmake

View File

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

View File

@ -498,6 +498,20 @@ add_custom_target(ci_test_diagnostics
COMMENT "Compile and test with improved diagnostics enabled" 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. # Coverage.
############################################################################### ###############################################################################
@ -797,8 +811,9 @@ endfunction()
ci_get_cmake(3.1.0 CMAKE_3_1_0_BINARY) ci_get_cmake(3.1.0 CMAKE_3_1_0_BINARY)
ci_get_cmake(3.13.0 CMAKE_3_13_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_1_0 JSON_Diagnostics JSON_ImplicitConversions JSON_LegacyDiscardedValueComparison
set(JSON_CMAKE_FLAGS_3_13_0 "JSON_BuildTests") 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) function(ci_add_cmake_flags_targets flag min_version)
string(TOLOWER "ci_cmake_flag_${flag}" flag_target) string(TOLOWER "ci_cmake_flag_${flag}" flag_target)

View File

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

View File

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

View File

@ -1,21 +1,31 @@
# <small>nlohmann::basic_json::</small>operator== # <small>nlohmann::basic_json::</small>operator==
```cpp ```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> 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> 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 2. Compares a JSON value and a scalar or a scalar and a JSON value for equality by converting the
are the same according to their respective `operator==`. scalar to a JSON value and comparing both JSON values according to 1.
- Integer and floating-point numbers are automatically converted before comparison. Note that two NaN values are always
treated as unequal.
## Template parameters ## Template parameters
@ -32,7 +42,7 @@ Compares two JSON values for equality according to the following rules:
## Return value ## Return value
whether the values `lhs` and `rhs` are equal whether the values `lhs`/`*this` and `rhs` are equal
## Exception safety ## Exception safety
@ -46,7 +56,11 @@ Linear.
!!! note "Comparing special values" !!! 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. - JSON `#!cpp null` values are all equal.
- Discarded values never compare equal to themselves. - Discarded values never compare equal to themselves.
@ -117,4 +131,5 @@ Linear.
## Version history ## 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>= # <small>nlohmann::basic_json::</small>operator>=
```cpp ```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> 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> 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 1. Compares whether one JSON value `lhs` is greater than or equal to another JSON value `rhs`
`#!cpp !(lhs < 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 ## Template parameters
@ -38,6 +46,21 @@ No-throw guarantee: this function never throws exceptions.
Linear. 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 ## Examples
??? example ??? example
@ -54,6 +77,11 @@ Linear.
--8<-- "examples/operator__greaterequal.output" --8<-- "examples/operator__greaterequal.output"
``` ```
## See also
- [**operator<=>**](operator_spaceship.md) comparison: 3-way
## Version history ## 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> # <small>nlohmann::basic_json::</small>operator>
```cpp ```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> 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> 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 ## Template parameters
@ -37,6 +45,21 @@ No-throw guarantee: this function never throws exceptions.
Linear. 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 ## Examples
??? example ??? example
@ -53,6 +76,11 @@ Linear.
--8<-- "examples/operator__greater.output" --8<-- "examples/operator__greater.output"
``` ```
## See also
- [**operator<=>**](operator_spaceship.md) comparison: 3-way
## Version history ## 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<= # <small>nlohmann::basic_json::</small>operator<=
```cpp ```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> 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> 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 1. Compares whether one JSON value `lhs` is less than or equal to another JSON value `rhs`
`#cpp !(rhs < lhs)`. 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 ## Template parameters
@ -38,6 +46,21 @@ No-throw guarantee: this function never throws exceptions.
Linear. 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 ## Examples
??? example ??? example
@ -54,6 +77,11 @@ Linear.
--8<-- "examples/operator__lessequal.output" --8<-- "examples/operator__lessequal.output"
``` ```
## See also
- [**operator<=>**](operator_spaceship.md) comparison: 3-way
## Version history ## 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< # <small>nlohmann::basic_json::</small>operator<
```cpp ```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> 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> 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. 2. Compares wether a JSON value is less than a scalar or a scalar is less than a JSON value by converting
- Integer and floating-point numbers are automatically converted before comparison the scalar to a JSON value and comparing both JSON values according to 1.
- 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.
## Template parameters ## Template parameters
@ -52,6 +55,21 @@ No-throw guarantee: this function never throws exceptions.
Linear. 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 ## Examples
??? example ??? example
@ -68,6 +86,11 @@ Linear.
--8<-- "examples/operator__less.output" --8<-- "examples/operator__less.output"
``` ```
## See also
- [**operator<=>**](operator_spaceship.md) comparison: 3-way
## Version history ## 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!= # <small>nlohmann::basic_json::</small>operator!=
```cpp ```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> 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> 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 ## Template parameters
@ -27,7 +43,7 @@ Compares two JSON values for inequality by calculating `#!cpp !(lhs == rhs)`.
## Return value ## Return value
whether the values `lhs` and `rhs` are not equal whether the values `lhs`/`*this` and `rhs` are not equal
## Exception safety ## Exception safety
@ -37,6 +53,16 @@ No-throw guarantee: this function never throws exceptions.
Linear. 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 ## Examples
??? example ??? example
@ -69,4 +95,5 @@ Linear.
## Version history ## 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 ## Notes
There are three enumeration entries (number_integer, number_unsigned, and number_float), because the library !!! note "Ordering"
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 The order of types is as follows:
for floating-point numbers or to approximate integers which do not fit in the limits of their respective type.
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 ## 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_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_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_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 - [**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 - [**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 ## 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 - [**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 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=.md
- 'operator+=': api/basic_json/operator+=.md
- 'operator==': api/basic_json/operator_eq.md - 'operator==': api/basic_json/operator_eq.md
- 'operator!=': api/basic_json/operator_ne.md - 'operator!=': api/basic_json/operator_ne.md
- 'operator<': api/basic_json/operator_lt.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_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_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': api/basic_json/operator_literal_json.md
- 'operator""_json_pointer': api/basic_json/operator_literal_json_pointer.md - 'operator""_json_pointer': api/basic_json/operator_literal_json_pointer.md
- 'out_of_range': api/basic_json/out_of_range.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_CPP_20': api/macros/json_has_cpp_11.md
- 'JSON_HAS_EXPERIMENTAL_FILESYSTEM': api/macros/json_has_filesystem.md - 'JSON_HAS_EXPERIMENTAL_FILESYSTEM': api/macros/json_has_filesystem.md
- 'JSON_HAS_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_NOEXCEPTION': api/macros/json_noexception.md
- 'JSON_NO_IO': api/macros/json_no_io.md - 'JSON_NO_IO': api/macros/json_no_io.md
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.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_THROW_USER': api/macros/json_throw_user.md
- 'JSON_TRY_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_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': api/macros/nlohmann_define_type_intrusive.md
- 'NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT': 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 - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE': api/macros/nlohmann_define_type_non_intrusive.md
@ -319,5 +323,8 @@ plugins:
minify_html: true minify_html: true
- git-revision-date-localized - git-revision-date-localized
extra_css:
- css/custom.css
extra_javascript: extra_javascript:
- https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML - 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 namespace detail
{ {
template<typename BasicJsonType> 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())) if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{ {
@ -86,7 +86,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
} }
template<typename BasicJsonType> 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())) 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> 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())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
@ -111,7 +111,7 @@ template <
std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
&& !std::is_same<typename BasicJsonType::string_t, StringType>::value && !std::is_same<typename BasicJsonType::string_t, StringType>::value
&& !is_json_ref<StringType>::value, int > = 0 > && !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())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
@ -122,26 +122,26 @@ void from_json(const BasicJsonType& j, StringType& s)
} }
template<typename BasicJsonType> 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); get_arithmetic_value(j, val);
} }
template<typename BasicJsonType> 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); get_arithmetic_value(j, val);
} }
template<typename BasicJsonType> 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); get_arithmetic_value(j, val);
} }
template<typename BasicJsonType, typename EnumType, template<typename BasicJsonType, typename EnumType,
enable_if_t<std::is_enum<EnumType>::value, int> = 0> 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; typename std::underlying_type<EnumType>::type val;
get_arithmetic_value(j, 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 // forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator, template<typename BasicJsonType, typename T, typename Allocator,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0> 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())) 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 // valarray doesn't have an insert method
template<typename BasicJsonType, typename T, template<typename BasicJsonType, typename T,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0> 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())) 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> 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*>(); arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
} }
@ -237,8 +237,8 @@ template<typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t< enable_if_t<
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value, std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
int> = 0> int> = 0>
void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
priority_tag<0> /*unused*/) priority_tag<0> /*unused*/)
{ {
using std::end; using std::end;
@ -295,7 +295,7 @@ auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
} }
template<typename BasicJsonType> 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())) 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, template<typename BasicJsonType, typename ConstructibleObjectType,
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0> 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())) 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::number_float_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value, !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
int > = 0 > 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)) 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> 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> {}); 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> 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...> {}); 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, template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
typename = enable_if_t < !std::is_constructible < typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >> 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())) 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, template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
typename = enable_if_t < !std::is_constructible < typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >> 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())) 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 #if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
template<typename BasicJsonType> 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())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
@ -482,11 +482,16 @@ struct from_json_fn
}; };
} // namespace detail } // namespace detail
#ifndef JSON_HAS_CPP_17
/// namespace to hold default `from_json` function /// namespace to hold default `from_json` function
/// to see why this is required: /// to see why this is required:
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html /// 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) 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 } // namespace
#endif
} // namespace nlohmann } // namespace nlohmann

View File

@ -267,55 +267,55 @@ struct external_constructor<value_t::object>
template<typename BasicJsonType, typename T, template<typename BasicJsonType, typename T,
enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0> 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); external_constructor<value_t::boolean>::construct(j, b);
} }
template<typename BasicJsonType, typename CompatibleString, template<typename BasicJsonType, typename CompatibleString,
enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0> 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); external_constructor<value_t::string>::construct(j, s);
} }
template<typename BasicJsonType> 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)); external_constructor<value_t::string>::construct(j, std::move(s));
} }
template<typename BasicJsonType, typename FloatType, template<typename BasicJsonType, typename FloatType,
enable_if_t<std::is_floating_point<FloatType>::value, int> = 0> 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)); external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
} }
template<typename BasicJsonType, typename CompatibleNumberUnsignedType, template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0> 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)); external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
} }
template<typename BasicJsonType, typename CompatibleNumberIntegerType, template<typename BasicJsonType, typename CompatibleNumberIntegerType,
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0> 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)); external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
} }
template<typename BasicJsonType, typename EnumType, template<typename BasicJsonType, typename EnumType,
enable_if_t<std::is_enum<EnumType>::value, int> = 0> 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; using underlying_type = typename std::underlying_type<EnumType>::type;
external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e)); external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
} }
template<typename BasicJsonType> 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); 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&& !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
!is_basic_json<CompatibleArrayType>::value, !is_basic_json<CompatibleArrayType>::value,
int > = 0 > 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); external_constructor<value_t::array>::construct(j, arr);
} }
template<typename BasicJsonType> 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); external_constructor<value_t::binary>::construct(j, bin);
} }
template<typename BasicJsonType, typename T, template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0> 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)); external_constructor<value_t::array>::construct(j, std::move(arr));
} }
template<typename BasicJsonType> 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)); external_constructor<value_t::array>::construct(j, std::move(arr));
} }
template < typename BasicJsonType, typename CompatibleObjectType, template < typename BasicJsonType, typename CompatibleObjectType,
enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 > 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); external_constructor<value_t::object>::construct(j, obj);
} }
template<typename BasicJsonType> 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)); 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, 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) const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
int > = 0 > 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); 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 > 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 }; 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 // for https://github.com/nlohmann/json/pull/1134
template<typename BasicJsonType, typename T, template<typename BasicJsonType, typename T,
enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0> 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()} }; j = { {b.key(), b.value()} };
} }
template<typename BasicJsonType, typename Tuple, std::size_t... Idx> 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)... }; j = { std::get<Idx>(t)... };
} }
template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0> 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> {}); to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
} }
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM #if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
template<typename BasicJsonType> 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(); j = p.string();
} }
@ -420,11 +420,16 @@ struct to_json_fn
}; };
} // namespace detail } // namespace detail
#ifndef JSON_HAS_CPP_17
/// namespace to hold default `to_json` function /// namespace to hold default `to_json` function
/// to see why this is required: /// to see why this is required:
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html /// 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) 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 } // namespace
#endif
} // namespace nlohmann } // 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 // make sure BasicJsonType is basic_json or const basic_json
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value, static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
"iter_impl only accepts (const) basic_json"); "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: public:
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. /// 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. /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
/// A user-defined iterator should provide publicly accessible typedefs named /// A user-defined iterator should provide publicly accessible typedefs named

View File

@ -6,6 +6,10 @@
#include <tuple> // tuple_size, get, tuple_element #include <tuple> // tuple_size, get, tuple_element
#include <utility> // move #include <utility> // move
#if JSON_HAS_RANGES
#include <ranges> // enable_borrowed_range
#endif
#include <nlohmann/detail/meta/type_traits.hpp> #include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp> #include <nlohmann/detail/value_t.hpp>
@ -25,14 +29,14 @@ template<typename IteratorType> class iteration_proxy_value
public: public:
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using value_type = iteration_proxy_value; using value_type = iteration_proxy_value;
using pointer = value_type * ; using pointer = value_type *;
using reference = value_type & ; using reference = value_type &;
using iterator_category = std::input_iterator_tag; 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; using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
private: private:
/// the iterator /// the iterator
IteratorType anchor; IteratorType anchor{};
/// an index for arrays (used to create key names) /// an index for arrays (used to create key names)
std::size_t array_index = 0; std::size_t array_index = 0;
/// last stringified array index /// last stringified array index
@ -40,15 +44,30 @@ template<typename IteratorType> class iteration_proxy_value
/// a string representation of the array index /// a string representation of the array index
mutable string_type array_index_str = "0"; mutable string_type array_index_str = "0";
/// an empty string (to return a reference for primitive values) /// an empty string (to return a reference for primitive values)
const string_type empty_str{}; string_type empty_str{};
public: 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)) : 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) /// dereference operator (needed for range-based for)
iteration_proxy_value& operator*() const iteration_proxy_value& operator*() const
{ {
return *this; return *this;
} }
@ -62,6 +81,14 @@ template<typename IteratorType> class iteration_proxy_value
return *this; 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) /// equality operator (needed for InputIterator)
bool operator==(const iteration_proxy_value& o) const bool operator==(const iteration_proxy_value& o) const
{ {
@ -122,25 +149,34 @@ template<typename IteratorType> class iteration_proxy
{ {
private: private:
/// the container to iterate /// the container to iterate
typename IteratorType::reference container; typename IteratorType::pointer container = nullptr;
public: public:
explicit iteration_proxy() = default;
/// construct iteration proxy from a container /// construct iteration proxy from a container
explicit iteration_proxy(typename IteratorType::reference cont) noexcept 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) /// 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) /// 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 // Structured Bindings Support
// For further reference see https://blog.tartanllama.xyz/structured-bindings/ // For further reference see https://blog.tartanllama.xyz/structured-bindings/
// And see https://github.com/nlohmann/json/pull/1391 // 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 #pragma clang diagnostic pop
#endif #endif
} // namespace std } // 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 #define JSON_HAS_CPP_11
#endif #endif
#ifdef __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif
#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) #if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
#ifdef JSON_HAS_CPP_17 #ifdef JSON_HAS_CPP_17
#if defined(__cpp_lib_filesystem) #if defined(__cpp_lib_filesystem)
@ -98,14 +104,31 @@
#endif #endif
#ifndef JSON_HAS_THREE_WAY_COMPARISON #ifndef JSON_HAS_THREE_WAY_COMPARISON
#if defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L \ #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
&& 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 #define JSON_HAS_THREE_WAY_COMPARISON 1
#else #else
#define JSON_HAS_THREE_WAY_COMPARISON 0 #define JSON_HAS_THREE_WAY_COMPARISON 0
#endif #endif
#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) #if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
#else #else
@ -429,3 +452,7 @@
#ifndef JSON_DIAGNOSTICS #ifndef JSON_DIAGNOSTICS
#define JSON_DIAGNOSTICS 0 #define JSON_DIAGNOSTICS 0
#endif #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 NLOHMANN_BASIC_JSON_TPL
#undef JSON_EXPLICIT #undef JSON_EXPLICIT
#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL #undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL
#undef JSON_INLINE_VARIABLE
#undef JSON_NO_UNIQUE_ADDRESS #undef JSON_NO_UNIQUE_ADDRESS
#ifndef JSON_TEST_KEEP_MACROS #ifndef JSON_TEST_KEEP_MACROS
@ -26,6 +27,8 @@
#undef JSON_HAS_FILESYSTEM #undef JSON_HAS_FILESYSTEM
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
#undef JSON_HAS_THREE_WAY_COMPARISON #undef JSON_HAS_THREE_WAY_COMPARISON
#undef JSON_HAS_RANGES
#undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#endif #endif
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp> #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>

View File

@ -5,6 +5,11 @@
#include <cstdint> // uint8_t #include <cstdint> // uint8_t
#include <string> // string #include <string> // string
#include <nlohmann/detail/macro_scope.hpp>
#if JSON_HAS_THREE_WAY_COMPARISON
#include <compare> // partial_ordering
#endif
namespace nlohmann namespace nlohmann
{ {
namespace detail namespace detail
@ -64,7 +69,11 @@ Returns an ordering that is similar to Python:
@since version 1.0.0 @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 = {{ static constexpr std::array<std::uint8_t, 9> order = {{
0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, 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 l_index = static_cast<std::size_t>(lhs);
const auto r_index = static_cast<std::size_t>(rhs); 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]; 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 detail
} // namespace nlohmann } // 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<std::is_same<ValueType, typename string_t::value_type>>,
detail::negation<detail::is_basic_json<ValueType>>, detail::negation<detail::is_basic_json<ValueType>>,
detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>, 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)) #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>>, detail::negation<std::is_same<ValueType, std::string_view>>,
#endif #endif
@ -3538,7 +3537,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @} /// @}
public:
////////////////////////////////////////// //////////////////////////////////////////
// lexicographical comparison operators // // lexicographical comparison operators //
////////////////////////////////////////// //////////////////////////////////////////
@ -3546,6 +3544,212 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @name lexicographical comparison operators /// @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 /// @brief comparison: equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
friend bool operator==(const_reference lhs, const_reference rhs) noexcept 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 push
#pragma GCC diagnostic ignored "-Wfloat-equal" #pragma GCC diagnostic ignored "-Wfloat-equal"
#endif #endif
const auto lhs_type = lhs.type(); JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
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;
#ifdef __GNUC__ #ifdef __GNUC__
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #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/ /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
{ {
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(lhs == rhs); return !(lhs == rhs);
} }
@ -3671,76 +3815,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
friend bool operator<(const_reference lhs, const_reference rhs) noexcept friend bool operator<(const_reference lhs, const_reference rhs) noexcept
{ {
const auto lhs_type = lhs.type(); // default_result is used if we cannot compare values. In that case,
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,
// we compare types. Note we have to call the operator explicitly, // we compare types. Note we have to call the operator explicitly,
// because MSVC has problems otherwise. // 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 /// @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/ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
friend bool operator<=(const_reference lhs, const_reference rhs) noexcept friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
{ {
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(rhs < lhs); 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/ /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
friend bool operator>(const_reference lhs, const_reference rhs) noexcept friend bool operator>(const_reference lhs, const_reference rhs) noexcept
{ {
// double inverse
if (compares_unordered(lhs, rhs))
{
return false;
}
return !(lhs <= rhs); 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/ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
friend bool operator>=(const_reference lhs, const_reference rhs) noexcept friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
{ {
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(lhs < rhs); return !(lhs < rhs);
} }
@ -3835,6 +3926,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{ {
return basic_json(lhs) >= rhs; 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 @brief compare two value_t enum values
@since version 3.0.0 @since version 3.0.0
*/ */
bool operator()(nlohmann::detail::value_t lhs, bool operator()(::nlohmann::detail::value_t lhs,
nlohmann::detail::value_t rhs) const noexcept ::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}) json_test_add_test_for(${file} MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force})
endforeach() 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 # *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") 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 // 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}}}})); 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})); 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. 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 "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
using nlohmann::json; 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 namespace
{ {
// helper function to check std::less<json::value_t> // 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") 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") SECTION("types")
{ {
std::vector<json::value_t> j_types = std::vector<json::value_t> j_types =
@ -57,97 +116,268 @@ TEST_CASE("lexicographical comparison operators")
json::value_t::object, json::value_t::object,
json::value_t::array, json::value_t::array,
json::value_t::string, 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") SECTION("comparison: less")
{ {
std::vector<std::vector<bool>> expected = REQUIRE(expected_lt.size() == j_types.size());
{
{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}
};
for (size_t i = 0; i < j_types.size(); ++i) 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) for (size_t j = 0; j < j_types.size(); ++j)
{ {
CAPTURE(i) CAPTURE(i)
CAPTURE(j) CAPTURE(j)
// check precomputed values // check precomputed values
CHECK(operator<(j_types[i], j_types[j]) == expected[i][j]); #if JSON_HAS_THREE_WAY_COMPARISON
CHECK(f(j_types[i], j_types[j]) == expected[i][j]); // 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") SECTION("values")
{ {
json j_values = json j_values =
{ {
nullptr, nullptr, nullptr, nullptr, // 0 1
-17, 42, -17, 42, // 2 3
8u, 13u, 8u, 13u, // 4 5
3.14159, 23.42, 3.14159, 23.42, // 6 7
"foo", "bar", nan, nan, // 8 9
true, false, "foo", "bar", // 10 11
{1, 2, 3}, {"one", "two", "three"}, true, false, // 12 13
{{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}, {1, 2, 3}, {"one", "two", "three"}, // 14 15
json::binary({1, 2, 3}), json::binary({1, 2, 4}) {{"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 = std::vector<std::vector<bool>> expected =
{ {
{true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, //0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 0
{false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 1
{false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 2
{false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 3
{false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 4
{false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 5
{false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 6
{false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 7
{false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false}, {f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 8
{false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, {f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 9
{false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 10
{false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 11
{false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 12
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 13
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false}, {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 14
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, {f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 15
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true} {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) 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) for (size_t j = 0; j < j_values.size(); ++j)
{ {
CAPTURE(i) CAPTURE(i)
CAPTURE(j) CAPTURE(j)
CAPTURE(j_values[i]) CAPTURE(j_values[i])
CAPTURE(j_values[j]) CAPTURE(j_values[j])
// check precomputed values CHECK(json::compares_unordered(j_values[i], j_values[j], true) == expected[i][j]);
CHECK( (j_values[i] == j_values[j]) == expected[i][j] );
} }
} }
}
#endif
// comparison with discarded elements SECTION("comparison: equal")
json j_discarded(json::value_t::discarded); {
for (const auto& v : j_values) // 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); REQUIRE(expected_eq[i].size() == j_values.size());
CHECK( (j_discarded == v) == false); for (size_t j = 0; j < j_values.size(); ++j)
CHECK( (j_discarded == j_discarded) == false); {
CAPTURE(i)
CAPTURE(j)
CHECK((j_values[i] == j_values[j]) == expected_eq[i][j]);
}
} }
// compare with null pointer // compare with null pointer
@ -158,121 +388,229 @@ TEST_CASE("lexicographical comparison operators")
SECTION("comparison: not equal") 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 i = 0; i < j_values.size(); ++i)
{ {
for (size_t j = 0; j < j_values.size(); ++j) for (size_t j = 0; j < j_values.size(); ++j)
{ {
CAPTURE(i) CAPTURE(i)
CAPTURE(j) 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 // compare with null pointer
json j_null; json j_null;
CHECK( (j_null != nullptr) == false); CHECK((j_null != nullptr) == false);
CHECK( (nullptr != j_null) == false); CHECK((nullptr != j_null) == false);
CHECK( (j_null != nullptr) == !(j_null == nullptr)); CHECK((j_null != nullptr) == !(j_null == nullptr));
CHECK( (nullptr != j_null) == !(nullptr == j_null)); CHECK((nullptr != j_null) == !(nullptr == j_null));
} }
SECTION("comparison: less") SECTION("comparison: less")
{ {
std::vector<std::vector<bool>> expected = // check that two values compare less than as expected
{ REQUIRE(expected_lt.size() == j_values.size());
{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}
};
for (size_t i = 0; i < j_values.size(); ++i) 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) 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(i)
CAPTURE(j) CAPTURE(j)
CAPTURE(j_values[i]) CHECK((j_values[i] < j_values[j]) == expected_lt[i][j]);
CAPTURE(j_values[j])
// check precomputed values
CHECK( (j_values[i] < j_values[j]) == expected[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") 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 i = 0; i < j_values.size(); ++i)
{ {
for (size_t j = 0; j < j_values.size(); ++j) for (size_t j = 0; j < j_values.size(); ++j)
{ {
CAPTURE(i) CAPTURE(i)
CAPTURE(j) CAPTURE(j)
// check definition if (json::compares_unordered(j_values[i], j_values[j], true))
CHECK( (j_values[i] <= j_values[j]) == !(j_values[j] < j_values[i]) ); {
// 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") 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 i = 0; i < j_values.size(); ++i)
{ {
for (size_t j = 0; j < j_values.size(); ++j) for (size_t j = 0; j < j_values.size(); ++j)
{ {
CAPTURE(i) CAPTURE(i)
CAPTURE(j) CAPTURE(j)
// check definition if (json::compares_unordered(j_values[i], j_values[j]))
CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) ); {
// 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") 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 i = 0; i < j_values.size(); ++i)
{ {
for (size_t j = 0; j < j_values.size(); ++j) for (size_t j = 0; j < j_values.size(); ++j)
{ {
CAPTURE(i) CAPTURE(i)
CAPTURE(j) CAPTURE(j)
// check definition if (json::compares_unordered(j_values[i], j_values[j], true))
CHECK( (j_values[i] >= j_values[j]) == !(j_values[i] < j_values[j]) ); {
// if two values compare unordered,
// check that the boolean 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. 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 "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE #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 DOCTEST_CLANG_SUPPRESS_WARNING_POP

View File

@ -80,7 +80,7 @@ TEST_CASE("iterator_wrapper")
json j = { {"A", 1}, {"B", 2} }; json j = { {"A", 1}, {"B", 2} };
int counter = 1; int counter = 1;
for (auto& i : json::iterator_wrapper(j)) for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{ {
switch (counter++) switch (counter++)
{ {
@ -226,7 +226,7 @@ TEST_CASE("iterator_wrapper")
const json j = { {"A", 1}, {"B", 2} }; const json j = { {"A", 1}, {"B", 2} };
int counter = 1; int counter = 1;
for (auto& i : json::iterator_wrapper(j)) for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{ {
switch (counter++) switch (counter++)
{ {
@ -361,7 +361,7 @@ TEST_CASE("iterator_wrapper")
json j = { "A", "B" }; json j = { "A", "B" };
int counter = 1; int counter = 1;
for (auto& i : json::iterator_wrapper(j)) for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{ {
switch (counter++) switch (counter++)
{ {
@ -507,7 +507,7 @@ TEST_CASE("iterator_wrapper")
const json j = { "A", "B" }; const json j = { "A", "B" };
int counter = 1; int counter = 1;
for (auto& i : json::iterator_wrapper(j)) for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{ {
switch (counter++) switch (counter++)
{ {
@ -624,7 +624,7 @@ TEST_CASE("iterator_wrapper")
json j = 1; json j = 1;
int counter = 1; int counter = 1;
for (auto& i : json::iterator_wrapper(j)) for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{ {
++counter; ++counter;
CHECK(i.key() == ""); CHECK(i.key() == "");
@ -693,7 +693,7 @@ TEST_CASE("iterator_wrapper")
const json j = 1; const json j = 1;
int counter = 1; int counter = 1;
for (auto& i : json::iterator_wrapper(j)) for (auto& i : json::iterator_wrapper(j)) // NOLINT(readability-qualified-auto)
{ {
++counter; ++counter;
CHECK(i.key() == ""); CHECK(i.key() == "");
@ -777,7 +777,7 @@ TEST_CASE("items()")
json j = { {"A", 1}, {"B", 2} }; json j = { {"A", 1}, {"B", 2} };
int counter = 1; int counter = 1;
for (auto& i : j.items()) for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{ {
switch (counter++) switch (counter++)
{ {
@ -939,7 +939,7 @@ TEST_CASE("items()")
const json j = { {"A", 1}, {"B", 2} }; const json j = { {"A", 1}, {"B", 2} };
int counter = 1; int counter = 1;
for (auto& i : j.items()) for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{ {
switch (counter++) switch (counter++)
{ {
@ -1074,7 +1074,7 @@ TEST_CASE("items()")
json j = { "A", "B" }; json j = { "A", "B" };
int counter = 1; int counter = 1;
for (auto& i : j.items()) for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{ {
switch (counter++) switch (counter++)
{ {
@ -1220,7 +1220,7 @@ TEST_CASE("items()")
const json j = { "A", "B" }; const json j = { "A", "B" };
int counter = 1; int counter = 1;
for (auto& i : j.items()) for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{ {
switch (counter++) switch (counter++)
{ {
@ -1337,7 +1337,7 @@ TEST_CASE("items()")
json j = 1; json j = 1;
int counter = 1; int counter = 1;
for (auto& i : j.items()) for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{ {
++counter; ++counter;
CHECK(i.key() == ""); CHECK(i.key() == "");
@ -1406,7 +1406,7 @@ TEST_CASE("items()")
const json j = 1; const json j = 1;
int counter = 1; int counter = 1;
for (auto& i : j.items()) for (auto& i : j.items()) // NOLINT(readability-qualified-auto)
{ {
++counter; ++counter;
CHECK(i.key() == ""); 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_GCC_SUPPRESS_WARNING_POP
DOCTEST_CLANG_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. 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 "doctest_compatibility.h"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
using nlohmann::json; using nlohmann::json;
#if JSON_HAS_RANGES
#include <algorithm>
#include <ranges>
#endif
TEST_CASE("iterators 2") TEST_CASE("iterators 2")
{ {
SECTION("iterator comparisons") 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. 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 "doctest_compatibility.h"
// for some reason including this after the json header leads to linker errors with VS 2017... // 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 #endif
#if JSON_HAS_EXPERIMENTAL_FILESYSTEM #if JSON_HAS_EXPERIMENTAL_FILESYSTEM
// JSON_HAS_CPP_17 (magic keyword; do not remove)
#include <experimental/filesystem> #include <experimental/filesystem>
namespace nlohmann::detail namespace nlohmann::detail
{ {
@ -788,6 +794,7 @@ TEST_CASE("regression tests 2")
} }
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM #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 ") SECTION("issue #3070 - Version 3.10.3 breaks backward-compatibility with 3.10.2 ")
{ {
nlohmann::detail::std_fs::path text_path("/tmp/text.txt"); nlohmann::detail::std_fs::path text_path("/tmp/text.txt");