diff options
Diffstat (limited to 'third_party/abseil_cpp/absl/types/internal/conformance_profile.h')
-rw-r--r-- | third_party/abseil_cpp/absl/types/internal/conformance_profile.h | 931 |
1 files changed, 931 insertions, 0 deletions
diff --git a/third_party/abseil_cpp/absl/types/internal/conformance_profile.h b/third_party/abseil_cpp/absl/types/internal/conformance_profile.h new file mode 100644 index 000000000000..cf64ff4fcd1e --- /dev/null +++ b/third_party/abseil_cpp/absl/types/internal/conformance_profile.h @@ -0,0 +1,931 @@ +// Copyright 2019 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. +// +// ----------------------------------------------------------------------------- +// conformance_profiles.h +// ----------------------------------------------------------------------------- +// +// This file contains templates for representing "Regularity Profiles" and +// concisely-named versions of commonly used Regularity Profiles. +// +// A Regularity Profile is a compile-time description of the types of operations +// that a given type supports, along with properties of those operations when +// they do exist. For instance, a Regularity Profile may describe a type that +// has a move-constructor that is noexcept and a copy constructor that is not +// noexcept. This description can then be examined and passed around to other +// templates for the purposes of asserting expectations on user-defined types +// via a series trait checks, or for determining what kinds of run-time tests +// are able to be performed. +// +// Regularity Profiles are also used when creating "archetypes," which are +// minimum-conforming types that meet all of the requirements of a given +// Regularity Profile. For more information regarding archetypes, see +// "conformance_archetypes.h". + +#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_ +#define ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_ + +#include <set> +#include <type_traits> +#include <utility> +#include <vector> + +#include "gtest/gtest.h" +#include "absl/algorithm/container.h" +#include "absl/meta/type_traits.h" +#include "absl/strings/ascii.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/types/internal/conformance_testing_helpers.h" +#include "absl/utility/utility.h" + +// TODO(calabrese) Add support for extending profiles. + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace types_internal { + +// Converts an enum to its underlying integral value. +template <typename Enum> +constexpr absl::underlying_type_t<Enum> UnderlyingValue(Enum value) { + return static_cast<absl::underlying_type_t<Enum>>(value); +} + +// A tag type used in place of a matcher when checking that an assertion result +// does not actually contain any errors. +struct NoError {}; + +// ----------------------------------------------------------------------------- +// ConformanceErrors +// ----------------------------------------------------------------------------- +class ConformanceErrors { + public: + // Setup the error reporting mechanism by seeding it with the name of the type + // that is being tested. + explicit ConformanceErrors(std::string type_name) + : assertion_result_(false), type_name_(std::move(type_name)) { + assertion_result_ << "\n\n" + "Assuming the following type alias:\n" + "\n" + " using _T = " + << type_name_ << ";\n\n"; + outputDivider(); + } + + // Adds the test name to the list of successfully run tests iff it was not + // previously reported as failing. This behavior is useful for tests that + // have multiple parts, where failures and successes are reported individually + // with the same test name. + void addTestSuccess(absl::string_view test_name) { + auto normalized_test_name = absl::AsciiStrToLower(test_name); + + // If the test is already reported as failing, do not add it to the list of + // successes. + if (test_failures_.find(normalized_test_name) == test_failures_.end()) { + test_successes_.insert(std::move(normalized_test_name)); + } + } + + // Streams a single error description into the internal buffer (a visual + // divider is automatically inserted after the error so that multiple errors + // are visibly distinct). + // + // This function increases the error count by 1. + // + // TODO(calabrese) Determine desired behavior when if this function throws. + template <class... P> + void addTestFailure(absl::string_view test_name, const P&... args) { + // Output a message related to the test failure. + assertion_result_ << "\n\n" + "Failed test: " + << test_name << "\n\n"; + addTestFailureImpl(args...); + assertion_result_ << "\n\n"; + outputDivider(); + + auto normalized_test_name = absl::AsciiStrToLower(test_name); + + // If previous parts of this test succeeded, remove it from that set. + test_successes_.erase(normalized_test_name); + + // Add the test name to the list of failed tests. + test_failures_.insert(std::move(normalized_test_name)); + + has_error_ = true; + } + + // Convert this object into a testing::AssertionResult instance such that it + // can be used with gtest. + ::testing::AssertionResult assertionResult() const { + return has_error_ ? assertion_result_ : ::testing::AssertionSuccess(); + } + + // Convert this object into a testing::AssertionResult instance such that it + // can be used with gtest. This overload expects errors, using the specified + // matcher. + ::testing::AssertionResult expectFailedTests( + const std::set<std::string>& test_names) const { + // Since we are expecting nonconformance, output an error message when the + // type actually conformed to the specified profile. + if (!has_error_) { + return ::testing::AssertionFailure() + << "Unexpected conformance of type:\n" + " " + << type_name_ << "\n\n"; + } + + // Get a list of all expected failures that did not actually fail + // (or that were not run). + std::vector<std::string> nonfailing_tests; + absl::c_set_difference(test_names, test_failures_, + std::back_inserter(nonfailing_tests)); + + // Get a list of all "expected failures" that were never actually run. + std::vector<std::string> unrun_tests; + absl::c_set_difference(nonfailing_tests, test_successes_, + std::back_inserter(unrun_tests)); + + // Report when the user specified tests that were not run. + if (!unrun_tests.empty()) { + const bool tests_were_run = + !(test_failures_.empty() && test_successes_.empty()); + + // Prepare an assertion result used in the case that tests pass that were + // expected to fail. + ::testing::AssertionResult result = ::testing::AssertionFailure(); + result << "When testing type:\n " << type_name_ + << "\n\nThe following tests were expected to fail but were not " + "run"; + + if (tests_were_run) result << " (was the test name spelled correctly?)"; + + result << ":\n\n"; + + // List all of the tests that unexpectedly passed. + for (const auto& test_name : unrun_tests) { + result << " " << test_name << "\n"; + } + + if (!tests_were_run) result << "\nNo tests were run."; + + if (!test_failures_.empty()) { + // List test failures + result << "\nThe tests that were run and failed are:\n\n"; + for (const auto& test_name : test_failures_) { + result << " " << test_name << "\n"; + } + } + + if (!test_successes_.empty()) { + // List test successes + result << "\nThe tests that were run and succeeded are:\n\n"; + for (const auto& test_name : test_successes_) { + result << " " << test_name << "\n"; + } + } + + return result; + } + + // If some tests passed when they were expected to fail, alert the caller. + if (nonfailing_tests.empty()) return ::testing::AssertionSuccess(); + + // Prepare an assertion result used in the case that tests pass that were + // expected to fail. + ::testing::AssertionResult unexpected_successes = + ::testing::AssertionFailure(); + unexpected_successes << "When testing type:\n " << type_name_ + << "\n\nThe following tests passed when they were " + "expected to fail:\n\n"; + + // List all of the tests that unexpectedly passed. + for (const auto& test_name : nonfailing_tests) { + unexpected_successes << " " << test_name << "\n"; + } + + return unexpected_successes; + } + + private: + void outputDivider() { + assertion_result_ << "========================================"; + } + + void addTestFailureImpl() {} + + template <class H, class... T> + void addTestFailureImpl(const H& head, const T&... tail) { + assertion_result_ << head; + addTestFailureImpl(tail...); + } + + ::testing::AssertionResult assertion_result_; + std::set<std::string> test_failures_; + std::set<std::string> test_successes_; + std::string type_name_; + bool has_error_ = false; +}; + +template <class T, class /*Enabler*/ = void> +struct PropertiesOfImpl {}; + +template <class T> +struct PropertiesOfImpl<T, absl::void_t<typename T::properties>> { + using type = typename T::properties; +}; + +template <class T> +struct PropertiesOfImpl<T, absl::void_t<typename T::profile_alias_of>> { + using type = typename PropertiesOfImpl<typename T::profile_alias_of>::type; +}; + +template <class T> +struct PropertiesOf : PropertiesOfImpl<T> {}; + +template <class T> +using PropertiesOfT = typename PropertiesOf<T>::type; + +// NOTE: These enums use this naming convention to be consistent with the +// standard trait names, which is useful since it allows us to match up each +// enum name with a corresponding trait name in macro definitions. + +// An enum that describes the various expectations on an operations existence. +enum class function_support { maybe, yes, nothrow, trivial }; + +constexpr const char* PessimisticPropertyDescription(function_support v) { + return v == function_support::maybe + ? "no" + : v == function_support::yes + ? "yes, potentially throwing" + : v == function_support::nothrow ? "yes, nothrow" + : "yes, trivial"; +} + +// Return a string that describes the kind of property support that was +// expected. +inline std::string ExpectedFunctionKindList(function_support min, + function_support max) { + if (min == max) { + std::string result = + absl::StrCat("Expected:\n ", + PessimisticPropertyDescription( + static_cast<function_support>(UnderlyingValue(min))), + "\n"); + return result; + } + + std::string result = "Expected one of:\n"; + for (auto curr_support = UnderlyingValue(min); + curr_support <= UnderlyingValue(max); ++curr_support) { + absl::StrAppend(&result, " ", + PessimisticPropertyDescription( + static_cast<function_support>(curr_support)), + "\n"); + } + + return result; +} + +template <class Enum> +void ExpectModelOfImpl(ConformanceErrors* errors, Enum min_support, + Enum max_support, Enum kind) { + const auto kind_value = UnderlyingValue(kind); + const auto min_support_value = UnderlyingValue(min_support); + const auto max_support_value = UnderlyingValue(max_support); + + if (!(kind_value >= min_support_value && kind_value <= max_support_value)) { + errors->addTestFailure( + PropertyName(kind), "**Failed property expectation**\n\n", + ExpectedFunctionKindList( + static_cast<function_support>(min_support_value), + static_cast<function_support>(max_support_value)), + '\n', "Actual:\n ", + PessimisticPropertyDescription( + static_cast<function_support>(kind_value))); + } else { + errors->addTestSuccess(PropertyName(kind)); + } +} + +#define ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM(description, name) \ + enum class name { maybe, yes, nothrow, trivial }; \ + \ + constexpr const char* PropertyName(name v) { return description; } \ + static_assert(true, "") // Force a semicolon when using this macro. + +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for default construction", + default_constructible); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for move construction", + move_constructible); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for copy construction", + copy_constructible); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for move assignment", + move_assignable); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for copy assignment", + copy_assignable); +ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM("support for destruction", + destructible); + +#undef ABSL_INTERNAL_SPECIAL_MEMBER_FUNCTION_ENUM + +#define ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM(description, name) \ + enum class name { maybe, yes, nothrow }; \ + \ + constexpr const char* PropertyName(name v) { return description; } \ + static_assert(true, "") // Force a semicolon when using this macro. + +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for ==", equality_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for !=", inequality_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for <", less_than_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for <=", less_equal_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for >=", + greater_equal_comparable); +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for >", greater_than_comparable); + +ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM("support for swap", swappable); + +#undef ABSL_INTERNAL_INTRINSIC_FUNCTION_ENUM + +enum class hashable { maybe, yes }; + +constexpr const char* PropertyName(hashable v) { + return "support for std::hash"; +} + +template <class T> +using AlwaysFalse = std::false_type; + +#define ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(name, property) \ + template <class T> \ + constexpr property property##_support_of() { \ + return std::is_##property<T>::value \ + ? std::is_nothrow_##property<T>::value \ + ? absl::is_trivially_##property<T>::value \ + ? property::trivial \ + : property::nothrow \ + : property::yes \ + : property::maybe; \ + } \ + \ + template <class T, class MinProf, class MaxProf> \ + void ExpectModelOf##name(ConformanceErrors* errors) { \ + (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::property##_support, \ + PropertiesOfT<MaxProf>::property##_support, \ + property##_support_of<T>()); \ + } + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(DefaultConstructible, + default_constructible); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(MoveConstructible, + move_constructible); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(CopyConstructible, + copy_constructible); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(MoveAssignable, + move_assignable); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(CopyAssignable, + copy_assignable); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER(Destructible, destructible); + +#undef ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_SPECIAL_MEMBER + +void BoolFunction(bool) noexcept; + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction for checking if an operation exists through SFINAE. +// +// `T` is the type to test and Op is an alias containing the expression to test. +template <class T, template <class...> class Op, class = void> +struct IsOpableImpl : std::false_type {}; + +template <class T, template <class...> class Op> +struct IsOpableImpl<T, Op, absl::void_t<Op<T>>> : std::true_type {}; + +template <template <class...> class Op> +struct IsOpable { + template <class T> + using apply = typename IsOpableImpl<T, Op>::type; +}; +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// A metafunction for checking if an operation exists and is also noexcept +// through SFINAE and the noexcept operator. +/// +// `T` is the type to test and Op is an alias containing the expression to test. +template <class T, template <class...> class Op, class = void> +struct IsNothrowOpableImpl : std::false_type {}; + +template <class T, template <class...> class Op> +struct IsNothrowOpableImpl<T, Op, absl::enable_if_t<Op<T>::value>> + : std::true_type {}; + +template <template <class...> class Op> +struct IsNothrowOpable { + template <class T> + using apply = typename IsNothrowOpableImpl<T, Op>::type; +}; +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// A macro that produces the necessary function for reporting what kind of +// support a specific comparison operation has and a function for reporting an +// error if a given type's support for that operation does not meet the expected +// requirements. +#define ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(name, property, op) \ + template <class T, \ + class Result = std::integral_constant< \ + bool, noexcept((BoolFunction)(std::declval<const T&>() op \ + std::declval<const T&>()))>> \ + using name = Result; \ + \ + template <class T> \ + constexpr property property##_support_of() { \ + return IsOpable<name>::apply<T>::value \ + ? IsNothrowOpable<name>::apply<T>::value ? property::nothrow \ + : property::yes \ + : property::maybe; \ + } \ + \ + template <class T, class MinProf, class MaxProf> \ + void ExpectModelOf##name(ConformanceErrors* errors) { \ + (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::property##_support, \ + PropertiesOfT<MaxProf>::property##_support, \ + property##_support_of<T>()); \ + } +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// Generate the necessary support-checking and error reporting functions for +// each of the comparison operators. +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(EqualityComparable, + equality_comparable, ==); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(InequalityComparable, + inequality_comparable, !=); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(LessThanComparable, + less_than_comparable, <); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(LessEqualComparable, + less_equal_comparable, <=); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(GreaterEqualComparable, + greater_equal_comparable, >=); + +ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON(GreaterThanComparable, + greater_than_comparable, >); + +#undef ABSL_INTERNAL_PESSIMISTIC_MODEL_OF_COMPARISON +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// The necessary support-checking and error-reporting functions for swap. +template <class T> +constexpr swappable swappable_support_of() { + return type_traits_internal::IsSwappable<T>::value + ? type_traits_internal::IsNothrowSwappable<T>::value + ? swappable::nothrow + : swappable::yes + : swappable::maybe; +} + +template <class T, class MinProf, class MaxProf> +void ExpectModelOfSwappable(ConformanceErrors* errors) { + (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::swappable_support, + PropertiesOfT<MaxProf>::swappable_support, + swappable_support_of<T>()); +} +// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// +// The necessary support-checking and error-reporting functions for std::hash. +template <class T> +constexpr hashable hashable_support_of() { + return type_traits_internal::IsHashable<T>::value ? hashable::yes + : hashable::maybe; +} + +template <class T, class MinProf, class MaxProf> +void ExpectModelOfHashable(ConformanceErrors* errors) { + (ExpectModelOfImpl)(errors, PropertiesOfT<MinProf>::hashable_support, + PropertiesOfT<MaxProf>::hashable_support, + hashable_support_of<T>()); +} +// +//////////////////////////////////////////////////////////////////////////////// + +template < + default_constructible DefaultConstructibleValue = + default_constructible::maybe, + move_constructible MoveConstructibleValue = move_constructible::maybe, + copy_constructible CopyConstructibleValue = copy_constructible::maybe, + move_assignable MoveAssignableValue = move_assignable::maybe, + copy_assignable CopyAssignableValue = copy_assignable::maybe, + destructible DestructibleValue = destructible::maybe, + equality_comparable EqualityComparableValue = equality_comparable::maybe, + inequality_comparable InequalityComparableValue = + inequality_comparable::maybe, + less_than_comparable LessThanComparableValue = less_than_comparable::maybe, + less_equal_comparable LessEqualComparableValue = + less_equal_comparable::maybe, + greater_equal_comparable GreaterEqualComparableValue = + greater_equal_comparable::maybe, + greater_than_comparable GreaterThanComparableValue = + greater_than_comparable::maybe, + swappable SwappableValue = swappable::maybe, + hashable HashableValue = hashable::maybe> +struct ConformanceProfile { + using properties = ConformanceProfile; + + static constexpr default_constructible + default_constructible_support = // NOLINT + DefaultConstructibleValue; + + static constexpr move_constructible move_constructible_support = // NOLINT + MoveConstructibleValue; + + static constexpr copy_constructible copy_constructible_support = // NOLINT + CopyConstructibleValue; + + static constexpr move_assignable move_assignable_support = // NOLINT + MoveAssignableValue; + + static constexpr copy_assignable copy_assignable_support = // NOLINT + CopyAssignableValue; + + static constexpr destructible destructible_support = // NOLINT + DestructibleValue; + + static constexpr equality_comparable equality_comparable_support = // NOLINT + EqualityComparableValue; + + static constexpr inequality_comparable + inequality_comparable_support = // NOLINT + InequalityComparableValue; + + static constexpr less_than_comparable + less_than_comparable_support = // NOLINT + LessThanComparableValue; + + static constexpr less_equal_comparable + less_equal_comparable_support = // NOLINT + LessEqualComparableValue; + + static constexpr greater_equal_comparable + greater_equal_comparable_support = // NOLINT + GreaterEqualComparableValue; + + static constexpr greater_than_comparable + greater_than_comparable_support = // NOLINT + GreaterThanComparableValue; + + static constexpr swappable swappable_support = SwappableValue; // NOLINT + + static constexpr hashable hashable_support = HashableValue; // NOLINT + + static constexpr bool is_default_constructible = // NOLINT + DefaultConstructibleValue != default_constructible::maybe; + + static constexpr bool is_move_constructible = // NOLINT + MoveConstructibleValue != move_constructible::maybe; + + static constexpr bool is_copy_constructible = // NOLINT + CopyConstructibleValue != copy_constructible::maybe; + + static constexpr bool is_move_assignable = // NOLINT + MoveAssignableValue != move_assignable::maybe; + + static constexpr bool is_copy_assignable = // NOLINT + CopyAssignableValue != copy_assignable::maybe; + + static constexpr bool is_destructible = // NOLINT + DestructibleValue != destructible::maybe; + + static constexpr bool is_equality_comparable = // NOLINT + EqualityComparableValue != equality_comparable::maybe; + + static constexpr bool is_inequality_comparable = // NOLINT + InequalityComparableValue != inequality_comparable::maybe; + + static constexpr bool is_less_than_comparable = // NOLINT + LessThanComparableValue != less_than_comparable::maybe; + + static constexpr bool is_less_equal_comparable = // NOLINT + LessEqualComparableValue != less_equal_comparable::maybe; + + static constexpr bool is_greater_equal_comparable = // NOLINT + GreaterEqualComparableValue != greater_equal_comparable::maybe; + + static constexpr bool is_greater_than_comparable = // NOLINT + GreaterThanComparableValue != greater_than_comparable::maybe; + + static constexpr bool is_swappable = // NOLINT + SwappableValue != swappable::maybe; + + static constexpr bool is_hashable = // NOLINT + HashableValue != hashable::maybe; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// Compliant SFINAE-friendliness is not always present on the standard library +// implementations that we support. This helper-struct (and associated enum) is +// used as a means to conditionally check the hashability support of a type. +enum class CheckHashability { no, yes }; + +template <class T, CheckHashability ShouldCheckHashability> +struct conservative_hashable_support_of; + +template <class T> +struct conservative_hashable_support_of<T, CheckHashability::no> { + static constexpr hashable Invoke() { return hashable::maybe; } +}; + +template <class T> +struct conservative_hashable_support_of<T, CheckHashability::yes> { + static constexpr hashable Invoke() { return hashable_support_of<T>(); } +}; +// +//////////////////////////////////////////////////////////////////////////////// + +// The ConformanceProfile that is expected based on introspection into the type +// by way of trait checks. +template <class T, CheckHashability ShouldCheckHashability> +struct SyntacticConformanceProfileOf { + using properties = ConformanceProfile< + default_constructible_support_of<T>(), move_constructible_support_of<T>(), + copy_constructible_support_of<T>(), move_assignable_support_of<T>(), + copy_assignable_support_of<T>(), destructible_support_of<T>(), + equality_comparable_support_of<T>(), + inequality_comparable_support_of<T>(), + less_than_comparable_support_of<T>(), + less_equal_comparable_support_of<T>(), + greater_equal_comparable_support_of<T>(), + greater_than_comparable_support_of<T>(), swappable_support_of<T>(), + conservative_hashable_support_of<T, ShouldCheckHashability>::Invoke()>; +}; + +#define ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(type, name) \ + template <default_constructible DefaultConstructibleValue, \ + move_constructible MoveConstructibleValue, \ + copy_constructible CopyConstructibleValue, \ + move_assignable MoveAssignableValue, \ + copy_assignable CopyAssignableValue, \ + destructible DestructibleValue, \ + equality_comparable EqualityComparableValue, \ + inequality_comparable InequalityComparableValue, \ + less_than_comparable LessThanComparableValue, \ + less_equal_comparable LessEqualComparableValue, \ + greater_equal_comparable GreaterEqualComparableValue, \ + greater_than_comparable GreaterThanComparableValue, \ + swappable SwappableValue, hashable HashableValue> \ + constexpr type ConformanceProfile< \ + DefaultConstructibleValue, MoveConstructibleValue, \ + CopyConstructibleValue, MoveAssignableValue, CopyAssignableValue, \ + DestructibleValue, EqualityComparableValue, InequalityComparableValue, \ + LessThanComparableValue, LessEqualComparableValue, \ + GreaterEqualComparableValue, GreaterThanComparableValue, SwappableValue, \ + HashableValue>::name + +#define ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(type) \ + ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(type, \ + type##_support); \ + ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL(bool, is_##type) + +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(default_constructible); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_constructible); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_constructible); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(move_assignable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(copy_assignable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(destructible); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(equality_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(inequality_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(less_than_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(less_equal_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_equal_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(greater_than_comparable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(swappable); +ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF(hashable); + +#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF +#undef ABSL_INTERNAL_CONFORMANCE_TESTING_DATA_MEMBER_DEF_IMPL + +// Retrieve the enum with the minimum underlying value. +// Note: std::min is not constexpr in C++11, which is why this is necessary. +template <class H> +constexpr H MinEnum(H head) { + return head; +} + +template <class H, class N, class... T> +constexpr H MinEnum(H head, N next, T... tail) { + return (UnderlyingValue)(head) < (UnderlyingValue)(next) + ? (MinEnum)(head, tail...) + : (MinEnum)(next, tail...); +} + +template <class... Profs> +struct MinimalProfiles { + static constexpr default_constructible + default_constructible_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::default_constructible_support...); + + static constexpr move_constructible move_constructible_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::move_constructible_support...); + + static constexpr copy_constructible copy_constructible_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::copy_constructible_support...); + + static constexpr move_assignable move_assignable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::move_assignable_support...); + + static constexpr copy_assignable copy_assignable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::copy_assignable_support...); + + static constexpr destructible destructible_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::destructible_support...); + + static constexpr equality_comparable equality_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::equality_comparable_support...); + + static constexpr inequality_comparable + inequality_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::inequality_comparable_support...); + + static constexpr less_than_comparable + less_than_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::less_than_comparable_support...); + + static constexpr less_equal_comparable + less_equal_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::less_equal_comparable_support...); + + static constexpr greater_equal_comparable + greater_equal_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::greater_equal_comparable_support...); + + static constexpr greater_than_comparable + greater_than_comparable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::greater_than_comparable_support...); + + static constexpr swappable swappable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::swappable_support...); + + static constexpr hashable hashable_support = // NOLINT + (MinEnum)(PropertiesOfT<Profs>::hashable_support...); + + using properties = ConformanceProfile< + default_constructible_support, move_constructible_support, + copy_constructible_support, move_assignable_support, + copy_assignable_support, destructible_support, + equality_comparable_support, inequality_comparable_support, + less_than_comparable_support, less_equal_comparable_support, + greater_equal_comparable_support, greater_than_comparable_support, + swappable_support, hashable_support>; +}; + +// Retrieve the enum with the greatest underlying value. +// Note: std::max is not constexpr in C++11, which is why this is necessary. +template <class H> +constexpr H MaxEnum(H head) { + return head; +} + +template <class H, class N, class... T> +constexpr H MaxEnum(H head, N next, T... tail) { + return (UnderlyingValue)(next) < (UnderlyingValue)(head) + ? (MaxEnum)(head, tail...) + : (MaxEnum)(next, tail...); +} + +template <class... Profs> +struct CombineProfilesImpl { + static constexpr default_constructible + default_constructible_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::default_constructible_support...); + + static constexpr move_constructible move_constructible_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::move_constructible_support...); + + static constexpr copy_constructible copy_constructible_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::copy_constructible_support...); + + static constexpr move_assignable move_assignable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::move_assignable_support...); + + static constexpr copy_assignable copy_assignable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::copy_assignable_support...); + + static constexpr destructible destructible_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::destructible_support...); + + static constexpr equality_comparable equality_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::equality_comparable_support...); + + static constexpr inequality_comparable + inequality_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::inequality_comparable_support...); + + static constexpr less_than_comparable + less_than_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::less_than_comparable_support...); + + static constexpr less_equal_comparable + less_equal_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::less_equal_comparable_support...); + + static constexpr greater_equal_comparable + greater_equal_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::greater_equal_comparable_support...); + + static constexpr greater_than_comparable + greater_than_comparable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::greater_than_comparable_support...); + + static constexpr swappable swappable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::swappable_support...); + + static constexpr hashable hashable_support = // NOLINT + (MaxEnum)(PropertiesOfT<Profs>::hashable_support...); + + using properties = ConformanceProfile< + default_constructible_support, move_constructible_support, + copy_constructible_support, move_assignable_support, + copy_assignable_support, destructible_support, + equality_comparable_support, inequality_comparable_support, + less_than_comparable_support, less_equal_comparable_support, + greater_equal_comparable_support, greater_than_comparable_support, + swappable_support, hashable_support>; +}; + +// NOTE: We use this as opposed to a direct alias of CombineProfilesImpl so that +// when named aliases of CombineProfiles are created (such as in +// conformance_aliases.h), we only pay for the combination algorithm on the +// profiles that are actually used. +template <class... Profs> +struct CombineProfiles { + using profile_alias_of = CombineProfilesImpl<Profs...>; +}; + +template <> +struct CombineProfiles<> { + using properties = ConformanceProfile<>; +}; + +template <class Profile, class Tag> +struct StrongProfileTypedef { + using properties = PropertiesOfT<Profile>; +}; + +template <class T, class /*Enabler*/ = void> +struct IsProfileImpl : std::false_type {}; + +template <class T> +struct IsProfileImpl<T, absl::void_t<PropertiesOfT<T>>> : std::true_type {}; + +template <class T> +struct IsProfile : IsProfileImpl<T>::type {}; + +// A tag that describes which set of properties we will check when the user +// requires a strict match in conformance (as opposed to a loose match which +// allows more-refined support of any given operation). +// +// Currently only the RegularityDomain exists and it includes all operations +// that the conformance testing suite knows about. The intent is that if the +// suite is expanded to support extension, such as for checking conformance of +// concepts like Iterators or Containers, additional corresponding domains can +// be created. +struct RegularityDomain {}; + +} // namespace types_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_TYPES_INTERNAL_CONFORMANCE_PROFILE_H_ |