mirror of
https://github.com/nlohmann/json.git
synced 2025-07-28 12:02:00 +03:00
Fix C++20/gcc-12 issues (Part 2) (#3446)
* Add C++20 3-way comparison operator and fix broken comparisons Fixes #3207. Fixes #3409. * Fix iterators to meet (more) std::ranges requirements Fixes #3130. Related discussion: #3408 * Add note about CMake standard version selection to unit tests Document how CMake chooses which C++ standard version to use when building tests. * Update documentation * CI: add legacy discarded value comparison * Fix internal linkage errors when building a module
This commit is contained in:
committed by
GitHub
parent
ede6667858
commit
6b97599a27
@ -1905,7 +1905,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
detail::negation<std::is_same<ValueType, typename string_t::value_type>>,
|
||||
detail::negation<detail::is_basic_json<ValueType>>,
|
||||
detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,
|
||||
|
||||
#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))
|
||||
detail::negation<std::is_same<ValueType, std::string_view>>,
|
||||
#endif
|
||||
@ -3538,7 +3537,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
|
||||
/// @}
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////
|
||||
// lexicographical comparison operators //
|
||||
//////////////////////////////////////////
|
||||
@ -3546,6 +3544,212 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @name lexicographical comparison operators
|
||||
/// @{
|
||||
|
||||
// note parentheses around operands are necessary; see
|
||||
// https://github.com/nlohmann/json/issues/1530
|
||||
#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result) \
|
||||
const auto lhs_type = lhs.type(); \
|
||||
const auto rhs_type = rhs.type(); \
|
||||
\
|
||||
if (lhs_type == rhs_type) /* NOLINT(readability/braces) */ \
|
||||
{ \
|
||||
switch (lhs_type) \
|
||||
{ \
|
||||
case value_t::array: \
|
||||
return (*lhs.m_value.array) op (*rhs.m_value.array); \
|
||||
\
|
||||
case value_t::object: \
|
||||
return (*lhs.m_value.object) op (*rhs.m_value.object); \
|
||||
\
|
||||
case value_t::null: \
|
||||
return (null_result); \
|
||||
\
|
||||
case value_t::string: \
|
||||
return (*lhs.m_value.string) op (*rhs.m_value.string); \
|
||||
\
|
||||
case value_t::boolean: \
|
||||
return (lhs.m_value.boolean) op (rhs.m_value.boolean); \
|
||||
\
|
||||
case value_t::number_integer: \
|
||||
return (lhs.m_value.number_integer) op (rhs.m_value.number_integer); \
|
||||
\
|
||||
case value_t::number_unsigned: \
|
||||
return (lhs.m_value.number_unsigned) op (rhs.m_value.number_unsigned); \
|
||||
\
|
||||
case value_t::number_float: \
|
||||
return (lhs.m_value.number_float) op (rhs.m_value.number_float); \
|
||||
\
|
||||
case value_t::binary: \
|
||||
return (*lhs.m_value.binary) op (*rhs.m_value.binary); \
|
||||
\
|
||||
case value_t::discarded: \
|
||||
default: \
|
||||
return (unordered_result); \
|
||||
} \
|
||||
} \
|
||||
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) \
|
||||
{ \
|
||||
return static_cast<number_float_t>(lhs.m_value.number_integer) op rhs.m_value.number_float; \
|
||||
} \
|
||||
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) \
|
||||
{ \
|
||||
return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_integer); \
|
||||
} \
|
||||
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) \
|
||||
{ \
|
||||
return static_cast<number_float_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_float; \
|
||||
} \
|
||||
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) \
|
||||
{ \
|
||||
return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_unsigned); \
|
||||
} \
|
||||
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) \
|
||||
{ \
|
||||
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_integer; \
|
||||
} \
|
||||
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) \
|
||||
{ \
|
||||
return lhs.m_value.number_integer op static_cast<number_integer_t>(rhs.m_value.number_unsigned); \
|
||||
} \
|
||||
else if(compares_unordered(lhs, rhs))\
|
||||
{\
|
||||
return (unordered_result);\
|
||||
}\
|
||||
\
|
||||
return (default_result);
|
||||
|
||||
JSON_PRIVATE_UNLESS_TESTED:
|
||||
// returns true if:
|
||||
// - any operand is NaN and the other operand is of number type
|
||||
// - any operand is discarded
|
||||
// in legacy mode, discarded values are considered ordered if
|
||||
// an operation is computed as an odd number of inverses of others
|
||||
static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept
|
||||
{
|
||||
if ((lhs.is_number_float() && std::isnan(lhs.m_value.number_float) && rhs.is_number())
|
||||
|| (rhs.is_number_float() && std::isnan(rhs.m_value.number_float) && lhs.is_number()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;
|
||||
#else
|
||||
static_cast<void>(inverse);
|
||||
return lhs.is_discarded() || rhs.is_discarded();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept
|
||||
{
|
||||
return compares_unordered(*this, rhs, inverse);
|
||||
}
|
||||
|
||||
public:
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
/// @brief comparison: equal
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
|
||||
bool operator==(const_reference rhs) const noexcept
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
const_reference lhs = *this;
|
||||
JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief comparison: equal
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
|
||||
template<typename ScalarType>
|
||||
requires std::is_scalar_v<ScalarType>
|
||||
bool operator==(ScalarType rhs) const noexcept
|
||||
{
|
||||
return *this == basic_json(rhs);
|
||||
}
|
||||
|
||||
/// @brief comparison: not equal
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
||||
bool operator!=(const_reference rhs) const noexcept
|
||||
{
|
||||
if (compares_unordered(rhs, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
/// @brief comparison: 3-way
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
|
||||
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
|
||||
{
|
||||
const_reference lhs = *this;
|
||||
// default_result is used if we cannot compare values. In that case,
|
||||
// we compare types.
|
||||
JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
|
||||
std::partial_ordering::equivalent,
|
||||
std::partial_ordering::unordered,
|
||||
lhs_type <=> rhs_type) // *NOPAD*
|
||||
}
|
||||
|
||||
/// @brief comparison: 3-way
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
|
||||
template<typename ScalarType>
|
||||
requires std::is_scalar_v<ScalarType>
|
||||
std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*
|
||||
{
|
||||
return *this <=> basic_json(rhs); // *NOPAD*
|
||||
}
|
||||
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
// all operators that are computed as an odd number of inverses of others
|
||||
// need to be overloaded to emulate the legacy comparison behavior
|
||||
|
||||
/// @brief comparison: less than or equal
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
|
||||
bool operator<=(const_reference rhs) const noexcept
|
||||
{
|
||||
if (compares_unordered(rhs, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !(rhs < *this);
|
||||
}
|
||||
|
||||
/// @brief comparison: less than or equal
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
|
||||
template<typename ScalarType>
|
||||
requires std::is_scalar_v<ScalarType>
|
||||
bool operator<=(ScalarType rhs) const noexcept
|
||||
{
|
||||
return *this <= basic_json(rhs);
|
||||
}
|
||||
|
||||
/// @brief comparison: greater than or equal
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
|
||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
|
||||
bool operator>=(const_reference rhs) const noexcept
|
||||
{
|
||||
if (compares_unordered(rhs, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !(*this < rhs);
|
||||
}
|
||||
|
||||
/// @brief comparison: greater than or equal
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
|
||||
template<typename ScalarType>
|
||||
requires std::is_scalar_v<ScalarType>
|
||||
bool operator>=(ScalarType rhs) const noexcept
|
||||
{
|
||||
return *this >= basic_json(rhs);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
/// @brief comparison: equal
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
|
||||
friend bool operator==(const_reference lhs, const_reference rhs) noexcept
|
||||
@ -3554,71 +3758,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
const auto lhs_type = lhs.type();
|
||||
const auto rhs_type = rhs.type();
|
||||
|
||||
if (lhs_type == rhs_type)
|
||||
{
|
||||
switch (lhs_type)
|
||||
{
|
||||
case value_t::array:
|
||||
return *lhs.m_value.array == *rhs.m_value.array;
|
||||
|
||||
case value_t::object:
|
||||
return *lhs.m_value.object == *rhs.m_value.object;
|
||||
|
||||
case value_t::null:
|
||||
return true;
|
||||
|
||||
case value_t::string:
|
||||
return *lhs.m_value.string == *rhs.m_value.string;
|
||||
|
||||
case value_t::boolean:
|
||||
return lhs.m_value.boolean == rhs.m_value.boolean;
|
||||
|
||||
case value_t::number_integer:
|
||||
return lhs.m_value.number_integer == rhs.m_value.number_integer;
|
||||
|
||||
case value_t::number_unsigned:
|
||||
return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;
|
||||
|
||||
case value_t::number_float:
|
||||
return lhs.m_value.number_float == rhs.m_value.number_float;
|
||||
|
||||
case value_t::binary:
|
||||
return *lhs.m_value.binary == *rhs.m_value.binary;
|
||||
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
|
||||
{
|
||||
return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;
|
||||
}
|
||||
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
|
||||
{
|
||||
return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
|
||||
}
|
||||
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
|
||||
{
|
||||
return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;
|
||||
}
|
||||
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
|
||||
{
|
||||
return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);
|
||||
}
|
||||
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
|
||||
{
|
||||
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;
|
||||
}
|
||||
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
|
||||
{
|
||||
return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);
|
||||
}
|
||||
|
||||
return false;
|
||||
JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
@ -3646,6 +3786,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
||||
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
if (compares_unordered(lhs, rhs, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
@ -3671,76 +3815,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
|
||||
friend bool operator<(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
const auto lhs_type = lhs.type();
|
||||
const auto rhs_type = rhs.type();
|
||||
|
||||
if (lhs_type == rhs_type)
|
||||
{
|
||||
switch (lhs_type)
|
||||
{
|
||||
case value_t::array:
|
||||
// note parentheses are necessary, see
|
||||
// https://github.com/nlohmann/json/issues/1530
|
||||
return (*lhs.m_value.array) < (*rhs.m_value.array);
|
||||
|
||||
case value_t::object:
|
||||
return (*lhs.m_value.object) < (*rhs.m_value.object);
|
||||
|
||||
case value_t::null:
|
||||
return false;
|
||||
|
||||
case value_t::string:
|
||||
return (*lhs.m_value.string) < (*rhs.m_value.string);
|
||||
|
||||
case value_t::boolean:
|
||||
return (lhs.m_value.boolean) < (rhs.m_value.boolean);
|
||||
|
||||
case value_t::number_integer:
|
||||
return (lhs.m_value.number_integer) < (rhs.m_value.number_integer);
|
||||
|
||||
case value_t::number_unsigned:
|
||||
return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned);
|
||||
|
||||
case value_t::number_float:
|
||||
return (lhs.m_value.number_float) < (rhs.m_value.number_float);
|
||||
|
||||
case value_t::binary:
|
||||
return (*lhs.m_value.binary) < (*rhs.m_value.binary);
|
||||
|
||||
case value_t::discarded:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)
|
||||
{
|
||||
return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;
|
||||
}
|
||||
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)
|
||||
{
|
||||
return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);
|
||||
}
|
||||
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)
|
||||
{
|
||||
return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;
|
||||
}
|
||||
else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)
|
||||
{
|
||||
return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);
|
||||
}
|
||||
else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)
|
||||
{
|
||||
return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);
|
||||
}
|
||||
else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)
|
||||
{
|
||||
return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;
|
||||
}
|
||||
|
||||
// We only reach this line if we cannot compare values. In that case,
|
||||
// default_result is used if we cannot compare values. In that case,
|
||||
// we compare types. Note we have to call the operator explicitly,
|
||||
// because MSVC has problems otherwise.
|
||||
return operator<(lhs_type, rhs_type);
|
||||
JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
|
||||
}
|
||||
|
||||
/// @brief comparison: less than
|
||||
@ -3765,6 +3843,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
|
||||
friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
if (compares_unordered(lhs, rhs, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
@ -3790,6 +3872,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
|
||||
friend bool operator>(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
// double inverse
|
||||
if (compares_unordered(lhs, rhs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !(lhs <= rhs);
|
||||
}
|
||||
|
||||
@ -3815,6 +3902,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
|
||||
friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
if (compares_unordered(lhs, rhs, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
@ -3835,6 +3926,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
{
|
||||
return basic_json(lhs) >= rhs;
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef JSON_IMPLEMENT_OPERATOR
|
||||
|
||||
/// @}
|
||||
|
||||
@ -5031,10 +5125,14 @@ struct less< ::nlohmann::detail::value_t> // do not remove the space after '<',
|
||||
@brief compare two value_t enum values
|
||||
@since version 3.0.0
|
||||
*/
|
||||
bool operator()(nlohmann::detail::value_t lhs,
|
||||
nlohmann::detail::value_t rhs) const noexcept
|
||||
bool operator()(::nlohmann::detail::value_t lhs,
|
||||
::nlohmann::detail::value_t rhs) const noexcept
|
||||
{
|
||||
return nlohmann::detail::operator<(lhs, rhs);
|
||||
#if JSON_HAS_THREE_WAY_COMPARISON
|
||||
return std::is_lt(lhs <=> rhs); // *NOPAD*
|
||||
#else
|
||||
return ::nlohmann::detail::operator<(lhs, rhs);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user