// 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. #ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_ #define ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_ // Checks to determine whether or not we can use abi::__cxa_demangle #if (defined(__ANDROID__) || defined(ANDROID)) && !defined(OS_ANDROID) #define ABSL_INTERNAL_OS_ANDROID #endif // We support certain compilers only. See demangle.h for details. #if defined(OS_ANDROID) && (defined(__i386__) || defined(__x86_64__)) #define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 0 #elif (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \ !defined(__mips__) #define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 1 #elif defined(__clang__) && !defined(_MSC_VER) #define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 1 #else #define ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE 0 #endif #include #include #include #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" #include "absl/utility/utility.h" #if ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE #include #include #endif namespace absl { ABSL_NAMESPACE_BEGIN namespace types_internal { // Return a readable name for type T. template absl::string_view NameOfImpl() { // TODO(calabrese) Investigate using debugging:internal_demangle as a fallback. #if ABSL_TYPES_INTERNAL_HAS_CXA_DEMANGLE int status = 0; char* demangled_name = nullptr; demangled_name = abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status); if (status == 0 && demangled_name != nullptr) { return demangled_name; } else { return typeid(T).name(); } #else return typeid(T).name(); #endif // NOTE: We intentionally leak demangled_name so that it remains valid // throughout the remainder of the program. } // Given a type, returns as nice of a type name as we can produce (demangled). // // Note: This currently strips cv-qualifiers and references, but that is okay // because we only use this internally with unqualified object types. template std::string NameOf() { static const absl::string_view result = NameOfImpl(); return std::string(result); } //////////////////////////////////////////////////////////////////////////////// // // Metafunction to check if a type is callable with no explicit arguments template struct IsNullaryCallableImpl : std::false_type {}; template struct IsNullaryCallableImpl< Fun, absl::void_t()())>> : std::true_type { using result_type = decltype(std::declval()()); template using for_type = std::is_same; using void_if_true = void; }; template struct IsNullaryCallable : IsNullaryCallableImpl {}; // //////////////////////////////////////////////////////////////////////////////// // A type that contains a function object that returns an instance of a type // that is undergoing conformance testing. This function is required to always // return the same value upon invocation. template struct GeneratorType; // A type that contains a tuple of GeneratorType where each Fun has the // same return type. The result of each of the different generators should all // be equal values, though the underlying object representation may differ (such // as if one returns 0.0 and another return -0.0, or if one returns an empty // vector and another returns an empty vector with a different capacity. template struct EquivalenceClassType; //////////////////////////////////////////////////////////////////////////////// // // A metafunction to check if a type is a specialization of EquivalenceClassType template struct IsEquivalenceClass : std::false_type {}; template <> struct IsEquivalenceClass> : std::true_type { using self = IsEquivalenceClass; // A metafunction to check if this EquivalenceClassType is a valid // EquivalenceClassType for a type `ValueType` that is undergoing testing template using for_type = std::true_type; }; template struct IsEquivalenceClass> : std::true_type { using self = IsEquivalenceClass; // The type undergoing conformance testing that this EquivalenceClass // corresponds to using result_type = typename IsNullaryCallable::result_type; // A metafunction to check if this EquivalenceClassType is a valid // EquivalenceClassType for a type `ValueType` that is undergoing testing template using for_type = std::is_same; }; // //////////////////////////////////////////////////////////////////////////////// // A type that contains an ordered series of EquivalenceClassTypes, where the // the function object of each underlying GeneratorType has the same return type // // These equivalence classes are required to be in a logical ascending order // that is consistent with comparison operators that are defined for the return // type of each GeneratorType, if any. template struct OrderedEquivalenceClasses; //////////////////////////////////////////////////////////////////////////////// // // A metafunction to determine the return type of the function object contained // in a GeneratorType specialization. template struct ResultOfGenerator {}; template struct ResultOfGenerator> { using type = decltype(std::declval()()); }; template using ResultOfGeneratorT = typename ResultOfGenerator>::type; // //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // A metafunction that yields true iff each of Funs is a GeneratorType // specialization and they all contain functions with the same return type template struct AreGeneratorsWithTheSameReturnTypeImpl : std::false_type {}; template <> struct AreGeneratorsWithTheSameReturnTypeImpl : std::true_type {}; template struct AreGeneratorsWithTheSameReturnTypeImpl< typename std::enable_if, ResultOfGeneratorT>...>::value>::type, Head, Tail...> : std::true_type {}; template struct AreGeneratorsWithTheSameReturnType : AreGeneratorsWithTheSameReturnTypeImpl::type {}; // //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // A metafunction that yields true iff each of Funs is an EquivalenceClassType // specialization and they all contain GeneratorType specializations that have // the same return type template struct AreEquivalenceClassesOfTheSameType { static_assert(sizeof...(EqClasses) != sizeof...(EqClasses), ""); }; template <> struct AreEquivalenceClassesOfTheSameType<> : std::true_type { using self = AreEquivalenceClassesOfTheSameType; // Metafunction to check that a type is the same as all of the equivalence // classes, if any. // Note: In this specialization there are no equivalence classes, so the // value type is always compatible. template using for_type = std::true_type; }; template struct AreEquivalenceClassesOfTheSameType> : std::true_type { using self = AreEquivalenceClassesOfTheSameType; // Metafunction to check that a type is the same as all of the equivalence // classes, if any. template using for_type = typename IsEquivalenceClass< EquivalenceClassType>::template for_type; }; template struct AreEquivalenceClassesOfTheSameType< EquivalenceClassType<>, EquivalenceClassType<>, TailEqClasses...> : AreEquivalenceClassesOfTheSameType::self {}; template struct AreEquivalenceClassesOfTheSameType< EquivalenceClassType<>, EquivalenceClassType, TailEqClasses...> : AreEquivalenceClassesOfTheSameType< EquivalenceClassType, TailEqClasses...>::self {}; template struct AreEquivalenceClassesOfTheSameType< EquivalenceClassType, EquivalenceClassType<>, TailEqClasses...> : AreEquivalenceClassesOfTheSameType< EquivalenceClassType, TailEqClasses...>::self {}; template struct AreEquivalenceClassesOfTheSameType< EquivalenceClassType, EquivalenceClassType, TailEqClasses...> : absl::conditional_t< IsNullaryCallable::template for_type< typename IsNullaryCallable::result_type>::value, AreEquivalenceClassesOfTheSameType< EquivalenceClassType, TailEqClasses...>, std::false_type> {}; // //////////////////////////////////////////////////////////////////////////////// // Execute a function for each passed-in parameter. template void ForEachParameter(const Fun& fun, const Cases&... cases) { const std::initializer_list results = { (static_cast(fun(cases)), true)...}; (void)results; } // Execute a function on each passed-in parameter (using a bound function). template struct ForEachParameterFun { template void operator()(const T&... cases) const { (ForEachParameter)(fun, cases...); } Fun fun; }; // Execute a function on each element of a tuple. template void ForEachTupleElement(const Fun& fun, const Tup& tup) { absl::apply(ForEachParameterFun{fun}, tup); } //////////////////////////////////////////////////////////////////////////////// // // Execute a function for each combination of two elements of a tuple, including // combinations of an element with itself. template struct ForEveryTwoImpl { template struct WithBoundLhs { template void operator()(const Rhs& rhs) const { fun(lhs, rhs); } Fun fun; Lhs lhs; }; template void operator()(const Lhs& lhs) const { (ForEachTupleElement)(WithBoundLhs{fun, lhs}, args); } Fun fun; std::tuple args; }; template void ForEveryTwo(const Fun& fun, std::tuple args) { (ForEachTupleElement)(ForEveryTwoImpl{fun, args}, args); } // //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // Insert all values into an associative container template void InsertEach(Container* cont) { } template void InsertEach(Container* cont, H&& head, T&&... tail) { cont->insert(head); (InsertEach)(cont, tail...); } // //////////////////////////////////////////////////////////////////////////////// // A template with a nested "Invoke" static-member-function that executes a // passed-in Callable when `Condition` is true, otherwise it ignores the // Callable. This is useful for executing a function object with a condition // that corresponds to whether or not the Callable can be safely instantiated. // It has some overlapping uses with C++17 `if constexpr`. template struct If; template <> struct If { template static void Invoke(const Fun& /*fun*/, P&&... /*args*/) {} }; template <> struct If { template static void Invoke(const Fun& fun, P&&... args) { // TODO(calabrese) Use std::invoke equivalent instead of function-call. fun(absl::forward

(args)...); } }; // // ABSL_INTERNAL_STRINGIZE(...) // // This variadic macro transforms its arguments into a c-string literal after // expansion. // // Example: // // ABSL_INTERNAL_STRINGIZE(std::array) // // Results in: // // "std::array" #define ABSL_INTERNAL_STRINGIZE(...) ABSL_INTERNAL_STRINGIZE_IMPL((__VA_ARGS__)) #define ABSL_INTERNAL_STRINGIZE_IMPL(arg) ABSL_INTERNAL_STRINGIZE_IMPL2 arg #define ABSL_INTERNAL_STRINGIZE_IMPL2(...) #__VA_ARGS__ } // namespace types_internal ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_TYPES_INTERNAL_CONFORMANCE_TESTING_HELPERS_H_