Skip to content

Commit

Permalink
Add C++20 3-way comparison operator and fix broken comparisons
Browse files Browse the repository at this point in the history
  • Loading branch information
falbrechtskirchinger committed May 29, 2022
1 parent 41226d0 commit 13aed5d
Show file tree
Hide file tree
Showing 11 changed files with 1,107 additions and 452 deletions.
17 changes: 13 additions & 4 deletions cmake/test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ endfunction()
#############################################################################
# json_test_add_test_for(
# <file>
# [NAME <name>]
# MAIN <main>
# [CXX_STANDARDS <version_number>...] [FORCE])
#
Expand All @@ -165,22 +166,30 @@ 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>.
# CXX_STANDARDS defaults to "11".
#############################################################################

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()
Expand Down
5 changes: 5 additions & 0 deletions docs/mkdocs/docs/api/basic_json/operator_spaceship.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# <small>nlohmann::basic_json::</small>operator<=>

## Version history

- Added in version 3.11.0.
1 change: 1 addition & 0 deletions docs/mkdocs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ 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_spaceship.md
- 'operator==': api/basic_json/operator_eq.md
- 'operator!=': api/basic_json/operator_ne.md
- 'operator<': api/basic_json/operator_lt.md
Expand Down
27 changes: 25 additions & 2 deletions include/nlohmann/detail/macro_scope.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -98,14 +104,27 @@
#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_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
Expand Down Expand Up @@ -429,3 +448,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
2 changes: 2 additions & 0 deletions include/nlohmann/detail/macro_unscope.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
30 changes: 29 additions & 1 deletion include/nlohmann/detail/value_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */,
Expand All @@ -75,7 +84,26 @@ inline bool operator<(const value_t lhs, const value_t rhs) noexcept

const auto l_index = static_cast<std::size_t>(lhs);
const auto 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 selects the built-in operator< over an operator rewritten from
// a user-defined spaceship operator
// Clang, MSVC, and ICC select the rewritten candidate
// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
{
return std::is_lt(lhs <=> rhs); // *NOPAD*
}
#endif
} // namespace detail
} // namespace nlohmann
Loading

0 comments on commit 13aed5d

Please sign in to comment.