This commit is contained in:
Florian Albrechtskirchinger 2022-05-11 17:00:05 +02:00 committed by GitHub
commit ccd80c0bd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 3735 additions and 629 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

@ -41,6 +41,7 @@ option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enab
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_LegacyDiscardedValueComparison "Enable legacy discarded value comparison." OFF)
option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT}) option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT})
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF) option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF)
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF) option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
@ -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

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

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
@ -44,13 +54,17 @@ Linear.
## Notes ## Notes
!!! note !!! note "Comparison of 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.
!!! note !!! note "Floating-point comparison with epsilon"
Floating-point numbers inside JSON values numbers are compared with `json::number_float_t::operator==` which is Floating-point numbers inside JSON values numbers are compared with `json::number_float_t::operator==` which is
`double::operator==` by default. To compare floating-point while respecting an epsilon, an alternative `double::operator==` by default. To compare floating-point while respecting an epsilon, an alternative
@ -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 "Comparison of `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 "Comparison of `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 "Comparison of `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,22 +1,23 @@
# <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 `lhs` and `rhs` have the same type, the values are compared using the default `<` operator. - If either operand is discarded, the comparison yields `#!cpp false`.
- Integer and floating-point numbers are automatically converted before comparison - If both operands have the same type, the values are compared using their respective `operator<`.
- Discarded values a - 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 - In case `lhs` and `rhs` have different types, the values are ignored and the order of the types
is: is considered, which is:
1. null 1. null
2. boolean 2. boolean
3. number (all types) 3. number (all types)
@ -24,9 +25,11 @@ Compares whether one JSON value `lhs` is less than another JSON value `rhs` acco
5. array 5. array
6. string 6. string
7. binary 7. binary
For instance, any boolean value is considered less than any string. For instance, any boolean value is considered less than any string.
2. Compares wether a JSON value is less than a scalar or a scalar is less than a JSON value by converting
the scalar to a JSON value and comparing both JSON values according to 1.
## Template parameters ## Template parameters
`ScalarType` `ScalarType`
@ -52,6 +55,21 @@ No-throw guarantee: this function never throws exceptions.
Linear. Linear.
## Notes
!!! note "Comparison of `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 "Comparison of `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
- `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
## Version history ## Version history

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

@ -733,6 +733,28 @@ The dynamic type of the object cannot be represented in the requested serializat
Encapsulate the JSON value in an object. That is, instead of serializing `#!json true`, serialize `#!json {"value": true}` Encapsulate the JSON value in an object. That is, instead of serializing `#!json true`, serialize `#!json {"value": true}`
### json.exception.type_error.318
The given callable cannot be invoked with the stored JSON value and the arguments provided.
!!! failure "Example message"
Calling `apply()` on a numeric JSON value with an unary non-member function accepting a string:
```
[json.exception.type_error.318] cannot invoke callable with JSON value of type number
```
### json.exception.type_error.319
The result callback cannot be invoked with the callback argument, in case of a non-static member function pointer callback, and the result of the invocation of the callable.
!!! failure "Example message"
Calling `apply_cb()` with an unary non-member function result callback when the result of the invocation of the callable is not convertible to the argument type of the result callback:
```
[json.exception.type_error.319] cannot invoke callback
```
## Out of range ## Out of range
This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance in case of array indices or nonexisting object keys. This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance in case of array indices or nonexisting object keys.

View File

@ -152,15 +152,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
@ -243,6 +244,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
@ -250,6 +253,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
@ -316,5 +320,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

@ -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>
@ -32,7 +36,7 @@ template<typename IteratorType> class iteration_proxy_value
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,12 +104,27 @@
#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 #endif
#endif
#ifndef JSON_HAS_THREE_WAY_COMPARISON
#define JSON_HAS_THREE_WAY_COMPARISON 0 #define JSON_HAS_THREE_WAY_COMPARISON 0
#endif #endif
#ifndef JSON_HAS_RANGES
// ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
#if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
#define JSON_HAS_RANGES 0
#elif defined(__cpp_lib_ranges)
#define JSON_HAS_RANGES 1
#endif
#endif
#ifndef JSON_HAS_RANGES
#define JSON_HAS_RANGES 0
#endif #endif
#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) #if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
@ -112,6 +133,7 @@
#define JSON_NO_UNIQUE_ADDRESS #define JSON_NO_UNIQUE_ADDRESS
#endif #endif
// disable documentation warnings on clang // disable documentation warnings on clang
#if defined(__clang__) #if defined(__clang__)
#pragma clang diagnostic push #pragma clang diagnostic push
@ -429,3 +451,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

@ -26,6 +26,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

@ -136,6 +136,61 @@ using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
#endif #endif
#ifdef JSON_HAS_CPP_17
// the following utilities are natively available in C++17
using std::conjunction;
using std::disjunction;
using std::negation;
using std::as_const;
#else
// https://en.cppreference.com/w/cpp/types/conjunction
template<class...> struct conjunction : std::true_type {};
template<class B> struct conjunction<B> : B {};
template<class B, class... Bn>
struct conjunction<B, Bn...>
: std::conditional<bool(B::value), conjunction<Bn...>, B>::type {};
// https://en.cppreference.com/w/cpp/types/disjunction
template<class...> struct disjunction : std::false_type {};
template<class B> struct disjunction<B> : B {};
template<class B, class... Bn>
struct disjunction<B, Bn...>
: std::conditional<bool(B::value), B, disjunction<Bn...>>::type {};
// https://en.cppreference.com/w/cpp/types/negation
template<class B> struct negation : std::integral_constant < bool, !B::value > {};
// https://en.cppreference.com/w/cpp/utility/as_const
template <typename T>
constexpr typename std::add_const<T>::type& as_const(T& t) noexcept
{
return t;
}
template <typename T>
void as_const(const T&&) = delete;
#endif
#ifdef JSON_HAS_CPP_20
// the following utilities are natively available in C++20
using std::type_identity;
#else
template<typename T>
struct type_identity
{
using type = T;
};
#endif
// dispatch utility (taken from ranges-v3) // dispatch utility (taken from ranges-v3)
template<unsigned N> struct priority_tag : priority_tag < N - 1 > {}; template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
template<> struct priority_tag<0> {}; template<> struct priority_tag<0> {};

View File

@ -0,0 +1,145 @@
#pragma once
#include "nlohmann/detail/meta/cpp_future.hpp"
#ifdef JSON_HAS_CPP_17
#include <functional> // invoke
#endif
#include <type_traits> // is_base_of, is_function, is_object, is_same, remove_reference
#include <utility> // declval, forward
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
namespace nlohmann
{
namespace detail
{
// invoke_impl derived from the C++ standard draft [func.require] for qslib (proprietary)
// modified to use standard library type traits and utilities where possible
namespace internal
{
template<typename Fn, typename... Args,
typename = decltype(std::declval<Fn>()(std::forward<Args>(std::declval<Args>())...))>
auto invoke_impl(Fn && f, Args && ...args) -> decltype(f(std::forward<Args>(args)...))
{
return f(std::forward<Args>(args)...);
};
template < typename T, typename U, typename V, typename... Args, enable_if_t <
std::is_function<T>::value
&& std::is_base_of<U, typename std::remove_reference<V>::type>::value, int > = 0 >
auto invoke_impl(T U::*f, V && v, Args && ...args) -> decltype((v.*f)(std::forward<Args>(args)...))
{
return (v.*f)(std::forward<Args>(args)...);
}
template < typename T, typename U, typename V, typename... Args, enable_if_t <
std::is_function<T>::value
&& !std::is_base_of<U, typename std::remove_reference<V>::type>::value, int > = 0 >
auto invoke_impl(T U::*f, V && v, Args && ...args) -> decltype(((*v).*f)(std::forward<Args>(args)...))
{
return ((*v).*f)(std::forward<Args>(args)...);
}
template < typename T, typename U, typename V, typename... Args, enable_if_t <
std::is_object<T>::value
&& sizeof...(Args) == 0, int > = 0 >
auto invoke_impl(T U::*f, V && v, Args && ... /*unused*/) -> decltype((*v).*f)
{
return (*v).*f;
}
// allow setting values by "invoking" data member pointers with one argument
template < typename T, typename U, typename V, typename Arg, enable_if_t<
std::is_object<T>::value, int> = 0>
auto invoke_impl(T U::*f, V && v, Arg && arg) -> decltype((*v).*f = std::forward<Arg>(arg))
{
return (*v).*f = std::forward<Arg>(arg);
}
template <typename Fn, typename... Args>
using detect_invocable = decltype(invoke_impl(std::declval<Fn>(), std::declval<Args>()...));
// https://en.cppreference.com/w/cpp/types/result_of
template <typename AlwaysVoid, typename, typename...>
struct invoke_result {};
template <typename Fn, typename...Args>
struct invoke_result<decltype(void(invoke_impl(std::declval<Fn>(), std::declval<Args>()...))), Fn, Args...>
{
using type = decltype(invoke_impl(std::declval<Fn>(), std::declval<Args>()...));
};
} // namespace internal
template <typename Fn, typename... Args>
auto invoke(Fn&& f, Args&& ... args) -> decltype(internal::invoke_impl(std::forward<Fn>(f), std::forward<Args>(args)...))
{
return internal::invoke_impl(std::forward<Fn>(f), std::forward<Args>(args)...);
}
template <typename Fn, typename... Args>
using invoke_result = internal::invoke_result<void, Fn, Args...>;
template <typename Fn, typename... Args>
using is_invocable = typename is_detected<internal::detect_invocable, Fn, Args...>::type;
// used as a dummy argument
struct null_arg_t
{
explicit null_arg_t() = default;
};
static constexpr null_arg_t null_arg{};
template<typename T>
using is_null_arg = typename std::is_same<uncvref_t<T>, null_arg_t>::type;
template<typename T>
using detect_type = typename T::type;
template<typename Value, typename Arg, bool HasType = is_detected<detect_type, Arg>::value>
struct apply_value_or_value_as_if_castable
{
using type = Value;
};
template<typename Value, typename Arg>
struct apply_value_or_value_as_if_castable<Value, Arg, true>
{
using as_type = typename Arg::type;
using type = typename std::conditional <
detail::is_static_castable<Value, as_type>::value,
as_type, Value >::type;
};
template<typename Value, typename Arg>
using apply_value_or_value_as_t = typename std::conditional <
is_basic_json_value_as_placeholder<Arg>::value,
typename apply_value_or_value_as_if_castable<Value, uncvref_t<Arg>>::type,
Value >::type;
template<typename Value, typename Tuple, std::size_t I>
struct apply_value_or_arg
{
using element_type = typename std::tuple_element<I, Tuple>::type;
using type = typename std::conditional <
is_any_basic_json_value_placeholder<element_type>::value,
apply_value_or_value_as_t<Value, element_type>,
element_type >::type;
};
template<typename Value, typename Fn, typename Tuple, std::size_t... I>
using apply_invoke_result_t = typename detail::invoke_result<Fn,
typename apply_value_or_arg<Value, Tuple, I>::type...>::type;
template<typename Value, typename Fn, typename Tuple, std::size_t... I>
using apply_is_invocable = typename detail::is_invocable<Fn,
typename apply_value_or_arg<Value, Tuple, I>::type...>::type;
} // namespace detail
} // namespace nlohmann

View File

@ -12,6 +12,7 @@
#include <nlohmann/detail/meta/call_std/end.hpp> #include <nlohmann/detail/meta/call_std/end.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp> #include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/detected.hpp> #include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/detail/placeholders.hpp>
#include <nlohmann/json_fwd.hpp> #include <nlohmann/json_fwd.hpp>
namespace nlohmann namespace nlohmann
@ -178,16 +179,6 @@ using actual_object_comparator_t = typename actual_object_comparator<BasicJsonTy
// is_ functions // // is_ functions //
/////////////////// ///////////////////
// https://en.cppreference.com/w/cpp/types/conjunction
template<class...> struct conjunction : std::true_type { };
template<class B> struct conjunction<B> : B { };
template<class B, class... Bn>
struct conjunction<B, Bn...>
: std::conditional<bool(B::value), conjunction<Bn...>, B>::type {};
// https://en.cppreference.com/w/cpp/types/negation
template<class B> struct negation : std::integral_constant < bool, !B::value > { };
// Reimplementation of is_constructible and is_default_constructible, due to them being broken for // Reimplementation of is_constructible and is_default_constructible, due to them being broken for
// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). // std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
// This causes compile errors in e.g. clang 3.5 or gcc 4.9. // This causes compile errors in e.g. clang 3.5 or gcc 4.9.
@ -564,6 +555,25 @@ struct is_ordered_map
enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
}; };
// checks if a type is a basic_json_value placeholder
template<typename T>
using is_basic_json_value_placeholder =
typename std::is_same<uncvref_t<T>, ::nlohmann::placeholders::basic_json_value_placeholder_t>::type;
template<typename T>
using is_basic_json_value_as_placeholder =
typename is_specialization_of<::nlohmann::placeholders::basic_json_value_as_placeholder_t, uncvref_t<T>>::type;
template<typename T>
using is_any_basic_json_value_placeholder =
typename disjunction<is_basic_json_value_placeholder<T>, is_basic_json_value_as_placeholder<T>>::type;
template<typename From, typename To>
using detect_is_static_castable = decltype(static_cast<To>(std::declval<From>()));
template<typename From, typename To>
using is_static_castable = is_detected<detect_is_static_castable, From, To>;
// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 > template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
T conditional_static_cast(U value) T conditional_static_cast(U value)
@ -577,5 +587,17 @@ T conditional_static_cast(U value)
return value; return value;
} }
// non-standard conditional version of as_const
template <bool AsConst, typename T>
constexpr typename std::conditional<AsConst,
typename std::add_const<T>::type, T>::type&
conditional_as_const(T& t) noexcept
{
return t;
}
template <bool AsConst, typename T>
void conditional_as_const(const T&&) = delete;
} // namespace detail } // namespace detail
} // namespace nlohmann } // namespace nlohmann

View File

@ -0,0 +1,29 @@
#pragma once
namespace nlohmann
{
namespace placeholders
{
struct basic_json_value_placeholder_t
{
explicit basic_json_value_placeholder_t() = default;
};
static constexpr basic_json_value_placeholder_t basic_json_value{};
template<typename T>
struct basic_json_value_as_placeholder_t
{
using type = T;
explicit basic_json_value_as_placeholder_t() = default;
};
template<typename T>
inline constexpr basic_json_value_as_placeholder_t<T> basic_json_value_as() noexcept
{
return basic_json_value_as_placeholder_t<T> {};
}
} // namespace placeholders
} // namespace nlohmann

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
*/ */
#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 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,25 @@ 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);
return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; #if JSON_HAS_THREE_WAY_COMPARISON
if (l_index < order.size() && r_index < order.size())
{
return order[l_index] <=> order[r_index]; // *NOPAD*
} }
return std::partial_ordering::unordered;
#else
return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
#endif
}
// GCC disagrees with Clang, MSVC, and ICC; selects builtin operator over user-defined
// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
// ICC may produce incorrect result; test and add if needed
#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

@ -62,6 +62,7 @@ SOFTWARE.
#include <memory> // unique_ptr #include <memory> // unique_ptr
#include <numeric> // accumulate #include <numeric> // accumulate
#include <string> // string, stoi, to_string #include <string> // string, stoi, to_string
#include <tuple> // forward_as_tuple, tuple
#include <utility> // declval, forward, move, pair, swap #include <utility> // declval, forward, move, pair, swap
#include <vector> // vector #include <vector> // vector
@ -86,10 +87,12 @@ SOFTWARE.
#include <nlohmann/detail/string_concat.hpp> #include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/string_escape.hpp> #include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp> #include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/invoke.hpp>
#include <nlohmann/detail/meta/type_traits.hpp> #include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/output/binary_writer.hpp> #include <nlohmann/detail/output/binary_writer.hpp>
#include <nlohmann/detail/output/output_adapters.hpp> #include <nlohmann/detail/output/output_adapters.hpp>
#include <nlohmann/detail/output/serializer.hpp> #include <nlohmann/detail/output/serializer.hpp>
#include <nlohmann/detail/placeholders.hpp>
#include <nlohmann/detail/value_t.hpp> #include <nlohmann/detail/value_t.hpp>
#include <nlohmann/json_fwd.hpp> #include <nlohmann/json_fwd.hpp>
#include <nlohmann/ordered_map.hpp> #include <nlohmann/ordered_map.hpp>
@ -207,6 +210,17 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// SAX interface type, see @ref nlohmann::json_sax /// SAX interface type, see @ref nlohmann::json_sax
using json_sax_t = json_sax<basic_json>; using json_sax_t = json_sax<basic_json>;
// placeholder for the stored JSON value used in apply*() functions
static constexpr decltype(::nlohmann::placeholders::basic_json_value) value_placeholder
= ::nlohmann::placeholders::basic_json_value;
template<typename T>
static constexpr auto value_as_placeholder()
noexcept -> decltype(::nlohmann::placeholders::basic_json_value_as<T>())
{
return ::nlohmann::placeholders::basic_json_value_as<T>();
}
//////////////// ////////////////
// exceptions // // exceptions //
//////////////// ////////////////
@ -1905,7 +1919,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 +3551,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @} /// @}
public:
////////////////////////////////////////// //////////////////////////////////////////
// lexicographical comparison operators // // lexicographical comparison operators //
////////////////////////////////////////// //////////////////////////////////////////
@ -3546,6 +3558,222 @@ 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: not equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
bool operator!=(ScalarType rhs) const noexcept
{
return *this != basic_json(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 the inverse of another operation, unless
// that operation is itself computed as the inverse of yet another operation,
// 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 +3782,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 +3810,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 +3839,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 +3867,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 +3896,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 +3926,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 +3950,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
/// @} /// @}
@ -5016,8 +5134,328 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
/// @} /// @}
private:
template<typename Arg, typename Value,
detail::enable_if_t<detail::is_basic_json_value_placeholder<Arg>::value, int> = 0>
auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) const -> decltype(std::forward<Value>(val))
{
return std::forward<Value>(val);
}
template < typename Arg, typename Value, typename AsType = typename detail::uncvref_t<Arg>::type,
detail::enable_if_t < detail::is_basic_json_value_as_placeholder<Arg>::value
&& detail::is_static_castable<Value, AsType>::value, int > = 0 >
auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) const -> decltype(static_cast<AsType>(std::forward<Value>(val)))
{
return static_cast<AsType>(std::forward<Value>(val));
}
template < typename Arg, typename Value, typename AsType = typename detail::uncvref_t<Arg>::type,
detail::enable_if_t < detail::is_basic_json_value_as_placeholder<Arg>::value
&& !detail::is_static_castable<Value, AsType>::value, int > = 0 >
//static auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) -> decltype(std::forward<Value>(val))
auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) const -> decltype(std::forward<Value>(val))
{
JSON_THROW(type_error::create(399, detail::concat("cannot cast to requested type"), this));
return std::forward<Value>(val);
}
template < typename Arg, typename Value,
detail::enable_if_t < !detail::is_any_basic_json_value_placeholder<Arg>::value, int > = 0 >
auto apply_resolve_placeholder(Arg && arg, Value&& /*val*/) const -> decltype(std::forward<Arg>(arg))
{
return std::forward<Arg>(arg);
}
// invoke the result callback
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
detail::is_null_arg<ResultCallback>::value
&& detail::is_null_arg<CallbackArg>::value, int > = 0 >
void apply_invoke_cb(ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, R&& /*r*/) const
{}
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
!detail::is_null_arg<ResultCallback>::value
&& detail::is_null_arg<CallbackArg>::value
&& detail::is_invocable<ResultCallback, R>::value, int > = 0 >
void apply_invoke_cb(ResultCallback && cb, CallbackArg && /*cb_arg*/, R && r) const
{
detail::invoke(std::forward<ResultCallback>(cb), std::forward<R>(r));
}
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
!detail::is_null_arg<ResultCallback>::value
&& !detail::is_null_arg<CallbackArg>::value
&& detail::is_invocable<ResultCallback, CallbackArg, R>::value, int > = 0 >
void apply_invoke_cb(ResultCallback && cb, CallbackArg && cb_arg, R && r) const
{
detail::invoke(std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg), std::forward<R>(r));
}
template < typename ResultCallback, typename CallbackArg, typename R,
detail::enable_if_t <
(!detail::is_null_arg<ResultCallback>::value
&& !detail::is_null_arg<CallbackArg>::value
&& !detail::is_invocable<ResultCallback, CallbackArg, R>::value)
|| (!detail::is_null_arg<ResultCallback>::value
&& detail::is_null_arg<CallbackArg>::value
&& !detail::is_invocable<ResultCallback, R>::value), int > = 0 >
void apply_invoke_cb(ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, R&& /*r*/) const
{
JSON_THROW(type_error::create(319, detail::concat("cannot invoke callback"), this));
}
template<bool ConstThis>
void apply_error() const
{
if (ConstThis)
{
JSON_THROW(type_error::create(318, detail::concat("cannot invoke callable with const JSON value of type ", type_name()), this));
}
JSON_THROW(type_error::create(318, detail::concat("cannot invoke callable with JSON value of type ", type_name()), this));
}
// invoke function and possibly delegate result
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename Tuple, std::size_t... I,
detail::enable_if_t < detail::apply_is_invocable<Value, Fn, Tuple, I...>::value
&& std::is_same<detail::apply_invoke_result_t<Value, Fn, Tuple, I...>, void>::value, int > = 0 >
void apply_invoke(Value && val, ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, Fn && f, Tuple && t, detail::index_sequence<I...> /*unused*/) const
{
detail::invoke(std::forward<Fn>(f), apply_resolve_placeholder(std::get<I>(t), std::forward<Value>(val))...);
}
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename Tuple, std::size_t... I,
detail::enable_if_t < detail::apply_is_invocable<Value, Fn, Tuple, I...>::value
&& !std::is_same<detail::apply_invoke_result_t<Value, Fn, Tuple, I...>, void>::value, int > = 0 >
void apply_invoke(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Tuple && t, detail::index_sequence<I...> /*unused*/) const
{
auto&& r = detail::invoke(std::forward<Fn>(f), apply_resolve_placeholder(std::get<I>(t), std::forward<Value>(val))...);
apply_invoke_cb(std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg), std::forward<decltype(r)>(r));
}
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename Tuple, std::size_t... I,
detail::enable_if_t < !detail::apply_is_invocable<Value, Fn, Tuple, I...>::value, int > = 0 >
void apply_invoke(Value && /*val*/, ResultCallback && /*cb*/, CallbackArg && /*cb_arg*/, Fn && /*f*/, Tuple && /*t*/, detail::index_sequence<I...> /*unused*/) const
{
apply_error<ConstThis>();
}
// workaround Clang <4 and GCC <5.2 not being able to construct tuples
// of basic_json by wrapping it using std::reference_wrapper
#if (defined(__clang__) && __clang_major__ < 4) || \
(defined(__GNUC__) && !JSON_HEDLEY_GCC_VERSION_CHECK(5, 2, 0))
using apply_basic_json_needs_to_be_wrapped = std::true_type;
#else
using apply_basic_json_needs_to_be_wrapped = std::false_type;
#endif
template<typename Arg>
using apply_arg_needs_to_be_wrapped = std::integral_constant < bool,
(apply_basic_json_needs_to_be_wrapped::value
&& detail::is_basic_json<detail::uncvref_t<Arg>>::value) >;
template < typename Arg,
detail::enable_if_t < apply_arg_needs_to_be_wrapped<Arg>::value&& std::is_const<Arg>::value, int > = 0 >
static auto apply_maybe_wrap_arg(Arg && arg) -> decltype(std::cref(std::forward<Arg>(arg)))
{
return std::cref(std::forward<Arg>(arg)); // LCOV_EXCL_LINE
}
template < typename Arg,
detail::enable_if_t < apply_arg_needs_to_be_wrapped<Arg>::value&& !std::is_const<Arg>::value, int > = 0 >
static auto apply_maybe_wrap_arg(Arg && arg) -> decltype(std::ref(std::forward<Arg>(arg)))
{
return std::ref(std::forward<Arg>(arg)); // LCOV_EXCL_LINE
}
template < typename Arg,
detail::enable_if_t < !apply_arg_needs_to_be_wrapped<Arg>::value, int > = 0 >
static auto apply_maybe_wrap_arg(Arg && arg) -> decltype(std::forward<Arg>(arg))
{
return std::forward<Arg>(arg);
}
// convert arguments to tuple; insert basic_json_value placeholder if missing
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename FnArg, typename... Args,
detail::enable_if_t < std::is_member_pointer<Fn>::value
&& !detail::is_any_basic_json_value_placeholder<FnArg>::value
&& !detail::disjunction<detail::is_any_basic_json_value_placeholder<Args>...>::value, int > = 0 >
void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, FnArg && f_arg, Args && ... args) const
{
apply_invoke<ConstThis>(
std::forward<Value>(val),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward_as_tuple(f_arg, value_placeholder, apply_maybe_wrap_arg(args)...),
detail::make_index_sequence < 2 + sizeof...(args) > ());
}
template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args,
detail::enable_if_t < !std::is_member_pointer<Fn>::value
&& !detail::disjunction<detail::is_any_basic_json_value_placeholder<Args>...>::value, int > = 0 >
void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const
{
apply_invoke<ConstThis>(
std::forward<Value>(val),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward_as_tuple(value_placeholder, apply_maybe_wrap_arg(args)...),
detail::make_index_sequence < 1 + sizeof...(args) > ());
}
template<bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args,
detail::enable_if_t<detail::disjunction<detail::is_any_basic_json_value_placeholder<Args>...>::value, int> = 0>
void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const
{
apply_invoke<ConstThis>(
std::forward<Value>(val),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward_as_tuple(apply_maybe_wrap_arg(args)...),
detail::make_index_sequence<sizeof...(args)>());
}
// dispatch based on stored value type
template<bool ConstThis, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args>
void apply_dispatch(ResultCallback&& cb, CallbackArg&& cb_arg, Fn&& f, Args&& ... args) const
{
switch (m_type)
{
case value_t::null:
return apply_make_tuple<ConstThis>(nullptr,
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::object:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.object),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::array:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.array),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::string:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.string),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::boolean:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.boolean),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::number_integer:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.number_integer),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::number_unsigned:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.number_unsigned),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::number_float:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(m_value.number_float),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::binary:
return apply_make_tuple<ConstThis>(detail::conditional_as_const<ConstThis>(*m_value.binary),
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
case value_t::discarded:
return apply_error<ConstThis>();
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
public:
template<typename Fn, typename... Args>
void apply(Fn&& f, Args&& ... args)
{
apply_dispatch<false>(
detail::null_arg, detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename Fn, typename... Args>
void apply(Fn&& f, Args&& ... args) const
{
apply_dispatch<true>(
detail::null_arg, detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename ResultCallback, typename CallbackArg, typename Fn, typename... Args, detail::enable_if_t<
std::is_member_pointer<ResultCallback>::value, int> = 0>
void apply_cb(ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args)
{
apply_dispatch<false>(
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename ResultCallback, typename CallbackArg, typename Fn, typename... Args, detail::enable_if_t<
std::is_member_pointer<ResultCallback>::value, int> = 0>
void apply_cb(ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const
{
apply_dispatch<true>(
std::forward<ResultCallback>(cb), std::forward<CallbackArg>(cb_arg),
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template < typename ResultCallback, typename Fn, typename... Args, detail::enable_if_t <
!std::is_member_pointer<ResultCallback>::value, int > = 0 >
void apply_cb(ResultCallback && cb, Fn && f, Args && ... args)
{
apply_dispatch<false>(
std::forward<ResultCallback>(cb), detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template < typename ResultCallback, typename Fn, typename... Args, detail::enable_if_t <
!std::is_member_pointer<ResultCallback>::value, int > = 0 >
void apply_cb(ResultCallback && cb, Fn && f, Args && ... args) const
{
apply_dispatch<true>(
std::forward<ResultCallback>(cb), detail::null_arg,
std::forward<Fn>(f), std::forward<Args>(args)...);
}
template<typename R, typename Fn, typename... Args>
R apply_r(Fn&& f, Args&& ... args)
{
R out;
// dynamic noexcept specifier fails to compile on Clang <3.6
apply_cb([&out](R && r)
#if !defined(__clang__) || (defined(__clang__) && __clang_major__ == 3 && __clang_minor__ > 5)
noexcept(noexcept(out = std::forward<decltype(r)>(r)))
#endif
{
out = std::forward<decltype(r)>(r);
}, std::forward<Fn>(f), std::forward<Args>(args)...);
return out;
}
template<typename R, typename Fn, typename... Args>
R apply_r(Fn&& f, Args&& ... args) const
{
R out;
// dynamic noexcept specifier fails to compile on Clang <3.6
apply_cb([&out](R && r)
#if !defined(__clang__) || (defined(__clang__) && __clang_major__ == 3 && __clang_minor__ > 5)
noexcept(noexcept(out = std::forward<decltype(r)>(r)))
#endif
{
out = std::forward<decltype(r)>(r);
}, std::forward<Fn>(f), std::forward<Args>(args)...);
return out;
}
}; };
#ifndef JSON_HAS_CPP_17
NLOHMANN_BASIC_JSON_TPL_DECLARATION
constexpr decltype(::nlohmann::placeholders::basic_json_value) NLOHMANN_BASIC_JSON_TPL::value_placeholder; // NOLINT(readability-redundant-declaration)
#endif
/// @brief user-defined to_string function for JSON values /// @brief user-defined to_string function for JSON values
/// @sa https://json.nlohmann.me/api/basic_json/to_string/ /// @sa https://json.nlohmann.me/api/basic_json/to_string/
NLOHMANN_BASIC_JSON_TPL_DECLARATION NLOHMANN_BASIC_JSON_TPL_DECLARATION
@ -5054,10 +5492,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
############################################################################# #############################################################################

667
tests/src/unit-apply.cpp Normal file
View File

@ -0,0 +1,667 @@
// invoke implementation fails to compile on GCC 8.1 (MinGW)
// apply_invoke overload resolution fails for unknown reason on MSVC 2015
#if !(defined(__GNUC__) || defined(_MSC_VER)) || (defined (__GNUC__) && !(__GNUC__ == 8 && __GNUC_MINOR__ == 1)) \
|| (defined(_MSC_VER) && _MSC_VER > 1900)
#include "doctest_compatibility.h"
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-conversion")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wimplicit-int-float-conversion")
DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion")
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion")
DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-conversion")
DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion")
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
DOCTEST_MSVC_SUPPRESS_WARNING(4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data
DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <cstdint>
#if JSON_HAS_RANGES
// JSON_HAS_CPP_20 (magic keyword; do not remove)
#include <ranges>
#endif
// MSSTL defines as_const in the global namespace :facepalm:
template<typename... Args>
static auto const_(Args&& ... args) -> decltype(nlohmann::detail::as_const(std::forward<Args>(args)...))
{
return nlohmann::detail::as_const(std::forward<Args>(args)...);
}
static void array_push_back(json::array_t& arr, json val)
{
arr.emplace_back(std::move(val));
}
static void array_push_front(json val, json::array_t& arr)
{
arr.emplace(arr.begin(), std::move(val));
}
struct foo
{
int bar = 0;
void set_bar(int i) noexcept
{
bar = i;
}
static int static_bar;
static void static_set_bar(int i) noexcept
{
static_bar = i;
}
};
int foo::static_bar = 0;
struct functor
{
int arg;
int value = 0;
explicit functor(int arg_ = 0) noexcept : arg(arg_) {}
void operator()(int a, int b = 0, int c = 0) noexcept
{
switch (arg)
{
default:
case 0:
value = a;
break;
case 1:
value = b;
break;
case 2:
value = c;
break;
}
}
};
static int get_value(int i) noexcept
{
return i;
}
static int callback_value = 0;
static void callback(int i) noexcept
{
callback_value = i;
}
struct not_an_int
{
explicit not_an_int() = default;
};
static not_an_int get_not_an_int(int /*unused*/)
{
return not_an_int{};
}
TEST_CASE("apply*() functions")
{
SECTION("placeholder")
{
using nlohmann::placeholders::basic_json_value;
using nlohmann::detail::is_basic_json_value_placeholder;
CHECK(std::is_same<decltype(basic_json_value), decltype(json::value_placeholder)>::value);
CHECK(is_basic_json_value_placeholder<decltype(json::value_placeholder)>::value);
CHECK_FALSE(is_basic_json_value_placeholder<json>::value);
}
SECTION("apply()")
{
SECTION("plain function")
{
SECTION("const")
{
const json j = json::array({"foo"});
CHECK_THROWS_WITH_AS(j.apply(array_push_back, 42),
"[json.exception.type_error.318] cannot invoke callable with const JSON value of type array",
json::type_error&);
}
SECTION("non-const")
{
SECTION("without explicit placeholder")
{
json j = json::array({"foo"});
json j_expected = json::array({"foo", 42});
j.apply(array_push_back, 42);
CHECK(j == j_expected);
}
SECTION("with explicit placeholder")
{
json j = json::array({"foo"});
json j_expected = json::array({42, "foo"});
json j_expected2 = json::array({42, "foo", 24});
j.apply(array_push_front, 42, json::value_placeholder);
CHECK(j == j_expected);
j.apply(array_push_back, json::value_placeholder, 24);
CHECK(j == j_expected2);
}
}
}
SECTION("static member function pointer")
{
json j(42);
SECTION("const (without explicit placeholder)")
{
foo::static_bar = 0;
const_(j).apply(&foo::static_set_bar);
CHECK(foo::static_bar == 42);
CHECK(j == 42);
}
SECTION("const (with explicit placeholder)")
{
foo::static_bar = 0;
const_(j).apply(&foo::static_set_bar, json::value_placeholder);
CHECK(foo::static_bar == 42);
CHECK(j == 42);
}
SECTION("non-const (without explicit placeholder)")
{
foo::static_bar = 0;
j.apply(&foo::static_set_bar);
CHECK(foo::static_bar == 42);
CHECK(j == 42);
}
SECTION("non-const (with explicit placeholder)")
{
foo::static_bar = 0;
j.apply(&foo::static_set_bar, json::value_placeholder);
CHECK(foo::static_bar == 42);
CHECK(j == 42);
}
}
SECTION("non-static member function pointer")
{
json j(42);
SECTION("const (without explicit placeholder)")
{
foo f;
const_(j).apply(&foo::set_bar, f);
CHECK(f.bar == 42);
CHECK(j == 42);
}
SECTION("const (with explicit placeholder)")
{
foo f;
const_(j).apply(&foo::set_bar, f, json::value_placeholder);
CHECK(f.bar == 42);
CHECK(j == 42);
}
SECTION("non-const (without explicit placeholder)")
{
foo f;
j.apply(&foo::set_bar, f);
CHECK(f.bar == 42);
CHECK(j == 42);
}
SECTION("non-const (with explicit placeholder)")
{
foo f;
j.apply(&foo::set_bar, f, json::value_placeholder);
CHECK(f.bar == 42);
CHECK(j == 42);
}
}
SECTION("non-static function member pointer (json::array_t::resize)")
{
json j = json::array();
json j_expected = json::array({42, 42});
SECTION("const")
{
CHECK_THROWS_WITH_AS(const_(j).apply(static_cast<void (json::array_t::*)(json::array_t::size_type, const json&)>(&json::array_t::resize), json::value_placeholder, 2, json(42)),
"[json.exception.type_error.318] cannot invoke callable with const JSON value of type array",
json::type_error&);
CHECK(j.empty());
}
SECTION("non-const (without explicit placeholder)")
{
CHECK_THROWS_WITH_AS(
j.apply(static_cast<void (json::array_t::*)(json::array_t::size_type, const json&)>(&json::array_t::resize), 2, json(42)),
"[json.exception.type_error.318] cannot invoke callable with JSON value of type array",
json::type_error&);
CHECK(j.empty());
}
SECTION("non-const (with explicit placeholder)")
{
j.apply(static_cast<void (json::array_t::*)(json::array_t::size_type, const json&)>(&json::array_t::resize), json::value_placeholder, 2, json(42));
CHECK(j == j_expected);
}
}
SECTION("functor")
{
json j(42);
SECTION("const (without explicit placeholder)")
{
functor f{0};
const_(j).apply(f, -1, -2);
CHECK(f.value == 42);
CHECK(j == 42);
}
SECTION("const (with explicit placeholder)")
{
functor f{1};
const_(j).apply(f, 0, json::value_placeholder, -2);
CHECK(f.value == 42);
CHECK(j == 42);
}
SECTION("non-const (without explicit placeholder)")
{
functor f{0};
j.apply(f, -1, -2);
CHECK(f.value == 42);
CHECK(j == 42);
}
SECTION("non-const (with explicit placeholder)")
{
functor f{1};
j.apply(f, 0, json::value_placeholder, -2);
CHECK(f.value == 42);
CHECK(j == 42);
}
}
SECTION("discarded JSON value")
{
json j(json::value_t::discarded);
SECTION("const")
{
CHECK_THROWS_WITH_AS(
const_(j).apply(nlohmann::detail::null_arg),
"[json.exception.type_error.318] cannot invoke callable with const JSON value of type discarded",
json::type_error&);
}
SECTION("non-const")
{
CHECK_THROWS_WITH_AS(
j.apply(nlohmann::detail::null_arg),
"[json.exception.type_error.318] cannot invoke callable with JSON value of type discarded",
json::type_error&);
}
}
}
SECTION("apply_r()")
{
SECTION("value types")
{
SECTION("null")
{
json j;
auto is_null = [](std::nullptr_t) noexcept
{
return true;
};
SECTION("const")
{
CHECK(j.is_null());
CHECK(const_(j).apply_r<bool>(is_null));
}
SECTION("non-const")
{
CHECK(j.is_null());
CHECK(j.apply_r<bool>(is_null));
}
}
SECTION("object")
{
json j{{"foo", 0}, {"bar", 42}};
auto get_bar = [](const json::object_t& obj)
{
#if JSON_USE_IMPLICIT_CONVERSIONS
return obj.at("bar");
#else
return obj.at("bar").get<int>();
#endif
};
SECTION("const")
{
CHECK(j.is_object());
CHECK(const_(j).apply_r<int>(get_bar) == 42);
}
SECTION("non-const")
{
CHECK(j.is_object());
CHECK(j.apply_r<int>(get_bar) == 42);
}
}
SECTION("array")
{
json j{0, 1, 42, 3, 4};
auto get_2 = [](const json::array_t& arr)
{
#if JSON_USE_IMPLICIT_CONVERSIONS
return arr[2];
#else
return arr[2].get<int>();
#endif
};
SECTION("const")
{
CHECK(j.is_array());
CHECK(const_(j).apply_r<int>(get_2) == 42);
}
SECTION("non-const")
{
CHECK(j.is_array());
CHECK(j.apply_r<int>(get_2) == 42);
}
}
SECTION("string")
{
json j("fourty two");
auto length = [](const json::string_t& str) noexcept
{
return str.size();
};
SECTION("const")
{
CHECK(j.is_string());
CHECK(const_(j).apply_r<std::size_t>(length) == 10);
}
SECTION("non-const")
{
CHECK(j.is_string());
CHECK(j.apply_r<std::size_t>(length) == 10);
}
}
SECTION("boolean")
{
json j(false);
auto negate = [](bool b) noexcept
{
return !b;
};
SECTION("const")
{
CHECK(j.is_boolean());
CHECK(const_(j).apply_r<bool>(negate));
}
SECTION("non-const")
{
CHECK(j.is_boolean());
CHECK(j.apply_r<bool>(negate));
}
}
SECTION("number_integer")
{
json j(-7);
auto calc = [](json::number_integer_t i) noexcept
{
return i * i + i;
};
SECTION("const")
{
CHECK(j.is_number_integer());
CHECK(const_(j).apply_r<int>(calc) == 42);
}
SECTION("non-const")
{
CHECK(j.is_number_integer());
CHECK(j.apply_r<int>(calc) == 42);
}
}
SECTION("number_unsigned")
{
json j(static_cast<json::number_unsigned_t>(7));
auto calc = [](json::number_unsigned_t i) noexcept
{
return i * i - i;
};
SECTION("const")
{
CHECK(j.is_number_unsigned());
CHECK(const_(j).apply_r<int>(calc) == 42);
}
SECTION("non-const")
{
CHECK(j.is_number_unsigned());
CHECK(j.apply_r<int>(calc) == 42);
}
}
SECTION("number_float")
{
json j(6.480741);
auto square = [](json::number_float_t f) noexcept
{
return f * f;
};
SECTION("const")
{
CHECK(j.is_number_float());
CHECK(const_(j).apply_r<double>(square) == doctest::Approx(42.0));
}
SECTION("non-const")
{
CHECK(j.is_number_float());
CHECK(j.apply_r<double>(square) == doctest::Approx(42.0));
}
}
SECTION("binary")
{
json j = json::binary(std::vector<std::uint8_t> {0xC0, 0xFF, 0xEE});
auto get_1 = [](const json::binary_t& bin) noexcept
{
return bin[1];
};
SECTION("const")
{
CHECK(j.is_binary());
CHECK(const_(j).apply_r<std::uint8_t>(get_1) == 0xFF);
}
SECTION("non-const")
{
CHECK(j.is_binary());
CHECK(j.apply_r<std::uint8_t>(get_1) == 0xFF);
}
}
}
#if JSON_HAS_RANGES
SECTION("std::ranges::min")
{
json j = json::array({5, 3, 4, 2});
SECTION("const (without explicit placeholder)")
{
CHECK(const_(j).apply_r<int>(std::ranges::min) == 2);
}
SECTION("const (with explicit placeholder)")
{
CHECK(const_(j).apply_r<int>(std::ranges::min, json::value_placeholder) == 2);
}
SECTION("non-const (without explicit placeholder)")
{
CHECK(j.apply_r<int>(std::ranges::min) == 2);
}
SECTION("non-const (with explicit placeholder)")
{
CHECK(j.apply_r<int>(std::ranges::min, json::value_placeholder) == 2);
}
}
#endif
}
SECTION("apply_cb()")
{
SECTION("plain function callback")
{
json j(42);
SECTION("const")
{
callback_value = 0;
const_(j).apply_cb(callback, get_value);
CHECK(callback_value == 42);
}
SECTION("non-const")
{
callback_value = 0;
j.apply_cb(callback, get_value);
CHECK(callback_value == 42);
}
SECTION("exception")
{
CHECK_THROWS_WITH_AS(
j.apply_cb(callback, get_not_an_int),
"[json.exception.type_error.319] cannot invoke callback",
json::type_error&);
}
}
SECTION("static member function pointer")
{
json j(42);
SECTION("const")
{
foo::static_bar = 0;
const_(j).apply_cb(&foo::static_set_bar, get_value);
CHECK(foo::static_bar == 42);
}
SECTION("non-const")
{
foo::static_bar = 0;
j.apply_cb(&foo::static_set_bar, get_value);
CHECK(foo::static_bar == 42);
}
}
SECTION("non-static member function pointer")
{
json j(42);
SECTION("const")
{
foo f;
const_(j).apply_cb(&foo::set_bar, f, get_value);
CHECK(f.bar == 42);
}
SECTION("non-const")
{
foo f;
j.apply_cb(&foo::set_bar, f, get_value);
CHECK(f.bar == 42);
}
}
// add functor
}
}
DOCTEST_CLANG_SUPPRESS_WARNING_POP
DOCTEST_GCC_SUPPRESS_WARNING_POP
DOCTEST_MSVC_SUPPRESS_WARNING_POP
#endif

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,265 @@ 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
};
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]);
}
}
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 precomputed values
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
}; };
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
};
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( (v == j_discarded) == false); REQUIRE(expected_eq.size() == j_values.size());
CHECK( (j_discarded == v) == false); for (size_t i = 0; i < j_values.size(); ++i)
CHECK( (j_discarded == j_discarded) == false); {
REQUIRE(expected_eq[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
// check precomputed values
CHECK((j_values[i] == j_values[j]) == expected_eq[i][j]);
}
} }
// compare with null pointer // compare with null pointer
@ -164,10 +391,18 @@ TEST_CASE("lexicographical comparison operators")
{ {
CAPTURE(i) CAPTURE(i)
CAPTURE(j) CAPTURE(j)
if (json::compares_unordered(j_values[i], j_values[j], true))
{
CHECK_FALSE(j_values[i] != j_values[j]);
}
else
{
// check definition // check definition
CHECK((j_values[i] != j_values[j]) == !(j_values[i] == j_values[j])); 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;
@ -179,58 +414,18 @@ TEST_CASE("lexicographical comparison operators")
SECTION("comparison: less") SECTION("comparison: less")
{ {
std::vector<std::vector<bool>> 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])
CAPTURE(j_values[j])
// check precomputed values // check precomputed values
CHECK( (j_values[i] < j_values[j]) == expected[i][j] ); CHECK((j_values[i] < j_values[j]) == expected_lt[i][j]);
} }
} }
// comparison with discarded elements
json j_discarded(json::value_t::discarded);
for (size_t i = 0; i < j_values.size(); ++i)
{
CAPTURE(i)
CHECK( (j_values[i] < j_discarded) == false);
CHECK( (j_discarded < j_values[i]) == false);
CHECK( (j_discarded < j_discarded) == false);
}
} }
SECTION("comparison: less than or equal equal") SECTION("comparison: less than or equal equal")
@ -241,11 +436,18 @@ TEST_CASE("lexicographical comparison operators")
{ {
CAPTURE(i) CAPTURE(i)
CAPTURE(j) CAPTURE(j)
if (json::compares_unordered(j_values[i], j_values[j], true))
{
CHECK_FALSE(j_values[i] <= j_values[j]);
}
else
{
// check definition // check definition
CHECK((j_values[i] <= j_values[j]) == !(j_values[j] < j_values[i])); CHECK((j_values[i] <= j_values[j]) == !(j_values[j] < j_values[i]));
} }
} }
} }
}
SECTION("comparison: greater than") SECTION("comparison: greater than")
{ {
@ -255,11 +457,18 @@ TEST_CASE("lexicographical comparison operators")
{ {
CAPTURE(i) CAPTURE(i)
CAPTURE(j) CAPTURE(j)
if (json::compares_unordered(j_values[i], j_values[j]))
{
CHECK_FALSE(j_values[i] > j_values[j]);
}
else
{
// check definition // check definition
CHECK((j_values[i] > j_values[j]) == (j_values[j] < j_values[i])); 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")
{ {
@ -269,10 +478,112 @@ TEST_CASE("lexicographical comparison operators")
{ {
CAPTURE(i) CAPTURE(i)
CAPTURE(j) CAPTURE(j)
if (json::compares_unordered(j_values[i], j_values[j], true))
{
CHECK_FALSE(j_values[i] >= j_values[j]);
}
else
{
// check definition // check definition
CHECK((j_values[i] >= j_values[j]) == !(j_values[i] < j_values[j])); 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
};
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]);
}
}
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");