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
strategy:
matrix:
target: [ci_test_diagnostics, ci_test_noexceptions, ci_test_noimplicitconversions]
target: [ci_test_diagnostics, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison]
steps:
- uses: actions/checkout@v3
- name: cmake

View File

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

View File

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

View File

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

View File

@ -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_ne.md) - comparison: not equal
- [**operator<**](operator_lt.md) - comparison: less than
- [**operator<=**](operator_le.md) - comparison: less than or equal
- [**operator>**](operator_gt.md) - comparison: greater than
- [**operator<=**](operator_le.md) - comparison: less than or equal
- [**operator>=**](operator_ge.md) - comparison: greater than or equal
- [**operator<=>**](operator_spaceship.md) - comparison: 3-way
### Serialization / Dumping

View File

@ -1,21 +1,31 @@
# <small>nlohmann::basic_json::</small>operator==
```cpp
bool operator==(const_reference lhs, const_reference rhs) noexcept;
// until C++20
bool operator==(const_reference lhs, const_reference rhs) noexcept; // (1)
template<typename ScalarType>
bool operator==(const_reference lhs, const ScalarType rhs) noexcept;
bool operator==(const_reference lhs, const ScalarType rhs) noexcept; // (2)
template<typename ScalarType>
bool operator==(ScalarType lhs, const const_reference rhs) noexcept;
bool operator==(ScalarType lhs, const const_reference rhs) noexcept; // (2)
// since C++20
class basic_json {
bool operator==(const_reference rhs) const noexcept; // (1)
template<typename ScalarType>
bool operator==(ScalarType rhs) const noexcept; // (2)
};
```
Compares two JSON values for equality according to the following rules:
1. Compares two JSON values for equality according to the following rules:
- Two JSON values are equal if (1) neither value is discarded, or (2) they are of the same
type and their stored values are the same according to their respective `operator==`.
- Integer and floating-point numbers are automatically converted before comparison.
- Two JSON values are equal if (1) they are not discarded, (2) they are from the same type, and (3) their stored values
are the same according to their respective `operator==`.
- Integer and floating-point numbers are automatically converted before comparison. Note that two NaN values are always
treated as unequal.
2. Compares a JSON value and a scalar or a scalar and a JSON value for equality by converting the
scalar to a JSON value and comparing both JSON values according to 1.
## Template parameters
@ -32,7 +42,7 @@ Compares two JSON values for equality according to the following rules:
## Return value
whether the values `lhs` and `rhs` are equal
whether the values `lhs`/`*this` and `rhs` are equal
## Exception safety
@ -44,13 +54,17 @@ Linear.
## 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.
- 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
`double::operator==` by default. To compare floating-point while respecting an epsilon, an alternative
@ -117,4 +131,5 @@ Linear.
## Version history
- Added in version 1.0.0.
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,10 +24,41 @@ functions [`is_null`](is_null.md), [`is_object`](is_object.md), [`is_array`](is_
## Notes
There are three enumeration entries (number_integer, number_unsigned, and number_float), because the library
distinguishes these three types for numbers: [`number_unsigned_t`](number_unsigned_t.md) is used for unsigned integers,
[`number_integer_t`](number_integer_t.md) is used for signed integers, and [`number_float_t`](number_float_t.md) is used
for floating-point numbers or to approximate integers which do not fit in the limits of their respective type.
!!! note "Ordering"
The order of types is as follows:
1. `null`
2. `boolean`
3. `number_integer`, `number_unsigned`, `number_float`
4. `object`
5. `array`
6. `string`
7. `binary`
`discarded` is unordered.
!!! note "Types of numbers"
There are three enumerators for numbers (`number_integer`, `number_unsigned`, and `number_float`) to distinguish
between different types of numbers:
- [`number_unsigned_t`](number_unsigned_t.md) for unsigned integers
- [`number_integer_t`](number_integer_t.md) for signed integers
- [`number_float_t`](number_float_t.md) for floating-point numbers or to approximate integers which do not fit
into the limits of their respective type
!!! warning "Comparison operators"
`operator<` and `operator<=>` (since C++20) are overloaded and compare according to the ordering described above.
Until C++20 all other relational and equality operators yield results according to the integer value of each
enumerator.
Since C++20 some compilers consider the _rewritten candidates_ generated from `operator<=>` during overload
resolution, while others do not.
For predictable and portable behavior use:
- `operator<` or `operator<=>` when wanting to compare according to the order described above
- `operator==` or `operator!=` when wanting to compare according to each enumerators integer value
## 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_FILESYSTEM**<br>**JSON_HAS_EXPERIMENTAL_FILESYSTEM**](json_has_filesystem.md) - control `std::filesystem` support
- [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support
- [**JSON_HAS_THREE_WAY_COMPARISON**](json_has_three_way_comparison.md) - control 3-way comparison support
- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
@ -29,6 +31,12 @@ header. See also the [macro overview page](../../features/macros.md).
- [**JSON_USE_IMPLICIT_CONVERSIONS**](json_use_implicit_conversions.md) - control implicit conversions
<!-- comment-->
## Comparison behavior
- [**JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON**](json_use_legacy_discarded_value_comparison.md) -
control comparison of discarded values
## Serialization/deserialization macros
- [**NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)**<br>**NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...)**](nlohmann_define_type_intrusive.md) - serialization/deserialization of types _with_ access to private variables

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -37,6 +37,12 @@
#define JSON_HAS_CPP_11
#endif
#ifdef __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif
#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
#ifdef JSON_HAS_CPP_17
#if defined(__cpp_lib_filesystem)
@ -98,20 +104,36 @@
#endif
#ifndef JSON_HAS_THREE_WAY_COMPARISON
#if defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L \
&& defined(__cpp_impl_three_way_comparison)&& __cpp_impl_three_way_comparison >= 201907L
#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
&& defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
#define JSON_HAS_THREE_WAY_COMPARISON 1
#else
#define JSON_HAS_THREE_WAY_COMPARISON 0
#endif
#endif
#ifndef JSON_HAS_THREE_WAY_COMPARISON
#define JSON_HAS_THREE_WAY_COMPARISON 0
#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
#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
#define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
#else
#define JSON_NO_UNIQUE_ADDRESS
#endif
// disable documentation warnings on clang
#if defined(__clang__)
#pragma clang diagnostic push
@ -429,3 +451,7 @@
#ifndef JSON_DIAGNOSTICS
#define JSON_DIAGNOSTICS 0
#endif
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
#endif

View File

@ -26,6 +26,8 @@
#undef JSON_HAS_FILESYSTEM
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
#undef JSON_HAS_THREE_WAY_COMPARISON
#undef JSON_HAS_RANGES
#undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
#endif
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>

View File

@ -136,6 +136,61 @@ using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
#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)
template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
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/cpp_future.hpp>
#include <nlohmann/detail/meta/detected.hpp>
#include <nlohmann/detail/placeholders.hpp>
#include <nlohmann/json_fwd.hpp>
namespace nlohmann
@ -178,16 +179,6 @@ using actual_object_comparator_t = typename actual_object_comparator<BasicJsonTy
// 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
// 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.
@ -564,6 +555,25 @@ struct is_ordered_map
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)
template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
T conditional_static_cast(U value)
@ -577,5 +587,17 @@ T conditional_static_cast(U 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 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 <string> // string
#include <nlohmann/detail/macro_scope.hpp>
#if JSON_HAS_THREE_WAY_COMPARISON
#include <compare> // partial_ordering
#endif
namespace nlohmann
{
namespace detail
@ -64,7 +69,11 @@ Returns an ordering that is similar to Python:
@since version 1.0.0
*/
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
#if JSON_HAS_THREE_WAY_COMPARISON
inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*
#else
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
#endif
{
static constexpr std::array<std::uint8_t, 9> order = {{
0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
@ -75,7 +84,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 r_index = static_cast<std::size_t>(rhs);
#if JSON_HAS_THREE_WAY_COMPARISON
if (l_index < order.size() && r_index < order.size())
{
return order[l_index] <=> order[r_index]; // *NOPAD*
}
return std::partial_ordering::unordered;
#else
return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
#endif
}
// GCC 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 nlohmann

View File

@ -62,6 +62,7 @@ SOFTWARE.
#include <memory> // unique_ptr
#include <numeric> // accumulate
#include <string> // string, stoi, to_string
#include <tuple> // forward_as_tuple, tuple
#include <utility> // declval, forward, move, pair, swap
#include <vector> // vector
@ -86,10 +87,12 @@ SOFTWARE.
#include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/invoke.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/output/binary_writer.hpp>
#include <nlohmann/detail/output/output_adapters.hpp>
#include <nlohmann/detail/output/serializer.hpp>
#include <nlohmann/detail/placeholders.hpp>
#include <nlohmann/detail/value_t.hpp>
#include <nlohmann/json_fwd.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
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 //
////////////////
@ -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<detail::is_basic_json<ValueType>>,
detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,
#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))
detail::negation<std::is_same<ValueType, std::string_view>>,
#endif
@ -3538,7 +3551,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @}
public:
//////////////////////////////////////////
// lexicographical comparison operators //
//////////////////////////////////////////
@ -3546,6 +3558,222 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @name lexicographical comparison operators
/// @{
// note parentheses around operands are necessary; see
// https://github.com/nlohmann/json/issues/1530
#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result) \
const auto lhs_type = lhs.type(); \
const auto rhs_type = rhs.type(); \
\
if (lhs_type == rhs_type) /* NOLINT(readability/braces) */ \
{ \
switch (lhs_type) \
{ \
case value_t::array: \
return (*lhs.m_value.array) op (*rhs.m_value.array); \
\
case value_t::object: \
return (*lhs.m_value.object) op (*rhs.m_value.object); \
\
case value_t::null: \
return (null_result); \
\
case value_t::string: \
return (*lhs.m_value.string) op (*rhs.m_value.string); \
\
case value_t::boolean: \
return (lhs.m_value.boolean) op (rhs.m_value.boolean); \
\
case value_t::number_integer: \
return (lhs.m_value.number_integer) op (rhs.m_value.number_integer); \
\
case value_t::number_unsigned: \
return (lhs.m_value.number_unsigned) op (rhs.m_value.number_unsigned); \
\
case value_t::number_float: \
return (lhs.m_value.number_float) op (rhs.m_value.number_float); \
\
case value_t::binary: \
return (*lhs.m_value.binary) op (*rhs.m_value.binary); \
\
case value_t::discarded: \
default: \
return (unordered_result); \
} \
} \
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) \
{ \
return static_cast<number_float_t>(lhs.m_value.number_integer) op rhs.m_value.number_float; \
} \
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) \
{ \
return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_integer); \
} \
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) \
{ \
return static_cast<number_float_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_float; \
} \
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) \
{ \
return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_unsigned); \
} \
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) \
{ \
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_integer; \
} \
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) \
{ \
return lhs.m_value.number_integer op static_cast<number_integer_t>(rhs.m_value.number_unsigned); \
} \
else if(compares_unordered(lhs, rhs))\
{\
return (unordered_result);\
}\
\
return (default_result);
JSON_PRIVATE_UNLESS_TESTED:
// returns true if:
// - any operand is NaN and the other operand is of number type
// - any operand is discarded
// in legacy mode, discarded values are considered ordered if
// an operation is computed as an odd number of inverses of others
static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept
{
if ((lhs.is_number_float() && std::isnan(lhs.m_value.number_float) && rhs.is_number())
|| (rhs.is_number_float() && std::isnan(rhs.m_value.number_float) && lhs.is_number()))
{
return true;
}
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;
#else
static_cast<void>(inverse);
return lhs.is_discarded() || rhs.is_discarded();
#endif
}
private:
bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept
{
return compares_unordered(*this, rhs, inverse);
}
public:
#if JSON_HAS_THREE_WAY_COMPARISON
/// @brief comparison: equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
bool operator==(const_reference rhs) const noexcept
{
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
const_reference lhs = *this;
JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
/// @brief comparison: equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
template<typename ScalarType>
requires std::is_scalar_v<ScalarType>
bool operator==(ScalarType rhs) const noexcept
{
return *this == basic_json(rhs);
}
/// @brief comparison: not equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
bool operator!=(const_reference rhs) const noexcept
{
if (compares_unordered(rhs, true))
{
return false;
}
return !operator==(rhs);
}
/// @brief comparison: 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
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
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 ignored "-Wfloat-equal"
#endif
const auto lhs_type = lhs.type();
const auto rhs_type = rhs.type();
if (lhs_type == rhs_type)
{
switch (lhs_type)
{
case value_t::array:
return *lhs.m_value.array == *rhs.m_value.array;
case value_t::object:
return *lhs.m_value.object == *rhs.m_value.object;
case value_t::null:
return true;
case value_t::string:
return *lhs.m_value.string == *rhs.m_value.string;
case value_t::boolean:
return lhs.m_value.boolean == rhs.m_value.boolean;
case value_t::number_integer:
return lhs.m_value.number_integer == rhs.m_value.number_integer;
case value_t::number_unsigned:
return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;
case value_t::number_float:
return lhs.m_value.number_float == rhs.m_value.number_float;
case value_t::binary:
return *lhs.m_value.binary == *rhs.m_value.binary;
case value_t::discarded:
default:
return false;
}
}
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;
}
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
{
return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
}
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;
}
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);
}
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
{
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;
}
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);
}
return false;
JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
@ -3646,6 +3810,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
{
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(lhs == rhs);
}
@ -3671,76 +3839,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
friend bool operator<(const_reference lhs, const_reference rhs) noexcept
{
const auto lhs_type = lhs.type();
const auto rhs_type = rhs.type();
if (lhs_type == rhs_type)
{
switch (lhs_type)
{
case value_t::array:
// note parentheses are necessary, see
// https://github.com/nlohmann/json/issues/1530
return (*lhs.m_value.array) < (*rhs.m_value.array);
case value_t::object:
return (*lhs.m_value.object) < (*rhs.m_value.object);
case value_t::null:
return false;
case value_t::string:
return (*lhs.m_value.string) < (*rhs.m_value.string);
case value_t::boolean:
return (lhs.m_value.boolean) < (rhs.m_value.boolean);
case value_t::number_integer:
return (lhs.m_value.number_integer) < (rhs.m_value.number_integer);
case value_t::number_unsigned:
return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned);
case value_t::number_float:
return (lhs.m_value.number_float) < (rhs.m_value.number_float);
case value_t::binary:
return (*lhs.m_value.binary) < (*rhs.m_value.binary);
case value_t::discarded:
default:
return false;
}
}
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;
}
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
{
return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);
}
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
{
return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
}
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);
}
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
{
return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);
}
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
{
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
}
// We only reach this line if we cannot compare values. In that case,
// default_result is used if we cannot compare values. In that case,
// we compare types. Note we have to call the operator explicitly,
// because MSVC has problems otherwise.
return operator<(lhs_type, rhs_type);
JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
}
/// @brief comparison: less than
@ -3765,6 +3867,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
{
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(rhs < lhs);
}
@ -3790,6 +3896,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
friend bool operator>(const_reference lhs, const_reference rhs) noexcept
{
// double inverse
if (compares_unordered(lhs, rhs))
{
return false;
}
return !(lhs <= rhs);
}
@ -3815,6 +3926,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
{
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(lhs < rhs);
}
@ -3835,6 +3950,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
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
/// @sa https://json.nlohmann.me/api/basic_json/to_string/
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
@since version 3.0.0
*/
bool operator()(nlohmann::detail::value_t lhs,
nlohmann::detail::value_t rhs) const noexcept
bool operator()(::nlohmann::detail::value_t lhs,
::nlohmann::detail::value_t rhs) const noexcept
{
return nlohmann::detail::operator<(lhs, rhs);
#if JSON_HAS_THREE_WAY_COMPARISON
return std::is_lt(lhs <=> rhs); // *NOPAD*
#else
return ::nlohmann::detail::operator<(lhs, rhs);
#endif
}
};

File diff suppressed because it is too large Load Diff

View File

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

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")
{
json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
json j_object = json::parse(s_object, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept
{
// filter all number(2) elements
return j != json(2);
return event != json::parse_event_t::value || j != json(2);
});
CHECK (j_object == json({{"bar", {{"baz", 1}}}}));
json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t /*unused*/, const json & j) noexcept
json j_array = json::parse(s_array, [](int /*unused*/, json::parse_event_t event, const json & j) noexcept
{
return j != json(2);
return event != json::parse_event_t::value || j != json(2);
});
CHECK (j_array == json({1, {3, 4, 5}, 4, 5}));

View File

@ -27,11 +27,49 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// cmake/test.cmake selects the C++ standard versions with which to build a
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
// When using macros that are only defined for particular versions of the standard
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
// version macro in a comment close by, like this:
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
#include <nlohmann/json.hpp>
using nlohmann::json;
#if JSON_HAS_THREE_WAY_COMPARISON
// this can be replaced with the doctest stl extension header in version 2.5
namespace doctest
{
template<> struct StringMaker<std::partial_ordering>
{
static String convert(const std::partial_ordering& order)
{
if (order == std::partial_ordering::less)
{
return "std::partial_ordering::less";
}
if (order == std::partial_ordering::equivalent)
{
return "std::partial_ordering::equivalent";
}
if (order == std::partial_ordering::greater)
{
return "std::partial_ordering::greater";
}
if (order == std::partial_ordering::unordered)
{
return "std::partial_ordering::unordered";
}
return "{?}";
}
};
} // namespace doctest
#endif
namespace
{
// helper function to check std::less<json::value_t>
@ -45,6 +83,27 @@ bool f(A a, B b, U u = U())
TEST_CASE("lexicographical comparison operators")
{
constexpr auto f_ = false;
constexpr auto _t = true;
constexpr auto nan = std::numeric_limits<json::number_float_t>::quiet_NaN();
#if JSON_HAS_THREE_WAY_COMPARISON
constexpr auto lt = std::partial_ordering::less;
constexpr auto gt = std::partial_ordering::greater;
constexpr auto eq = std::partial_ordering::equivalent;
constexpr auto un = std::partial_ordering::unordered;
#endif
#if JSON_HAS_THREE_WAY_COMPARISON
INFO("using 3-way comparison");
#endif
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
INFO("using legacy comparison");
#endif
//REQUIRE(std::numeric_limits<json::number_float_t>::has_quiet_NaN);
REQUIRE(std::isnan(nan));
SECTION("types")
{
std::vector<json::value_t> j_types =
@ -57,97 +116,265 @@ TEST_CASE("lexicographical comparison operators")
json::value_t::object,
json::value_t::array,
json::value_t::string,
json::value_t::binary
json::value_t::binary,
json::value_t::discarded
};
std::vector<std::vector<bool>> expected_lt =
{
//0 1 2 3 4 5 6 7 8 9
{f_, _t, _t, _t, _t, _t, _t, _t, _t, f_}, // 0
{f_, f_, _t, _t, _t, _t, _t, _t, _t, f_}, // 1
{f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, // 2
{f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, // 3
{f_, f_, f_, f_, f_, _t, _t, _t, _t, f_}, // 4
{f_, f_, f_, f_, f_, f_, _t, _t, _t, f_}, // 5
{f_, f_, f_, f_, f_, f_, f_, _t, _t, f_}, // 6
{f_, f_, f_, f_, f_, f_, f_, f_, _t, f_}, // 7
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 8
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 9
};
SECTION("comparison: less")
{
std::vector<std::vector<bool>> expected =
{
{false, true, true, true, true, true, true, true, true},
{false, false, true, true, true, true, true, true, true},
{false, false, false, false, false, true, true, true, true},
{false, false, false, false, false, true, true, true, true},
{false, false, false, false, false, true, true, true, true},
{false, false, false, false, false, false, true, true, true},
{false, false, false, false, false, false, false, true, true},
{false, false, false, false, false, false, false, false, true},
{false, false, false, false, false, false, false, false, false}
};
REQUIRE(expected_lt.size() == j_types.size());
for (size_t i = 0; i < j_types.size(); ++i)
{
REQUIRE(expected_lt[i].size() == j_types.size());
for (size_t j = 0; j < j_types.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
// check precomputed values
CHECK(operator<(j_types[i], j_types[j]) == expected[i][j]);
CHECK(f(j_types[i], j_types[j]) == expected[i][j]);
#if JSON_HAS_THREE_WAY_COMPARISON
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
CHECK((j_types[i] < j_types[j]) == expected_lt[i][j]);
#else
CHECK(operator<(j_types[i], j_types[j]) == expected_lt[i][j]);
#endif
CHECK(f(j_types[i], j_types[j]) == expected_lt[i][j]);
}
}
}
#if JSON_HAS_THREE_WAY_COMPARISON
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
SECTION("comparison: 3-way")
{
std::vector<std::vector<std::partial_ordering>> expected =
{
//0 1 2 3 4 5 6 7 8 9
{eq, lt, lt, lt, lt, lt, lt, lt, lt, un}, // 0
{gt, eq, lt, lt, lt, lt, lt, lt, lt, un}, // 1
{gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, // 2
{gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, // 3
{gt, gt, eq, eq, eq, lt, lt, lt, lt, un}, // 4
{gt, gt, gt, gt, gt, eq, lt, lt, lt, un}, // 5
{gt, gt, gt, gt, gt, gt, eq, lt, lt, un}, // 6
{gt, gt, gt, gt, gt, gt, gt, eq, lt, un}, // 7
{gt, gt, gt, gt, gt, gt, gt, gt, eq, un}, // 8
{un, un, un, un, un, un, un, un, un, un}, // 9
};
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")
{
json j_values =
{
nullptr, nullptr,
-17, 42,
8u, 13u,
3.14159, 23.42,
"foo", "bar",
true, false,
{1, 2, 3}, {"one", "two", "three"},
{{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}},
json::binary({1, 2, 3}), json::binary({1, 2, 4})
nullptr, nullptr, // 0 1
-17, 42, // 2 3
8u, 13u, // 4 5
3.14159, 23.42, // 6 7
nan, nan, // 8 9
"foo", "bar", // 10 11
true, false, // 12 13
{1, 2, 3}, {"one", "two", "three"}, // 14 15
{{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}, // 16 17
json::binary({1, 2, 3}), json::binary({1, 2, 4}), // 18 19
json(json::value_t::discarded), json(json::value_t::discarded) // 20 21
};
SECTION("comparison: equal")
std::vector<std::vector<bool>> expected_eq =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{_t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 0
{_t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 1
{f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 2
{f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 3
{f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 4
{f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 5
{f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 6
{f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 7
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 8
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 11
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 12
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, f_}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_}, // 19
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
};
std::vector<std::vector<bool>> expected_lt =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_}, // 0
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_}, // 1
{f_, f_, f_, _t, _t, _t, _t, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 2
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 3
{f_, f_, f_, _t, f_, _t, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 4
{f_, f_, f_, _t, f_, f_, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 5
{f_, f_, f_, _t, _t, _t, f_, _t, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 6
{f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 7
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 8
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 11
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 12
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, f_, _t, _t, _t, _t, _t, _t, f_, f_}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, _t, f_, f_, _t, _t, f_, f_}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, f_, f_, _t, _t, f_, f_}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, _t, _t, _t, f_, _t, _t, f_, f_}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, f_, f_}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 19
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
};
SECTION("compares unordered")
{
std::vector<std::vector<bool>> expected =
{
{true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true}
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 0
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 1
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 2
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 3
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 4
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 5
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 6
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 7
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 8
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 11
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 12
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, _t, _t}, // 19
{_t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t}, // 20
{_t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t, _t}, // 21
};
REQUIRE(expected.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CHECK(json::compares_unordered(j_values[i], j_values[j]) == expected[i][j]);
}
}
}
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
SECTION("compares unordered (inverse)")
{
std::vector<std::vector<bool>> expected =
{
//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 0
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 1
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 2
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 3
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 4
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 5
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 6
{f_, f_, f_, f_, f_, f_, f_, f_, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 7
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 8
{f_, f_, _t, _t, _t, _t, _t, _t, _t, _t, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 9
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 10
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 11
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 12
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 13
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 14
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 15
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 16
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 17
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 18
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 19
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 20
{f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_, f_}, // 21
};
REQUIRE(expected.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
CAPTURE(j_values[i])
CAPTURE(j_values[j])
// check precomputed values
CHECK( (j_values[i] == j_values[j]) == expected[i][j] );
CHECK(json::compares_unordered(j_values[i], j_values[j], true) == expected[i][j]);
}
}
}
#endif
// comparison with discarded elements
json j_discarded(json::value_t::discarded);
for (const auto& v : j_values)
SECTION("comparison: equal")
{
REQUIRE(expected_eq.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
CHECK( (v == j_discarded) == false);
CHECK( (j_discarded == v) == false);
CHECK( (j_discarded == j_discarded) == false);
REQUIRE(expected_eq[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i)
CAPTURE(j)
// check precomputed values
CHECK((j_values[i] == j_values[j]) == expected_eq[i][j]);
}
}
// compare with null pointer
@ -164,73 +391,41 @@ TEST_CASE("lexicographical comparison operators")
{
CAPTURE(i)
CAPTURE(j)
// check definition
CHECK( (j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]) );
if (json::compares_unordered(j_values[i], j_values[j], true))
{
CHECK_FALSE(j_values[i] != j_values[j]);
}
else
{
// check definition
CHECK((j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]));
}
}
}
// compare with null pointer
json j_null;
CHECK( (j_null != nullptr) == false);
CHECK( (nullptr != j_null) == false);
CHECK( (j_null != nullptr) == !(j_null == nullptr));
CHECK( (nullptr != j_null) == !(nullptr == j_null));
CHECK((j_null != nullptr) == false);
CHECK((nullptr != j_null) == false);
CHECK((j_null != nullptr) == !(j_null == nullptr));
CHECK((nullptr != j_null) == !(nullptr == j_null));
}
SECTION("comparison: less")
{
std::vector<std::vector<bool>> expected =
{
{false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
{false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
{false, false, false, true, true, true, true, true, true, true, false, false, true, true, true, true, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true, true, true},
{false, false, false, true, false, true, false, true, true, true, false, false, true, true, true, true, true, true},
{false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true, true, true},
{false, false, false, true, true, true, false, true, true, true, false, false, true, true, true, true, true, true},
{false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true, true, true},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true},
{false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, true},
{false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true, true, true},
{false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false, true, true},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
};
REQUIRE(expected_lt.size() == j_values.size());
for (size_t i = 0; i < j_values.size(); ++i)
{
REQUIRE(expected_lt[i].size() == j_values.size());
for (size_t j = 0; j < j_values.size(); ++j)
{
// Skip comparing indicies 12 and 13, and 13 and 12 in C++20 pending fix
// See issue #3207
#if defined(JSON_HAS_CPP_20) || JSON_HAS_THREE_WAY_COMPARISON
if ((i == 12 && j == 13) || (i == 13 && j == 12))
{
continue;
}
#endif
CAPTURE(i)
CAPTURE(j)
CAPTURE(j_values[i])
CAPTURE(j_values[j])
// check precomputed values
CHECK( (j_values[i] < j_values[j]) == expected[i][j] );
CHECK((j_values[i] < j_values[j]) == expected_lt[i][j]);
}
}
// comparison with discarded elements
json j_discarded(json::value_t::discarded);
for (size_t i = 0; i < j_values.size(); ++i)
{
CAPTURE(i)
CHECK( (j_values[i] < j_discarded) == false);
CHECK( (j_discarded < j_values[i]) == false);
CHECK( (j_discarded < j_discarded) == false);
}
}
SECTION("comparison: less than or equal equal")
@ -241,8 +436,15 @@ TEST_CASE("lexicographical comparison operators")
{
CAPTURE(i)
CAPTURE(j)
// check definition
CHECK( (j_values[i] <= j_values[j]) == !(j_values[j] < j_values[i]) );
if (json::compares_unordered(j_values[i], j_values[j], true))
{
CHECK_FALSE(j_values[i] <= j_values[j]);
}
else
{
// check definition
CHECK((j_values[i] <= j_values[j]) == !(j_values[j] < j_values[i]));
}
}
}
}
@ -255,8 +457,15 @@ TEST_CASE("lexicographical comparison operators")
{
CAPTURE(i)
CAPTURE(j)
// check definition
CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) );
if (json::compares_unordered(j_values[i], j_values[j]))
{
CHECK_FALSE(j_values[i] > j_values[j]);
}
else
{
// check definition
CHECK((j_values[i] > j_values[j]) == (j_values[j] < j_values[i]));
}
}
}
}
@ -269,10 +478,112 @@ TEST_CASE("lexicographical comparison operators")
{
CAPTURE(i)
CAPTURE(j)
// check definition
CHECK( (j_values[i] >= j_values[j]) == !(j_values[i] < j_values[j]) );
if (json::compares_unordered(j_values[i], j_values[j], true))
{
CHECK_FALSE(j_values[i] >= j_values[j]);
}
else
{
// check definition
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.
*/
// cmake/test.cmake selects the C++ standard versions with which to build a
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
// When using macros that are only defined for particular versions of the standard
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
// version macro in a comment close by, like this:
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
#include "doctest_compatibility.h"
#define JSON_TESTS_PRIVATE
@ -1582,12 +1589,4 @@ TEST_CASE("JSON to enum mapping")
}
}
#ifdef JSON_HAS_CPP_17
#undef JSON_HAS_CPP_17
#endif
#ifdef JSON_HAS_CPP_14
#undef JSON_HAS_CPP_14
#endif
DOCTEST_CLANG_SUPPRESS_WARNING_POP

View File

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

View File

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

View File

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