blob: 00775f960c3682ab3a59a724d33f335395e9db63 [file] [log] [blame]
// 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 <tuple>
#include <type_traits>
#include <utility>
#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 <cxxabi.h>
#include <cstdlib>
#endif
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace types_internal {
// Return a readable name for type T.
template <class T>
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 <class T>
std::string NameOf() {
static const absl::string_view result = NameOfImpl<T>();
return std::string(result);
}
////////////////////////////////////////////////////////////////////////////////
//
// Metafunction to check if a type is callable with no explicit arguments
template <class Fun, class /*Enabler*/ = void>
struct IsNullaryCallableImpl : std::false_type {};
template <class Fun>
struct IsNullaryCallableImpl<
Fun, absl::void_t<decltype(std::declval<const Fun&>()())>>
: std::true_type {
using result_type = decltype(std::declval<const Fun&>()());
template <class ValueType>
using for_type = std::is_same<ValueType, result_type>;
using void_if_true = void;
};
template <class Fun>
struct IsNullaryCallable : IsNullaryCallableImpl<Fun> {};
//
////////////////////////////////////////////////////////////////////////////////
// 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 <class Fun>
struct GeneratorType;
// A type that contains a tuple of GeneratorType<Fun> 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 <class... Funs>
struct EquivalenceClassType;
////////////////////////////////////////////////////////////////////////////////
//
// A metafunction to check if a type is a specialization of EquivalenceClassType
template <class T>
struct IsEquivalenceClass : std::false_type {};
template <>
struct IsEquivalenceClass<EquivalenceClassType<>> : 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 <class ValueType>
using for_type = std::true_type;
};
template <class Head, class... Tail>
struct IsEquivalenceClass<EquivalenceClassType<Head, Tail...>>
: std::true_type {
using self = IsEquivalenceClass;
// The type undergoing conformance testing that this EquivalenceClass
// corresponds to
using result_type = typename IsNullaryCallable<Head>::result_type;
// A metafunction to check if this EquivalenceClassType is a valid
// EquivalenceClassType for a type `ValueType` that is undergoing testing
template <class ValueType>
using for_type = std::is_same<ValueType, result_type>;
};
//
////////////////////////////////////////////////////////////////////////////////
// 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 <class... EqClasses>
struct OrderedEquivalenceClasses;
////////////////////////////////////////////////////////////////////////////////
//
// A metafunction to determine the return type of the function object contained
// in a GeneratorType specialization.
template <class T>
struct ResultOfGenerator {};
template <class Fun>
struct ResultOfGenerator<GeneratorType<Fun>> {
using type = decltype(std::declval<const Fun&>()());
};
template <class Fun>
using ResultOfGeneratorT = typename ResultOfGenerator<GeneratorType<Fun>>::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 <class /*Enabler*/, class... Funs>
struct AreGeneratorsWithTheSameReturnTypeImpl : std::false_type {};
template <>
struct AreGeneratorsWithTheSameReturnTypeImpl<void> : std::true_type {};
template <class Head, class... Tail>
struct AreGeneratorsWithTheSameReturnTypeImpl<
typename std::enable_if<absl::conjunction<std::is_same<
ResultOfGeneratorT<Head>, ResultOfGeneratorT<Tail>>...>::value>::type,
Head, Tail...> : std::true_type {};
template <class... Funs>
struct AreGeneratorsWithTheSameReturnType
: AreGeneratorsWithTheSameReturnTypeImpl<void, Funs...>::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 <class... EqClasses>
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 <class /*ValueType*/>
using for_type = std::true_type;
};
template <class... Funs>
struct AreEquivalenceClassesOfTheSameType<EquivalenceClassType<Funs...>>
: std::true_type {
using self = AreEquivalenceClassesOfTheSameType;
// Metafunction to check that a type is the same as all of the equivalence
// classes, if any.
template <class ValueType>
using for_type = typename IsEquivalenceClass<
EquivalenceClassType<Funs...>>::template for_type<ValueType>;
};
template <class... TailEqClasses>
struct AreEquivalenceClassesOfTheSameType<
EquivalenceClassType<>, EquivalenceClassType<>, TailEqClasses...>
: AreEquivalenceClassesOfTheSameType<TailEqClasses...>::self {};
template <class HeadNextFun, class... TailNextFuns, class... TailEqClasses>
struct AreEquivalenceClassesOfTheSameType<
EquivalenceClassType<>, EquivalenceClassType<HeadNextFun, TailNextFuns...>,
TailEqClasses...>
: AreEquivalenceClassesOfTheSameType<
EquivalenceClassType<HeadNextFun, TailNextFuns...>,
TailEqClasses...>::self {};
template <class HeadHeadFun, class... TailHeadFuns, class... TailEqClasses>
struct AreEquivalenceClassesOfTheSameType<
EquivalenceClassType<HeadHeadFun, TailHeadFuns...>, EquivalenceClassType<>,
TailEqClasses...>
: AreEquivalenceClassesOfTheSameType<
EquivalenceClassType<HeadHeadFun, TailHeadFuns...>,
TailEqClasses...>::self {};
template <class HeadHeadFun, class... TailHeadFuns, class HeadNextFun,
class... TailNextFuns, class... TailEqClasses>
struct AreEquivalenceClassesOfTheSameType<
EquivalenceClassType<HeadHeadFun, TailHeadFuns...>,
EquivalenceClassType<HeadNextFun, TailNextFuns...>, TailEqClasses...>
: absl::conditional_t<
IsNullaryCallable<HeadNextFun>::template for_type<
typename IsNullaryCallable<HeadHeadFun>::result_type>::value,
AreEquivalenceClassesOfTheSameType<
EquivalenceClassType<HeadHeadFun, TailHeadFuns...>,
TailEqClasses...>,
std::false_type> {};
//
////////////////////////////////////////////////////////////////////////////////
// Execute a function for each passed-in parameter.
template <class Fun, class... Cases>
void ForEachParameter(const Fun& fun, const Cases&... cases) {
const std::initializer_list<bool> results = {
(static_cast<void>(fun(cases)), true)...};
(void)results;
}
// Execute a function on each passed-in parameter (using a bound function).
template <class Fun>
struct ForEachParameterFun {
template <class... T>
void operator()(const T&... cases) const {
(ForEachParameter)(fun, cases...);
}
Fun fun;
};
// Execute a function on each element of a tuple.
template <class Fun, class Tup>
void ForEachTupleElement(const Fun& fun, const Tup& tup) {
absl::apply(ForEachParameterFun<Fun>{fun}, tup);
}
////////////////////////////////////////////////////////////////////////////////
//
// Execute a function for each combination of two elements of a tuple, including
// combinations of an element with itself.
template <class Fun, class... T>
struct ForEveryTwoImpl {
template <class Lhs>
struct WithBoundLhs {
template <class Rhs>
void operator()(const Rhs& rhs) const {
fun(lhs, rhs);
}
Fun fun;
Lhs lhs;
};
template <class Lhs>
void operator()(const Lhs& lhs) const {
(ForEachTupleElement)(WithBoundLhs<Lhs>{fun, lhs}, args);
}
Fun fun;
std::tuple<T...> args;
};
template <class Fun, class... T>
void ForEveryTwo(const Fun& fun, std::tuple<T...> args) {
(ForEachTupleElement)(ForEveryTwoImpl<Fun, T...>{fun, args}, args);
}
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Insert all values into an associative container
template<class Container>
void InsertEach(Container* cont) {
}
template<class Container, class H, class... T>
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 <bool Condition>
struct If;
template <>
struct If</*Condition =*/false> {
template <class Fun, class... P>
static void Invoke(const Fun& /*fun*/, P&&... /*args*/) {}
};
template <>
struct If</*Condition =*/true> {
template <class Fun, class... P>
static void Invoke(const Fun& fun, P&&... args) {
// TODO(calabrese) Use std::invoke equivalent instead of function-call.
fun(absl::forward<P>(args)...);
}
};
//
// ABSL_INTERNAL_STRINGIZE(...)
//
// This variadic macro transforms its arguments into a c-string literal after
// expansion.
//
// Example:
//
// ABSL_INTERNAL_STRINGIZE(std::array<int, 10>)
//
// Results in:
//
// "std::array<int, 10>"
#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_