// 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_