// Copyright 2018 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // ----------------------------------------------------------------------------- // compare.h // ----------------------------------------------------------------------------- // // This header file defines the `absl::weak_equality`, `absl::strong_equality`, // `absl::partial_ordering`, `absl::weak_ordering`, and `absl::strong_ordering` // types for storing the results of three way comparisons. // // Example: // absl::weak_ordering compare(const std::string& a, const std::string& b); // // These are C++11 compatible versions of the C++20 corresponding types // (`std::weak_equality`, etc.) and are designed to be drop-in replacements // for code compliant with C++20. #ifndef ABSL_TYPES_COMPARE_H_ #define ABSL_TYPES_COMPARE_H_ #include #include #include #include #include "absl/base/attributes.h" #include "absl/meta/type_traits.h" namespace absl { namespace compare_internal { using value_type = int8_t; template struct Fail { static_assert(sizeof(T) < 0, "Only literal `0` is allowed."); }; // We need the NullPtrT template to avoid triggering the modernize-use-nullptr // ClangTidy warning in user code. template struct OnlyLiteralZero { constexpr OnlyLiteralZero(NullPtrT) noexcept {} // NOLINT // Fails compilation when `nullptr` or integral type arguments other than // `int` are passed. This constructor doesn't accept `int` because literal `0` // has type `int`. Literal `0` arguments will be implicitly converted to // `std::nullptr_t` and accepted by the above constructor, while other `int` // arguments will fail to be converted and cause compilation failure. template < typename T, typename = typename std::enable_if< std::is_same::value || (std::is_integral::value && !std::is_same::value)>::type, typename = typename Fail::type> OnlyLiteralZero(T); // NOLINT }; enum class eq : value_type { equal = 0, equivalent = equal, nonequal = 1, nonequivalent = nonequal, }; enum class ord : value_type { less = -1, greater = 1 }; enum class ncmp : value_type { unordered = -127 }; // Define macros to allow for creation or emulation of C++17 inline variables // based on whether the feature is supported. Note: we can't use // ABSL_INTERNAL_INLINE_CONSTEXPR here because the variables here are of // incomplete types so they need to be defined after the types are complete. #ifdef __cpp_inline_variables #define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) #define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) \ static const type name #define ABSL_COMPARE_INLINE_INIT(type, name, init) \ inline constexpr type type::name(init) #else // __cpp_inline_variables #define ABSL_COMPARE_INLINE_BASECLASS_DECL(name) \ ABSL_CONST_INIT static const T name #define ABSL_COMPARE_INLINE_SUBCLASS_DECL(type, name) #define ABSL_COMPARE_INLINE_INIT(type, name, init) \ template \ const T compare_internal::type##_base::name(init) #endif // __cpp_inline_variables // These template base classes allow for defining the values of the constants // in the header file (for performance) without using inline variables (which // aren't available in C++11). template struct weak_equality_base { ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent); }; template struct strong_equality_base { ABSL_COMPARE_INLINE_BASECLASS_DECL(equal); ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequal); ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent); }; template struct partial_ordering_base { ABSL_COMPARE_INLINE_BASECLASS_DECL(less); ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); ABSL_COMPARE_INLINE_BASECLASS_DECL(greater); ABSL_COMPARE_INLINE_BASECLASS_DECL(unordered); }; template struct weak_ordering_base { ABSL_COMPARE_INLINE_BASECLASS_DECL(less); ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); ABSL_COMPARE_INLINE_BASECLASS_DECL(greater); }; template struct strong_ordering_base { ABSL_COMPARE_INLINE_BASECLASS_DECL(less); ABSL_COMPARE_INLINE_BASECLASS_DECL(equal); ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); ABSL_COMPARE_INLINE_BASECLASS_DECL(greater); }; } // namespace compare_internal class weak_equality : public compare_internal::weak_equality_base { explicit constexpr weak_equality(compare_internal::eq v) noexcept : value_(static_cast(v)) {} friend struct compare_internal::weak_equality_base; public: ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, equivalent); ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, nonequivalent); // Comparisons friend constexpr bool operator==( weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ == 0; } friend constexpr bool operator!=( weak_equality v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ != 0; } friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, weak_equality v) noexcept { return 0 == v.value_; } friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, weak_equality v) noexcept { return 0 != v.value_; } private: compare_internal::value_type value_; }; ABSL_COMPARE_INLINE_INIT(weak_equality, equivalent, compare_internal::eq::equivalent); ABSL_COMPARE_INLINE_INIT(weak_equality, nonequivalent, compare_internal::eq::nonequivalent); class strong_equality : public compare_internal::strong_equality_base { explicit constexpr strong_equality(compare_internal::eq v) noexcept : value_(static_cast(v)) {} friend struct compare_internal::strong_equality_base; public: ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equal); ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequal); ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equivalent); ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequivalent); // Conversion constexpr operator weak_equality() const noexcept { // NOLINT return value_ == 0 ? weak_equality::equivalent : weak_equality::nonequivalent; } // Comparisons friend constexpr bool operator==( strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ == 0; } friend constexpr bool operator!=( strong_equality v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ != 0; } friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, strong_equality v) noexcept { return 0 == v.value_; } friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, strong_equality v) noexcept { return 0 != v.value_; } private: compare_internal::value_type value_; }; ABSL_COMPARE_INLINE_INIT(strong_equality, equal, compare_internal::eq::equal); ABSL_COMPARE_INLINE_INIT(strong_equality, nonequal, compare_internal::eq::nonequal); ABSL_COMPARE_INLINE_INIT(strong_equality, equivalent, compare_internal::eq::equivalent); ABSL_COMPARE_INLINE_INIT(strong_equality, nonequivalent, compare_internal::eq::nonequivalent); class partial_ordering : public compare_internal::partial_ordering_base { explicit constexpr partial_ordering(compare_internal::eq v) noexcept : value_(static_cast(v)) {} explicit constexpr partial_ordering(compare_internal::ord v) noexcept : value_(static_cast(v)) {} explicit constexpr partial_ordering(compare_internal::ncmp v) noexcept : value_(static_cast(v)) {} friend struct compare_internal::partial_ordering_base; constexpr bool is_ordered() const noexcept { return value_ != compare_internal::value_type(compare_internal::ncmp::unordered); } public: ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, less); ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, equivalent); ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, greater); ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, unordered); // Conversion constexpr operator weak_equality() const noexcept { // NOLINT return value_ == 0 ? weak_equality::equivalent : weak_equality::nonequivalent; } // Comparisons friend constexpr bool operator==( partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.is_ordered() && v.value_ == 0; } friend constexpr bool operator!=( partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return !v.is_ordered() || v.value_ != 0; } friend constexpr bool operator<( partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.is_ordered() && v.value_ < 0; } friend constexpr bool operator<=( partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.is_ordered() && v.value_ <= 0; } friend constexpr bool operator>( partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.is_ordered() && v.value_ > 0; } friend constexpr bool operator>=( partial_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.is_ordered() && v.value_ >= 0; } friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, partial_ordering v) noexcept { return v.is_ordered() && 0 == v.value_; } friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, partial_ordering v) noexcept { return !v.is_ordered() || 0 != v.value_; } friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, partial_ordering v) noexcept { return v.is_ordered() && 0 < v.value_; } friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, partial_ordering v) noexcept { return v.is_ordered() && 0 <= v.value_; } friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, partial_ordering v) noexcept { return v.is_ordered() && 0 > v.value_; } friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, partial_ordering v) noexcept { return v.is_ordered() && 0 >= v.value_; } private: compare_internal::value_type value_; }; ABSL_COMPARE_INLINE_INIT(partial_ordering, less, compare_internal::ord::less); ABSL_COMPARE_INLINE_INIT(partial_ordering, equivalent, compare_internal::eq::equivalent); ABSL_COMPARE_INLINE_INIT(partial_ordering, greater, compare_internal::ord::greater); ABSL_COMPARE_INLINE_INIT(partial_ordering, unordered, compare_internal::ncmp::unordered); class weak_ordering : public compare_internal::weak_ordering_base { explicit constexpr weak_ordering(compare_internal::eq v) noexcept : value_(static_cast(v)) {} explicit constexpr weak_ordering(compare_internal::ord v) noexcept : value_(static_cast(v)) {} friend struct compare_internal::weak_ordering_base; public: ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, less); ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, equivalent); ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, greater); // Conversions constexpr operator weak_equality() const noexcept { // NOLINT return value_ == 0 ? weak_equality::equivalent : weak_equality::nonequivalent; } constexpr operator partial_ordering() const noexcept { // NOLINT return value_ == 0 ? partial_ordering::equivalent : (value_ < 0 ? partial_ordering::less : partial_ordering::greater); } // Comparisons friend constexpr bool operator==( weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ == 0; } friend constexpr bool operator!=( weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ != 0; } friend constexpr bool operator<( weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ < 0; } friend constexpr bool operator<=( weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ <= 0; } friend constexpr bool operator>( weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ > 0; } friend constexpr bool operator>=( weak_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ >= 0; } friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, weak_ordering v) noexcept { return 0 == v.value_; } friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, weak_ordering v) noexcept { return 0 != v.value_; } friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, weak_ordering v) noexcept { return 0 < v.value_; } friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, weak_ordering v) noexcept { return 0 <= v.value_; } friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, weak_ordering v) noexcept { return 0 > v.value_; } friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, weak_ordering v) noexcept { return 0 >= v.value_; } private: compare_internal::value_type value_; }; ABSL_COMPARE_INLINE_INIT(weak_ordering, less, compare_internal::ord::less); ABSL_COMPARE_INLINE_INIT(weak_ordering, equivalent, compare_internal::eq::equivalent); ABSL_COMPARE_INLINE_INIT(weak_ordering, greater, compare_internal::ord::greater); class strong_ordering : public compare_internal::strong_ordering_base { explicit constexpr strong_ordering(compare_internal::eq v) noexcept : value_(static_cast(v)) {} explicit constexpr strong_ordering(compare_internal::ord v) noexcept : value_(static_cast(v)) {} friend struct compare_internal::strong_ordering_base; public: ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, less); ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equal); ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, equivalent); ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, greater); // Conversions constexpr operator weak_equality() const noexcept { // NOLINT return value_ == 0 ? weak_equality::equivalent : weak_equality::nonequivalent; } constexpr operator strong_equality() const noexcept { // NOLINT return value_ == 0 ? strong_equality::equal : strong_equality::nonequal; } constexpr operator partial_ordering() const noexcept { // NOLINT return value_ == 0 ? partial_ordering::equivalent : (value_ < 0 ? partial_ordering::less : partial_ordering::greater); } constexpr operator weak_ordering() const noexcept { // NOLINT return value_ == 0 ? weak_ordering::equivalent : (value_ < 0 ? weak_ordering::less : weak_ordering::greater); } // Comparisons friend constexpr bool operator==( strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ == 0; } friend constexpr bool operator!=( strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ != 0; } friend constexpr bool operator<( strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ < 0; } friend constexpr bool operator<=( strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ <= 0; } friend constexpr bool operator>( strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ > 0; } friend constexpr bool operator>=( strong_ordering v, compare_internal::OnlyLiteralZero<>) noexcept { return v.value_ >= 0; } friend constexpr bool operator==(compare_internal::OnlyLiteralZero<>, strong_ordering v) noexcept { return 0 == v.value_; } friend constexpr bool operator!=(compare_internal::OnlyLiteralZero<>, strong_ordering v) noexcept { return 0 != v.value_; } friend constexpr bool operator<(compare_internal::OnlyLiteralZero<>, strong_ordering v) noexcept { return 0 < v.value_; } friend constexpr bool operator<=(compare_internal::OnlyLiteralZero<>, strong_ordering v) noexcept { return 0 <= v.value_; } friend constexpr bool operator>(compare_internal::OnlyLiteralZero<>, strong_ordering v) noexcept { return 0 > v.value_; } friend constexpr bool operator>=(compare_internal::OnlyLiteralZero<>, strong_ordering v) noexcept { return 0 >= v.value_; } private: compare_internal::value_type value_; }; ABSL_COMPARE_INLINE_INIT(strong_ordering, less, compare_internal::ord::less); ABSL_COMPARE_INLINE_INIT(strong_ordering, equal, compare_internal::eq::equal); ABSL_COMPARE_INLINE_INIT(strong_ordering, equivalent, compare_internal::eq::equivalent); ABSL_COMPARE_INLINE_INIT(strong_ordering, greater, compare_internal::ord::greater); #undef ABSL_COMPARE_INLINE_BASECLASS_DECL #undef ABSL_COMPARE_INLINE_SUBCLASS_DECL #undef ABSL_COMPARE_INLINE_INIT namespace compare_internal { // We also provide these comparator adapter functions for internal absl use. // Helper functions to do a boolean comparison of two keys given a boolean // or three-way comparator. // SFINAE prevents implicit conversions to bool (such as from int). template ::value, int> = 0> constexpr bool compare_result_as_less_than(const Bool r) { return r; } constexpr bool compare_result_as_less_than(const absl::weak_ordering r) { return r < 0; } template constexpr bool do_less_than_comparison(const Compare &compare, const K &x, const LK &y) { return compare_result_as_less_than(compare(x, y)); } // Helper functions to do a three-way comparison of two keys given a boolean or // three-way comparator. // SFINAE prevents implicit conversions to int (such as from bool). template ::value, int> = 0> constexpr absl::weak_ordering compare_result_as_ordering(const Int c) { return c < 0 ? absl::weak_ordering::less : c == 0 ? absl::weak_ordering::equivalent : absl::weak_ordering::greater; } constexpr absl::weak_ordering compare_result_as_ordering( const absl::weak_ordering c) { return c; } template < typename Compare, typename K, typename LK, absl::enable_if_t>::value, int> = 0> constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare, const K &x, const LK &y) { return compare_result_as_ordering(compare(x, y)); } template < typename Compare, typename K, typename LK, absl::enable_if_t>::value, int> = 0> constexpr absl::weak_ordering do_three_way_comparison(const Compare &compare, const K &x, const LK &y) { return compare(x, y) ? absl::weak_ordering::less : compare(y, x) ? absl::weak_ordering::greater : absl::weak_ordering::equivalent; } } // namespace compare_internal } // namespace absl #endif // ABSL_TYPES_COMPARE_H_