From c0694f7966e28e0f8b77f64b10a05618862e075a Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 2 Oct 2026 10:19:15 +0200 Subject: [PATCH] Make comparisons with scalars noexcept only when the conversion is The comparison operators taking a scalar (==, !=, <, <=, >, >=, and C++20's <=>) convert the scalar to a basic_json and compare, but were unconditionally noexcept. When that conversion throws, the program called std::terminate instead of propagating the exception, e.g. when comparing a json with a string literal under memory pressure (std::bad_alloc) or with an enum value not mapped by NLOHMANN_JSON_SERIALIZE_ENUM_STRICT (out_of_range.410). clang-tidy 22.1 reports the latter as bugprone-exception-escape. Declare the 16 scalar overloads noexcept(std::is_nothrow_constructible::value): they stay noexcept for numbers, Booleans, nullptr, and plain enums, and are noexcept(false) for strings and enums whose to_json may throw. The comparisons of two basic_json values are unchanged. Restore the strict-enum comparisons removed from unit-conversions.cpp in the previous PR, check that comparing an unmapped strict enum now throws, and pin the new exception specifications in unit-noexcept.cpp. Document the exception safety of overload (2) on all seven operator pages. Ran make amalgamate. Signed-off-by: Niels Lohmann --- .../mkdocs/docs/api/basic_json/operator_eq.md | 14 +++++--- .../mkdocs/docs/api/basic_json/operator_ge.md | 12 +++++-- .../mkdocs/docs/api/basic_json/operator_gt.md | 12 +++++-- .../mkdocs/docs/api/basic_json/operator_le.md | 12 +++++-- .../mkdocs/docs/api/basic_json/operator_lt.md | 12 +++++-- .../mkdocs/docs/api/basic_json/operator_ne.md | 14 +++++--- .../docs/api/basic_json/operator_spaceship.md | 10 ++++-- include/nlohmann/json.hpp | 32 +++++++++---------- single_include/nlohmann/json.hpp | 32 +++++++++---------- tests/src/unit-conversions.cpp | 21 ++++++++++++ tests/src/unit-noexcept.cpp | 9 ++++++ 11 files changed, 126 insertions(+), 54 deletions(-) diff --git a/docs/mkdocs/docs/api/basic_json/operator_eq.md b/docs/mkdocs/docs/api/basic_json/operator_eq.md index b575622d1..a7901fd42 100644 --- a/docs/mkdocs/docs/api/basic_json/operator_eq.md +++ b/docs/mkdocs/docs/api/basic_json/operator_eq.md @@ -5,17 +5,17 @@ bool operator==(const_reference lhs, const_reference rhs) noexcept; // (1) template -bool operator==(const_reference lhs, const ScalarType rhs) noexcept; // (2) +bool operator==(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2) template -bool operator==(ScalarType lhs, const const_reference rhs) noexcept; // (2) +bool operator==(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2) // since C++20 class basic_json { bool operator==(const_reference rhs) const noexcept; // (1) template - bool operator==(ScalarType rhs) const noexcept; // (2) + bool operator==(ScalarType rhs) const noexcept(/* see below */); // (2) }; ``` @@ -46,7 +46,12 @@ whether the values `lhs`/`*this` and `rhs` are equal ## Exception safety -No-throw guarantee: this function never throws exceptions. +1. No-throw guarantee: this function never throws exceptions. +2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and + `#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion + throws, for example `std::bad_alloc` when converting a string, or + [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by + [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md). ## Complexity @@ -171,3 +176,4 @@ Linear. 1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. 2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. + Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`. diff --git a/docs/mkdocs/docs/api/basic_json/operator_ge.md b/docs/mkdocs/docs/api/basic_json/operator_ge.md index 9ae6ada86..c38ca22e2 100644 --- a/docs/mkdocs/docs/api/basic_json/operator_ge.md +++ b/docs/mkdocs/docs/api/basic_json/operator_ge.md @@ -5,10 +5,10 @@ bool operator>=(const_reference lhs, const_reference rhs) noexcept; // (1) template -bool operator>=(const_reference lhs, const ScalarType rhs) noexcept; // (2) +bool operator>=(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2) template -bool operator>=(ScalarType lhs, const const_reference rhs) noexcept; // (2) +bool operator>=(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2) ``` 1. Compares whether one JSON value `lhs` is greater than or equal to another JSON value `rhs` according to the following @@ -39,7 +39,12 @@ whether `lhs` is greater than or equal to `rhs` ## Exception safety -No-throw guarantee: this function never throws exceptions. +1. No-throw guarantee: this function never throws exceptions. +2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and + `#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion + throws, for example `std::bad_alloc` when converting a string, or + [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by + [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md). ## Complexity @@ -84,3 +89,4 @@ Linear. 1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0. 2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0. + Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`. diff --git a/docs/mkdocs/docs/api/basic_json/operator_gt.md b/docs/mkdocs/docs/api/basic_json/operator_gt.md index 486da5fd0..3fcb339da 100644 --- a/docs/mkdocs/docs/api/basic_json/operator_gt.md +++ b/docs/mkdocs/docs/api/basic_json/operator_gt.md @@ -5,10 +5,10 @@ bool operator>(const_reference lhs, const_reference rhs) noexcept; // (1) template -bool operator>(const_reference lhs, const ScalarType rhs) noexcept; // (2) +bool operator>(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2) template -bool operator>(ScalarType lhs, const const_reference rhs) noexcept; // (2) +bool operator>(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2) ``` 1. Compares whether one JSON value `lhs` is greater than another JSON value `rhs` according to the @@ -39,7 +39,12 @@ whether `lhs` is greater than `rhs` ## Exception safety -No-throw guarantee: this function never throws exceptions. +1. No-throw guarantee: this function never throws exceptions. +2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and + `#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion + throws, for example `std::bad_alloc` when converting a string, or + [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by + [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md). ## Complexity @@ -84,3 +89,4 @@ Linear. 1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0. 2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0. + Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`. diff --git a/docs/mkdocs/docs/api/basic_json/operator_le.md b/docs/mkdocs/docs/api/basic_json/operator_le.md index 9dfa4e1e0..5f0a8fb0e 100644 --- a/docs/mkdocs/docs/api/basic_json/operator_le.md +++ b/docs/mkdocs/docs/api/basic_json/operator_le.md @@ -5,10 +5,10 @@ bool operator<=(const_reference lhs, const_reference rhs) noexcept; // (1) template -bool operator<=(const_reference lhs, const ScalarType rhs) noexcept; // (2) +bool operator<=(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2) template -bool operator<=(ScalarType lhs, const const_reference rhs) noexcept; // (2) +bool operator<=(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2) ``` 1. Compares whether one JSON value `lhs` is less than or equal to another JSON value `rhs` @@ -40,7 +40,12 @@ whether `lhs` is less than or equal to `rhs` ## Exception safety -No-throw guarantee: this function never throws exceptions. +1. No-throw guarantee: this function never throws exceptions. +2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and + `#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion + throws, for example `std::bad_alloc` when converting a string, or + [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by + [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md). ## Complexity @@ -85,3 +90,4 @@ Linear. 1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0. 2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0. + Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`. diff --git a/docs/mkdocs/docs/api/basic_json/operator_lt.md b/docs/mkdocs/docs/api/basic_json/operator_lt.md index 118d817c8..1b8e65225 100644 --- a/docs/mkdocs/docs/api/basic_json/operator_lt.md +++ b/docs/mkdocs/docs/api/basic_json/operator_lt.md @@ -5,10 +5,10 @@ bool operator<(const_reference lhs, const_reference rhs) noexcept; // (1) template -bool operator<(const_reference lhs, const ScalarType rhs) noexcept; // (2) +bool operator<(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2) template -bool operator<(ScalarType lhs, const const_reference rhs) noexcept; // (2) +bool operator<(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2) ``` 1. Compares whether one JSON value `lhs` is less than another JSON value `rhs` according to the @@ -49,7 +49,12 @@ whether `lhs` is less than `rhs` ## Exception safety -No-throw guarantee: this function never throws exceptions. +1. No-throw guarantee: this function never throws exceptions. +2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and + `#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion + throws, for example `std::bad_alloc` when converting a string, or + [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by + [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md). ## Complexity @@ -94,3 +99,4 @@ Linear. 1. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0. 2. Added in version 1.0.0. Conditionally removed since C++20 in version 3.11.0. + Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`. diff --git a/docs/mkdocs/docs/api/basic_json/operator_ne.md b/docs/mkdocs/docs/api/basic_json/operator_ne.md index 5abb4a5af..391079f98 100644 --- a/docs/mkdocs/docs/api/basic_json/operator_ne.md +++ b/docs/mkdocs/docs/api/basic_json/operator_ne.md @@ -5,17 +5,17 @@ bool operator!=(const_reference lhs, const_reference rhs) noexcept; // (1) template -bool operator!=(const_reference lhs, const ScalarType rhs) noexcept; // (2) +bool operator!=(const_reference lhs, const ScalarType rhs) noexcept(/* see below */); // (2) template -bool operator!=(ScalarType lhs, const const_reference rhs) noexcept; // (2) +bool operator!=(ScalarType lhs, const const_reference rhs) noexcept(/* see below */); // (2) // since C++20 class basic_json { bool operator!=(const_reference rhs) const noexcept; // (1) template - bool operator!=(ScalarType rhs) const noexcept; // (2) + bool operator!=(ScalarType rhs) const noexcept(/* see below */); // (2) }; ``` @@ -44,7 +44,12 @@ whether the values `lhs`/`*this` and `rhs` are not equal ## Exception safety -No-throw guarantee: this function never throws exceptions. +1. No-throw guarantee: this function never throws exceptions. +2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and + `#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion + throws, for example `std::bad_alloc` when converting a string, or + [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by + [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md). ## Complexity @@ -95,3 +100,4 @@ Linear. special-casing for `NaN` and `discarded` values; `operator!=` now consistently means `!(a == b)`. 2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. Changed in version 3.13.0 to remove special-casing for `NaN` and `discarded` values; `operator!=` now consistently means `!(a == b)`. + Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`. diff --git a/docs/mkdocs/docs/api/basic_json/operator_spaceship.md b/docs/mkdocs/docs/api/basic_json/operator_spaceship.md index 9e91d0d2d..47ca22484 100644 --- a/docs/mkdocs/docs/api/basic_json/operator_spaceship.md +++ b/docs/mkdocs/docs/api/basic_json/operator_spaceship.md @@ -6,7 +6,7 @@ class basic_json { std::partial_ordering operator<=>(const_reference rhs) const noexcept; // (1) template - std::partial_ordering operator<=>(const ScalarType rhs) const noexcept; // (2) + std::partial_ordering operator<=>(const ScalarType rhs) const noexcept(/* see below */); // (2) }; ``` @@ -39,7 +39,12 @@ the `std::partial_ordering` of the 3-way comparison of `*this` and `rhs` ## Exception safety -No-throw guarantee: this function never throws exceptions. +1. No-throw guarantee: this function never throws exceptions. +2. No-throw guarantee if converting the scalar to a JSON value cannot throw, as for numbers, Booleans, and + `#!cpp nullptr`; the function is `#!cpp noexcept` exactly in that case. Otherwise, it throws what the conversion + throws, for example `std::bad_alloc` when converting a string, or + [`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) for an enum value not mapped by + [`NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`](../macros/nlohmann_json_serialize_enum_strict.md). ## Complexity @@ -98,3 +103,4 @@ Linear. 1. Added in version 3.11.0. 2. Added in version 3.11.0. + Made conditionally `#!cpp noexcept` in version 3.13.0; before, a throwing conversion called `std::terminate`. diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index bbe310d9b..cc61e05c7 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -4865,7 +4865,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ template requires std::is_scalar_v - bool operator==(ScalarType rhs) const noexcept + bool operator==(ScalarType rhs) const noexcept(std::is_nothrow_constructible::value) { return *this == basic_json(rhs); } @@ -4888,7 +4888,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/ template requires std::is_scalar_v - std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD* + std::partial_ordering operator<=>(ScalarType rhs) const noexcept(std::is_nothrow_constructible::value) // *NOPAD* { return *this <=> basic_json(rhs); // *NOPAD* } @@ -4913,7 +4913,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ template requires std::is_scalar_v - bool operator<=(ScalarType rhs) const noexcept + bool operator<=(ScalarType rhs) const noexcept(std::is_nothrow_constructible::value) { return *this <= basic_json(rhs); } @@ -4934,7 +4934,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ template requires std::is_scalar_v - bool operator>=(ScalarType rhs) const noexcept + bool operator>=(ScalarType rhs) const noexcept(std::is_nothrow_constructible::value) { return *this >= basic_json(rhs); } @@ -4959,7 +4959,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ template::value, int>::type = 0> - friend bool operator==(const_reference lhs, ScalarType rhs) noexcept + friend bool operator==(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs == basic_json(rhs); } @@ -4968,7 +4968,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ template::value, int>::type = 0> - friend bool operator==(ScalarType lhs, const_reference rhs) noexcept + friend bool operator==(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) == rhs; } @@ -4984,7 +4984,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ template::value, int>::type = 0> - friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept + friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs != basic_json(rhs); } @@ -4993,7 +4993,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ template::value, int>::type = 0> - friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept + friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) != rhs; } @@ -5013,7 +5013,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ template::value, int>::type = 0> - friend bool operator<(const_reference lhs, ScalarType rhs) noexcept + friend bool operator<(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs < basic_json(rhs); } @@ -5022,7 +5022,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ template::value, int>::type = 0> - friend bool operator<(ScalarType lhs, const_reference rhs) noexcept + friend bool operator<(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) < rhs; } @@ -5042,7 +5042,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ template::value, int>::type = 0> - friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept + friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs <= basic_json(rhs); } @@ -5051,7 +5051,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ template::value, int>::type = 0> - friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) <= rhs; } @@ -5072,7 +5072,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ template::value, int>::type = 0> - friend bool operator>(const_reference lhs, ScalarType rhs) noexcept + friend bool operator>(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs > basic_json(rhs); } @@ -5081,7 +5081,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ template::value, int>::type = 0> - friend bool operator>(ScalarType lhs, const_reference rhs) noexcept + friend bool operator>(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) > rhs; } @@ -5101,7 +5101,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ template::value, int>::type = 0> - friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept + friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs >= basic_json(rhs); } @@ -5110,7 +5110,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ template::value, int>::type = 0> - friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) >= rhs; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 73079da57..643120f45 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -31562,7 +31562,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ template requires std::is_scalar_v - bool operator==(ScalarType rhs) const noexcept + bool operator==(ScalarType rhs) const noexcept(std::is_nothrow_constructible::value) { return *this == basic_json(rhs); } @@ -31585,7 +31585,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/ template requires std::is_scalar_v - std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD* + std::partial_ordering operator<=>(ScalarType rhs) const noexcept(std::is_nothrow_constructible::value) // *NOPAD* { return *this <=> basic_json(rhs); // *NOPAD* } @@ -31610,7 +31610,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ template requires std::is_scalar_v - bool operator<=(ScalarType rhs) const noexcept + bool operator<=(ScalarType rhs) const noexcept(std::is_nothrow_constructible::value) { return *this <= basic_json(rhs); } @@ -31631,7 +31631,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ template requires std::is_scalar_v - bool operator>=(ScalarType rhs) const noexcept + bool operator>=(ScalarType rhs) const noexcept(std::is_nothrow_constructible::value) { return *this >= basic_json(rhs); } @@ -31656,7 +31656,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ template::value, int>::type = 0> - friend bool operator==(const_reference lhs, ScalarType rhs) noexcept + friend bool operator==(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs == basic_json(rhs); } @@ -31665,7 +31665,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ template::value, int>::type = 0> - friend bool operator==(ScalarType lhs, const_reference rhs) noexcept + friend bool operator==(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) == rhs; } @@ -31681,7 +31681,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ template::value, int>::type = 0> - friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept + friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs != basic_json(rhs); } @@ -31690,7 +31690,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ template::value, int>::type = 0> - friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept + friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) != rhs; } @@ -31710,7 +31710,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ template::value, int>::type = 0> - friend bool operator<(const_reference lhs, ScalarType rhs) noexcept + friend bool operator<(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs < basic_json(rhs); } @@ -31719,7 +31719,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ template::value, int>::type = 0> - friend bool operator<(ScalarType lhs, const_reference rhs) noexcept + friend bool operator<(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) < rhs; } @@ -31739,7 +31739,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ template::value, int>::type = 0> - friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept + friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs <= basic_json(rhs); } @@ -31748,7 +31748,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ template::value, int>::type = 0> - friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) <= rhs; } @@ -31769,7 +31769,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ template::value, int>::type = 0> - friend bool operator>(const_reference lhs, ScalarType rhs) noexcept + friend bool operator>(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs > basic_json(rhs); } @@ -31778,7 +31778,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ template::value, int>::type = 0> - friend bool operator>(ScalarType lhs, const_reference rhs) noexcept + friend bool operator>(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) > rhs; } @@ -31798,7 +31798,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ template::value, int>::type = 0> - friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept + friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept(std::is_nothrow_constructible::value) { return lhs >= basic_json(rhs); } @@ -31807,7 +31807,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ template::value, int>::type = 0> - friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) >= rhs; } diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions.cpp index ff6e5e5c7..df8685529 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions.cpp @@ -1764,6 +1764,12 @@ TEST_CASE("Strict JSON to enum mapping") CHECK(json("herz").get() == strict_cards::herz); CHECK(json("karo").get() == strict_cards::karo); + // comparison of enum and json + CHECK(strict_cards::kreuz == json("kreuz")); + CHECK(strict_cards::pik == json("pik")); + CHECK(strict_cards::herz == json("herz")); + CHECK(strict_cards::karo == json("karo")); + // invalid json -> exception thrown json _; CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"what?\"", json::out_of_range&); @@ -1771,6 +1777,12 @@ TEST_CASE("Strict JSON to enum mapping") // conversion of unmapped enum -> exception thrown CHECK_THROWS_WITH_AS(json(strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); + // comparing an unmapped enum with json throws the same exception + // (the scalar comparison operators used to be noexcept, so this + // called std::terminate) + CHECK_THROWS_WITH_AS(static_cast(strict_cards::andere == json("andere")), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); + CHECK_THROWS_WITH_AS(static_cast(json("andere") != strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); + // invalid UTF-8 -> out_of_range.410, not the type_error.316 thrown while building the // message (regression test for #5667); such strings can reach get() unvalidated, // e.g. from from_cbor()/from_msgpack() (#5529) @@ -1792,12 +1804,21 @@ TEST_CASE("Strict JSON to enum mapping") CHECK(json("completed").get() == STRICT_TS_COMPLETED); CHECK(json().get() == STRICT_TS_INVALID); + // comparison of enum and json + CHECK(STRICT_TS_STOPPED == json("stopped")); + CHECK(STRICT_TS_RUNNING == json("running")); + CHECK(STRICT_TS_COMPLETED == json("completed")); + CHECK(STRICT_TS_INVALID == json()); + // invalid json -> exception thrown json _; CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState: \"what?\"", json::out_of_range&); // conversion of unmapped enum -> exception thrown CHECK_THROWS_WITH_AS(json(STRICT_TS_OTHER), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&); + + // comparing an unmapped enum with json throws the same exception + CHECK_THROWS_WITH_AS(static_cast(STRICT_TS_OTHER < json("x")), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&); } } diff --git a/tests/src/unit-noexcept.cpp b/tests/src/unit-noexcept.cpp index 637915f24..85361df24 100644 --- a/tests/src/unit-noexcept.cpp +++ b/tests/src/unit-noexcept.cpp @@ -54,6 +54,15 @@ static_assert(noexcept(json(pod {})), ""); static_assert(noexcept(std::declval().get()), ""); static_assert(!noexcept(std::declval().get()), ""); static_assert(noexcept(json(pod{})), ""); + +// comparing with a scalar is noexcept exactly when converting the scalar is +static_assert(noexcept(std::declval() == 1), ""); +static_assert(noexcept(1 != std::declval()), ""); +static_assert(noexcept(std::declval() < 2.5), ""); +static_assert(noexcept(nullptr == std::declval()), ""); +static_assert(!noexcept(std::declval() == "foo"), ""); +static_assert(!noexcept("foo" >= std::declval()), ""); +static_assert(noexcept(std::declval() == std::declval()), ""); } // namespace TEST_CASE("noexcept")