|
#pragma once |
|
#ifndef C10_UTIL_CPP17_H_ |
|
#define C10_UTIL_CPP17_H_ |
|
|
|
#include <c10/macros/Macros.h> |
|
#include <cstdlib> |
|
#include <functional> |
|
#include <memory> |
|
#include <sstream> |
|
#include <string> |
|
#include <type_traits> |
|
#include <utility> |
|
|
|
#if !defined(__clang__) && !defined(_MSC_VER) && defined(__GNUC__) && \ |
|
__GNUC__ < 5 |
|
#error \ |
|
"You're trying to build PyTorch with a too old version of GCC. We need GCC 5 or later." |
|
#endif |
|
|
|
#if defined(__clang__) && __clang_major__ < 4 |
|
#error \ |
|
"You're trying to build PyTorch with a too old version of Clang. We need Clang 4 or later." |
|
#endif |
|
|
|
#if (defined(_MSC_VER) && (!defined(_MSVC_LANG) || _MSVC_LANG < 201402L)) || \ |
|
(!defined(_MSC_VER) && __cplusplus < 201402L) |
|
#error You need C++14 to compile PyTorch |
|
#endif |
|
|
|
#if defined(_WIN32) && (defined(min) || defined(max)) |
|
#error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows |
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
namespace c10 { |
|
|
|
|
|
|
|
template <typename F, typename... args> |
|
#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L |
|
using invoke_result = typename std::invoke_result<F, args...>; |
|
#else |
|
using invoke_result = typename std::result_of<F && (args && ...)>; |
|
#endif |
|
|
|
template <typename F, typename... args> |
|
using invoke_result_t = typename invoke_result<F, args...>::type; |
|
|
|
namespace guts { |
|
|
|
template <typename Base, typename Child, typename... Args> |
|
typename std::enable_if< |
|
!std::is_array<Base>::value && !std::is_array<Child>::value && |
|
std::is_base_of<Base, Child>::value, |
|
std::unique_ptr<Base>>::type |
|
make_unique_base(Args&&... args) { |
|
return std::unique_ptr<Base>(new Child(std::forward<Args>(args)...)); |
|
} |
|
|
|
#if defined(__cpp_lib_logical_traits) && !(defined(_MSC_VER) && _MSC_VER < 1920) |
|
|
|
template <class... B> |
|
using conjunction = std::conjunction<B...>; |
|
template <class... B> |
|
using disjunction = std::disjunction<B...>; |
|
template <bool B> |
|
using bool_constant = std::bool_constant<B>; |
|
template <class B> |
|
using negation = std::negation<B>; |
|
|
|
#else |
|
|
|
|
|
template <class...> |
|
struct conjunction : std::true_type {}; |
|
template <class B1> |
|
struct conjunction<B1> : B1 {}; |
|
template <class B1, class... Bn> |
|
struct conjunction<B1, Bn...> |
|
: std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {}; |
|
|
|
|
|
template <class...> |
|
struct disjunction : std::false_type {}; |
|
template <class B1> |
|
struct disjunction<B1> : B1 {}; |
|
template <class B1, class... Bn> |
|
struct disjunction<B1, Bn...> |
|
: std::conditional_t<bool(B1::value), B1, disjunction<Bn...>> {}; |
|
|
|
|
|
|
|
template <bool B> |
|
using bool_constant = std::integral_constant<bool, B>; |
|
|
|
|
|
template <class B> |
|
struct negation : bool_constant<!bool(B::value)> {}; |
|
|
|
#endif |
|
|
|
#ifdef __cpp_lib_void_t |
|
|
|
template <class T> |
|
using void_t = std::void_t<T>; |
|
|
|
#else |
|
|
|
|
|
|
|
template <typename... Ts> |
|
struct make_void { |
|
typedef void type; |
|
}; |
|
template <typename... Ts> |
|
using void_t = typename make_void<Ts...>::type; |
|
|
|
#endif |
|
|
|
#if defined(USE_ROCM) |
|
|
|
#define CUDA_HOST_DEVICE |
|
#else |
|
#define CUDA_HOST_DEVICE C10_HOST_DEVICE |
|
#endif |
|
|
|
#ifdef __cpp_lib_apply |
|
|
|
template <class F, class Tuple> |
|
CUDA_HOST_DEVICE inline constexpr decltype(auto) apply(F&& f, Tuple&& t) { |
|
return std::apply(std::forward<F>(f), std::forward<Tuple>(t)); |
|
} |
|
|
|
#else |
|
|
|
|
|
|
|
|
|
|
|
namespace detail { |
|
template <class F, class Tuple, std::size_t... INDEX> |
|
#if defined(_MSC_VER) |
|
|
|
|
|
C10_HOST_DEVICE constexpr auto apply_impl( |
|
F&& f, |
|
Tuple&& t, |
|
std::index_sequence<INDEX...>) |
|
#else |
|
|
|
CUDA_HOST_DEVICE constexpr decltype(auto) apply_impl( |
|
F&& f, |
|
Tuple&& t, |
|
std::index_sequence<INDEX...>) |
|
#endif |
|
{ |
|
return std::forward<F>(f)(std::get<INDEX>(std::forward<Tuple>(t))...); |
|
} |
|
} |
|
|
|
template <class F, class Tuple> |
|
CUDA_HOST_DEVICE constexpr decltype(auto) apply(F&& f, Tuple&& t) { |
|
return detail::apply_impl( |
|
std::forward<F>(f), |
|
std::forward<Tuple>(t), |
|
std::make_index_sequence< |
|
std::tuple_size<std::remove_reference_t<Tuple>>::value>{}); |
|
} |
|
|
|
#endif |
|
|
|
#undef CUDA_HOST_DEVICE |
|
|
|
template <typename Functor, typename... Args> |
|
typename std::enable_if< |
|
std::is_member_pointer<typename std::decay<Functor>::type>::value, |
|
typename c10::invoke_result_t<Functor, Args...>>::type |
|
invoke(Functor&& f, Args&&... args) { |
|
return std::mem_fn(std::forward<Functor>(f))(std::forward<Args>(args)...); |
|
} |
|
|
|
template <typename Functor, typename... Args> |
|
typename std::enable_if< |
|
!std::is_member_pointer<typename std::decay<Functor>::type>::value, |
|
typename c10::invoke_result_t<Functor, Args...>>::type |
|
invoke(Functor&& f, Args&&... args) { |
|
return std::forward<Functor>(f)(std::forward<Args>(args)...); |
|
} |
|
|
|
namespace detail { |
|
struct _identity final { |
|
template <class T> |
|
using type_identity = T; |
|
|
|
template <class T> |
|
decltype(auto) operator()(T&& arg) { |
|
return std::forward<T>(arg); |
|
} |
|
}; |
|
|
|
template <class Func, class Enable = void> |
|
struct function_takes_identity_argument : std::false_type {}; |
|
#if defined(_MSC_VER) |
|
|
|
|
|
|
|
template <class Func> |
|
struct function_takes_identity_argument< |
|
Func, |
|
std::void_t<decltype(std::declval<Func>()(_identity()))>> : std::true_type { |
|
}; |
|
#else |
|
template <class Func> |
|
struct function_takes_identity_argument< |
|
Func, |
|
void_t<decltype(std::declval<Func>()(_identity()))>> : std::true_type {}; |
|
#endif |
|
|
|
template <bool Condition> |
|
struct _if_constexpr; |
|
|
|
template <> |
|
struct _if_constexpr<true> final { |
|
template < |
|
class ThenCallback, |
|
class ElseCallback, |
|
std::enable_if_t< |
|
function_takes_identity_argument<ThenCallback>::value, |
|
void*> = nullptr> |
|
static decltype(auto) call( |
|
ThenCallback&& thenCallback, |
|
ElseCallback&& ) { |
|
|
|
|
|
|
|
return thenCallback(_identity()); |
|
} |
|
|
|
template < |
|
class ThenCallback, |
|
class ElseCallback, |
|
std::enable_if_t< |
|
!function_takes_identity_argument<ThenCallback>::value, |
|
void*> = nullptr> |
|
static decltype(auto) call( |
|
ThenCallback&& thenCallback, |
|
ElseCallback&& ) { |
|
return thenCallback(); |
|
} |
|
}; |
|
|
|
template <> |
|
struct _if_constexpr<false> final { |
|
template < |
|
class ThenCallback, |
|
class ElseCallback, |
|
std::enable_if_t< |
|
function_takes_identity_argument<ElseCallback>::value, |
|
void*> = nullptr> |
|
static decltype(auto) call( |
|
ThenCallback&& , |
|
ElseCallback&& elseCallback) { |
|
|
|
|
|
|
|
return elseCallback(_identity()); |
|
} |
|
|
|
template < |
|
class ThenCallback, |
|
class ElseCallback, |
|
std::enable_if_t< |
|
!function_takes_identity_argument<ElseCallback>::value, |
|
void*> = nullptr> |
|
static decltype(auto) call( |
|
ThenCallback&& , |
|
ElseCallback&& elseCallback) { |
|
return elseCallback(); |
|
} |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <bool Condition, class ThenCallback, class ElseCallback> |
|
decltype(auto) if_constexpr( |
|
ThenCallback&& thenCallback, |
|
ElseCallback&& elseCallback) { |
|
#if defined(__cpp_if_constexpr) |
|
|
|
|
|
if constexpr (Condition) { |
|
if constexpr (detail::function_takes_identity_argument< |
|
ThenCallback>::value) { |
|
|
|
|
|
|
|
|
|
return static_cast<ThenCallback&&>(thenCallback)(detail::_identity()); |
|
} else { |
|
return static_cast<ThenCallback&&>(thenCallback)(); |
|
} |
|
} else { |
|
if constexpr (detail::function_takes_identity_argument< |
|
ElseCallback>::value) { |
|
return static_cast<ElseCallback&&>(elseCallback)(detail::_identity()); |
|
} else { |
|
return static_cast<ElseCallback&&>(elseCallback)(); |
|
} |
|
} |
|
#else |
|
|
|
return detail::_if_constexpr<Condition>::call( |
|
static_cast<ThenCallback&&>(thenCallback), |
|
static_cast<ElseCallback&&>(elseCallback)); |
|
#endif |
|
} |
|
|
|
template <bool Condition, class ThenCallback> |
|
decltype(auto) if_constexpr(ThenCallback&& thenCallback) { |
|
#if defined(__cpp_if_constexpr) |
|
|
|
|
|
if constexpr (Condition) { |
|
if constexpr (detail::function_takes_identity_argument< |
|
ThenCallback>::value) { |
|
|
|
|
|
|
|
|
|
return static_cast<ThenCallback&&>(thenCallback)(detail::_identity()); |
|
} else { |
|
return static_cast<ThenCallback&&>(thenCallback)(); |
|
} |
|
} |
|
#else |
|
|
|
return if_constexpr<Condition>( |
|
static_cast<ThenCallback&&>(thenCallback), [](auto) {}); |
|
#endif |
|
} |
|
|
|
|
|
|
|
namespace detail { |
|
class DummyClassForToString final {}; |
|
} |
|
} |
|
} |
|
namespace std { |
|
|
|
|
|
|
|
|
|
|
|
inline std::string to_string(c10::guts::detail::DummyClassForToString) { |
|
return ""; |
|
} |
|
|
|
} |
|
namespace c10 { |
|
namespace guts { |
|
namespace detail { |
|
|
|
template <class T, class Enable = void> |
|
struct to_string_ final { |
|
static std::string call(T value) { |
|
std::ostringstream str; |
|
str << value; |
|
return str.str(); |
|
} |
|
}; |
|
|
|
template <class T> |
|
struct to_string_<T, void_t<decltype(std::to_string(std::declval<T>()))>> |
|
final { |
|
static std::string call(T value) { |
|
return std::to_string(value); |
|
} |
|
}; |
|
} |
|
template <class T> |
|
inline std::string to_string(T value) { |
|
return detail::to_string_<T>::call(value); |
|
} |
|
|
|
template <class T> |
|
constexpr const T& min(const T& a, const T& b) { |
|
return (b < a) ? b : a; |
|
} |
|
|
|
template <class T> |
|
constexpr const T& max(const T& a, const T& b) { |
|
return (a < b) ? b : a; |
|
} |
|
|
|
} |
|
} |
|
|
|
#endif |
|
|