Line data Source code
1 : #pragma once
2 : #ifndef C10_UTIL_CPP17_H_
3 : #define C10_UTIL_CPP17_H_
4 :
5 : #include <c10/macros/Macros.h>
6 : #include <cstdlib>
7 : #include <functional>
8 : #include <memory>
9 : #include <sstream>
10 : #include <string>
11 : #include <type_traits>
12 : #include <utility>
13 :
14 : #if !defined(__clang__) && !defined(_MSC_VER) && defined(__GNUC__) && \
15 : __GNUC__ < 5
16 : #error \
17 : "You're trying to build PyTorch with a too old version of GCC. We need GCC 5 or later."
18 : #endif
19 :
20 : #if defined(__clang__) && __clang_major__ < 4
21 : #error \
22 : "You're trying to build PyTorch with a too old version of Clang. We need Clang 4 or later."
23 : #endif
24 :
25 : #if (defined(_MSC_VER) && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L)) || \
26 : (!defined(_MSC_VER) && __cplusplus < 201703L)
27 : #error You need C++17 to compile PyTorch
28 : #endif
29 :
30 : #if defined(_WIN32) && (defined(min) || defined(max))
31 : #error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows
32 : #endif
33 :
34 : /*
35 : * This header adds some polyfills with C++17 functionality
36 : */
37 :
38 : namespace c10 {
39 :
40 : // in c++17 std::result_of has been superceded by std::invoke_result. Since
41 : // c++20, std::result_of is removed.
42 : template <typename F, typename... args>
43 : #if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L
44 : using invoke_result = typename std::invoke_result<F, args...>;
45 : #else
46 : using invoke_result = typename std::result_of<F && (args && ...)>;
47 : #endif
48 :
49 : template <typename F, typename... args>
50 : using invoke_result_t = typename invoke_result<F, args...>::type;
51 :
52 : // std::is_pod is deprecated in C++20, std::is_standard_layout and
53 : // std::is_trivial are introduced in C++11, std::conjunction has been introduced
54 : // in C++17.
55 : template <typename T>
56 : #if defined(__cpp_lib_logical_traits) && __cpp_lib_logical_traits >= 201510L
57 : using is_pod = std::conjunction<std::is_standard_layout<T>, std::is_trivial<T>>;
58 : #else
59 : using is_pod = std::is_pod<T>;
60 : #endif
61 :
62 : template <typename T>
63 : constexpr bool is_pod_v = is_pod<T>::value;
64 :
65 : namespace guts {
66 :
67 : template <typename Base, typename Child, typename... Args>
68 : typename std::enable_if<
69 : !std::is_array<Base>::value && !std::is_array<Child>::value &&
70 : std::is_base_of<Base, Child>::value,
71 : std::unique_ptr<Base>>::type
72 : make_unique_base(Args&&... args) {
73 : return std::unique_ptr<Base>(new Child(std::forward<Args>(args)...));
74 : }
75 :
76 : #if defined(__cpp_lib_logical_traits) && !(defined(_MSC_VER) && _MSC_VER < 1920)
77 :
78 : template <class... B>
79 : using conjunction = std::conjunction<B...>;
80 : template <class... B>
81 : using disjunction = std::disjunction<B...>;
82 : template <bool B>
83 : using bool_constant = std::bool_constant<B>;
84 : template <class B>
85 : using negation = std::negation<B>;
86 :
87 : #else
88 :
89 : // Implementation taken from http://en.cppreference.com/w/cpp/types/conjunction
90 : template <class...>
91 : struct conjunction : std::true_type {};
92 : template <class B1>
93 : struct conjunction<B1> : B1 {};
94 : template <class B1, class... Bn>
95 : struct conjunction<B1, Bn...>
96 : : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};
97 :
98 : // Implementation taken from http://en.cppreference.com/w/cpp/types/disjunction
99 : template <class...>
100 : struct disjunction : std::false_type {};
101 : template <class B1>
102 : struct disjunction<B1> : B1 {};
103 : template <class B1, class... Bn>
104 : struct disjunction<B1, Bn...>
105 : : std::conditional_t<bool(B1::value), B1, disjunction<Bn...>> {};
106 :
107 : // Implementation taken from
108 : // http://en.cppreference.com/w/cpp/types/integral_constant
109 : template <bool B>
110 : using bool_constant = std::integral_constant<bool, B>;
111 :
112 : // Implementation taken from http://en.cppreference.com/w/cpp/types/negation
113 : template <class B>
114 : struct negation : bool_constant<!bool(B::value)> {};
115 :
116 : #endif
117 :
118 : #ifdef __cpp_lib_void_t
119 :
120 : template <class T>
121 : using void_t = std::void_t<T>;
122 :
123 : #else
124 :
125 : // Implementation taken from http://en.cppreference.com/w/cpp/types/void_t
126 : // (it takes CWG1558 into account and also works for older compilers)
127 : template <typename... Ts>
128 : struct make_void {
129 : typedef void type;
130 : };
131 : template <typename... Ts>
132 : using void_t = typename make_void<Ts...>::type;
133 :
134 : #endif
135 :
136 : #if defined(USE_ROCM)
137 : // rocm doesn't like the C10_HOST_DEVICE
138 : #define CUDA_HOST_DEVICE
139 : #else
140 : #define CUDA_HOST_DEVICE C10_HOST_DEVICE
141 : #endif
142 :
143 : #if defined(__cpp_lib_apply) && !defined(__CUDA_ARCH__)
144 :
145 : template <class F, class Tuple>
146 : CUDA_HOST_DEVICE inline constexpr decltype(auto) apply(F&& f, Tuple&& t) {
147 : return std::apply(std::forward<F>(f), std::forward<Tuple>(t));
148 : }
149 :
150 : #else
151 :
152 : // Implementation from http://en.cppreference.com/w/cpp/utility/apply (but
153 : // modified)
154 : // TODO This is an incomplete implementation of std::apply, not working for
155 : // member functions.
156 : namespace detail {
157 : template <class F, class Tuple, std::size_t... INDEX>
158 : #if defined(_MSC_VER)
159 : // MSVC has a problem with the decltype() return type, but it also doesn't need
160 : // it
161 : C10_HOST_DEVICE constexpr auto apply_impl(
162 : F&& f,
163 : Tuple&& t,
164 : std::index_sequence<INDEX...>)
165 : #else
166 : // GCC/Clang need the decltype() return type
167 : CUDA_HOST_DEVICE constexpr decltype(auto) apply_impl(
168 : F&& f,
169 : Tuple&& t,
170 : std::index_sequence<INDEX...>)
171 : #endif
172 : {
173 : return std::forward<F>(f)(std::get<INDEX>(std::forward<Tuple>(t))...);
174 : }
175 : } // namespace detail
176 :
177 : template <class F, class Tuple>
178 : CUDA_HOST_DEVICE constexpr decltype(auto) apply(F&& f, Tuple&& t) {
179 : return detail::apply_impl(
180 : std::forward<F>(f),
181 : std::forward<Tuple>(t),
182 : std::make_index_sequence<
183 : std::tuple_size<std::remove_reference_t<Tuple>>::value>{});
184 : }
185 :
186 : #endif
187 :
188 : #undef CUDA_HOST_DEVICE
189 :
190 : template <typename Functor, typename... Args>
191 : typename std::enable_if<
192 : std::is_member_pointer<typename std::decay<Functor>::type>::value,
193 : typename c10::invoke_result_t<Functor, Args...>>::type
194 : invoke(Functor&& f, Args&&... args) {
195 : return std::mem_fn(std::forward<Functor>(f))(std::forward<Args>(args)...);
196 : }
197 :
198 : template <typename Functor, typename... Args>
199 : typename std::enable_if<
200 : !std::is_member_pointer<typename std::decay<Functor>::type>::value,
201 : typename c10::invoke_result_t<Functor, Args...>>::type
202 : invoke(Functor&& f, Args&&... args) {
203 : return std::forward<Functor>(f)(std::forward<Args>(args)...);
204 : }
205 :
206 : namespace detail {
207 : struct _identity final {
208 : template <class T>
209 : using type_identity = T;
210 :
211 : template <class T>
212 : decltype(auto) operator()(T&& arg) {
213 : return std::forward<T>(arg);
214 : }
215 : };
216 :
217 : template <class Func, class Enable = void>
218 : struct function_takes_identity_argument : std::false_type {};
219 : #if defined(_MSC_VER)
220 : // For some weird reason, MSVC shows a compiler error when using guts::void_t
221 : // instead of std::void_t. But we're only building on MSVC versions that have
222 : // std::void_t, so let's just use that one.
223 : template <class Func>
224 : struct function_takes_identity_argument<
225 : Func,
226 : std::void_t<decltype(std::declval<Func>()(_identity()))>> : std::true_type {
227 : };
228 : #else
229 : template <class Func>
230 : struct function_takes_identity_argument<
231 : Func,
232 : void_t<decltype(std::declval<Func>()(_identity()))>> : std::true_type {};
233 : #endif
234 : } // namespace detail
235 :
236 : // GCC 4.8 doesn't define std::to_string, even though that's in C++11. Let's
237 : // define it.
238 : namespace detail {
239 : class DummyClassForToString final {};
240 : } // namespace detail
241 : } // namespace guts
242 : } // namespace c10
243 : namespace std {
244 : // We use SFINAE to detect if std::to_string exists for a type, but that only
245 : // works if the function name is defined. So let's define a std::to_string for a
246 : // dummy type. If you're getting an error here saying that this overload doesn't
247 : // match your std::to_string() call, then you're calling std::to_string() but
248 : // should be calling c10::guts::to_string().
249 : inline std::string to_string(c10::guts::detail::DummyClassForToString) {
250 : return "";
251 : }
252 :
253 : } // namespace std
254 : namespace c10 {
255 : namespace guts {
256 : namespace detail {
257 :
258 : template <class T, class Enable = void>
259 : struct to_string_ final {
260 : static std::string call(T value) {
261 : std::ostringstream str;
262 : str << value;
263 : return str.str();
264 : }
265 : };
266 : // If a std::to_string exists, use that instead
267 : template <class T>
268 : struct to_string_<T, void_t<decltype(std::to_string(std::declval<T>()))>>
269 : final {
270 0 : static std::string call(T value) {
271 0 : return std::to_string(value);
272 : }
273 : };
274 : } // namespace detail
275 : template <class T>
276 0 : inline std::string to_string(T value) {
277 0 : return detail::to_string_<T>::call(value);
278 : }
279 :
280 : } // namespace guts
281 : } // namespace c10
282 :
283 : #endif // C10_UTIL_CPP17_H_
|