1#pragma once
2
3#include <c10/util/C++17.h>
4#include <functional>
5
6namespace c10 {
7namespace guts {
8
9/**
10 * is_equality_comparable<T> is true_type iff the equality operator is defined
11 * for T.
12 */
13template <class T, class Enable = void>
14struct is_equality_comparable : std::false_type {};
15template <class T>
16struct is_equality_comparable<
17 T,
18 void_t<decltype(std::declval<T&>() == std::declval<T&>())>>
19 : std::true_type {};
20template <class T>
21using is_equality_comparable_t = typename is_equality_comparable<T>::type;
22
23/**
24 * is_hashable<T> is true_type iff std::hash is defined for T
25 */
26template <class T, class Enable = void>
27struct is_hashable : std::false_type {};
28template <class T>
29struct is_hashable<T, void_t<decltype(std::hash<T>()(std::declval<T&>()))>>
30 : std::true_type {};
31template <class T>
32using is_hashable_t = typename is_hashable<T>::type;
33
34/**
35 * is_function_type<T> is true_type iff T is a plain function type (i.e.
36 * "Result(Args...)")
37 */
38template <class T>
39struct is_function_type : std::false_type {};
40template <class Result, class... Args>
41struct is_function_type<Result(Args...)> : std::true_type {};
42template <class T>
43using is_function_type_t = typename is_function_type<T>::type;
44
45/**
46 * is_instantiation_of<T, I> is true_type iff I is a template instantiation of T
47 * (e.g. vector<int> is an instantiation of vector) Example:
48 * is_instantiation_of_t<vector, vector<int>> // true
49 * is_instantiation_of_t<pair, pair<int, string>> // true
50 * is_instantiation_of_t<vector, pair<int, string>> // false
51 */
52template <template <class...> class Template, class T>
53struct is_instantiation_of : std::false_type {};
54template <template <class...> class Template, class... Args>
55struct is_instantiation_of<Template, Template<Args...>> : std::true_type {};
56template <template <class...> class Template, class T>
57using is_instantiation_of_t = typename is_instantiation_of<Template, T>::type;
58
59namespace detail {
60/**
61 * strip_class: helper to remove the class type from pointers to `operator()`.
62 */
63
64template <typename T>
65struct strip_class {};
66template <typename Class, typename Result, typename... Args>
67struct strip_class<Result (Class::*)(Args...)> {
68 using type = Result(Args...);
69};
70template <typename Class, typename Result, typename... Args>
71struct strip_class<Result (Class::*)(Args...) const> {
72 using type = Result(Args...);
73};
74template <typename T>
75using strip_class_t = typename strip_class<T>::type;
76} // namespace detail
77
78/**
79 * Evaluates to true_type, iff the given class is a Functor
80 * (i.e. has a call operator with some set of arguments)
81 */
82
83template <class Functor, class Enable = void>
84struct is_functor : std::false_type {};
85template <class Functor>
86struct is_functor<
87 Functor,
88 std::enable_if_t<is_function_type<
89 detail::strip_class_t<decltype(&Functor::operator())>>::value>>
90 : std::true_type {};
91
92/**
93 * lambda_is_stateless<T> is true iff the lambda type T is stateless
94 * (i.e. does not have a closure).
95 * Example:
96 * auto stateless_lambda = [] (int a) {return a;};
97 * lambda_is_stateless<decltype(stateless_lambda)> // true
98 * auto stateful_lambda = [&] (int a) {return a;};
99 * lambda_is_stateless<decltype(stateful_lambda)> // false
100 */
101namespace detail {
102template <class LambdaType, class FuncType>
103struct is_stateless_lambda__ final {
104 static_assert(
105 !std::is_same<LambdaType, LambdaType>::value,
106 "Base case shouldn't be hit");
107};
108// implementation idea: According to the C++ standard, stateless lambdas are
109// convertible to function pointers
110template <class LambdaType, class C, class Result, class... Args>
111struct is_stateless_lambda__<LambdaType, Result (C::*)(Args...) const>
112 : std::is_convertible<LambdaType, Result (*)(Args...)> {};
113template <class LambdaType, class C, class Result, class... Args>
114struct is_stateless_lambda__<LambdaType, Result (C::*)(Args...)>
115 : std::is_convertible<LambdaType, Result (*)(Args...)> {};
116
117// case where LambdaType is not even a functor
118template <class LambdaType, class Enable = void>
119struct is_stateless_lambda_ final : std::false_type {};
120// case where LambdaType is a functor
121template <class LambdaType>
122struct is_stateless_lambda_<
123 LambdaType,
124 std::enable_if_t<is_functor<LambdaType>::value>>
125 : is_stateless_lambda__<LambdaType, decltype(&LambdaType::operator())> {};
126} // namespace detail
127template <class T>
128using is_stateless_lambda = detail::is_stateless_lambda_<std::decay_t<T>>;
129
130/**
131 * is_type_condition<C> is true_type iff C<...> is a type trait representing a
132 * condition (i.e. has a constexpr static bool ::value member) Example:
133 * is_type_condition<std::is_reference> // true
134 */
135template <template <class> class C, class Enable = void>
136struct is_type_condition : std::false_type {};
137template <template <class> class C>
138struct is_type_condition<
139 C,
140 std::enable_if_t<
141 std::is_same<bool, std::remove_cv_t<decltype(C<int>::value)>>::value>>
142 : std::true_type {};
143
144/**
145 * is_fundamental<T> is true_type iff the lambda type T is a fundamental type
146 * (that is, arithmetic type, void, or nullptr_t). Example: is_fundamental<int>
147 * // true We define it here to resolve a MSVC bug. See
148 * https://github.com/pytorch/pytorch/issues/30932 for details.
149 */
150template <class T>
151struct is_fundamental : std::is_fundamental<T> {};
152} // namespace guts
153} // namespace c10
154