123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- #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
- /*
- * This header adds some polyfills with C++17 functionality
- */
- namespace c10 {
- // in c++17 std::result_of has been superceded by std::invoke_result. Since
- // c++20, std::result_of is removed.
- 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;
- // std::is_pod is deprecated in C++20, std::is_standard_layout and
- // std::is_trivial are introduced in C++11, std::conjunction has been introduced
- // in C++17.
- template <typename T>
- #if defined(__cpp_lib_logical_traits) && __cpp_lib_logical_traits >= 201510L
- using is_pod = std::conjunction<std::is_standard_layout<T>, std::is_trivial<T>>;
- #else
- using is_pod = std::is_pod<T>;
- #endif
- template <typename T>
- constexpr bool is_pod_v = is_pod<T>::value;
- 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
- // Implementation taken from http://en.cppreference.com/w/cpp/types/conjunction
- 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> {};
- // Implementation taken from http://en.cppreference.com/w/cpp/types/disjunction
- 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...>> {};
- // Implementation taken from
- // http://en.cppreference.com/w/cpp/types/integral_constant
- template <bool B>
- using bool_constant = std::integral_constant<bool, B>;
- // Implementation taken from http://en.cppreference.com/w/cpp/types/negation
- 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
- // Implementation taken from http://en.cppreference.com/w/cpp/types/void_t
- // (it takes CWG1558 into account and also works for older compilers)
- 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)
- // rocm doesn't like the C10_HOST_DEVICE
- #define CUDA_HOST_DEVICE
- #else
- #define CUDA_HOST_DEVICE C10_HOST_DEVICE
- #endif
- #if defined(__cpp_lib_apply) && !defined(__CUDA_ARCH__)
- 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
- // Implementation from http://en.cppreference.com/w/cpp/utility/apply (but
- // modified)
- // TODO This is an incomplete implementation of std::apply, not working for
- // member functions.
- namespace detail {
- template <class F, class Tuple, std::size_t... INDEX>
- #if defined(_MSC_VER)
- // MSVC has a problem with the decltype() return type, but it also doesn't need
- // it
- C10_HOST_DEVICE constexpr auto apply_impl(
- F&& f,
- Tuple&& t,
- std::index_sequence<INDEX...>)
- #else
- // GCC/Clang need the decltype() return type
- 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))...);
- }
- } // namespace detail
- 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)
- // For some weird reason, MSVC shows a compiler error when using guts::void_t
- // instead of std::void_t. But we're only building on MSVC versions that have
- // std::void_t, so let's just use that one.
- 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&& /* elseCallback */) {
- // The _identity instance passed in can be used to delay evaluation of an
- // expression, because the compiler can't know that it's just the identity
- // we're passing in.
- 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&& /* 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&& /* thenCallback */,
- ElseCallback&& elseCallback) {
- // The _identity instance passed in can be used to delay evaluation of an
- // expression, because the compiler can't know that it's just the identity
- // we're passing in.
- 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&& /* thenCallback */,
- ElseCallback&& elseCallback) {
- return elseCallback();
- }
- };
- } // namespace detail
- /*
- * Get something like C++17 if constexpr in C++14.
- *
- * Example 1: simple constexpr if/then/else
- * template<int arg> int increment_absolute_value() {
- * int result = arg;
- * if_constexpr<(arg > 0)>(
- * [&] { ++result; } // then-case
- * [&] { --result; } // else-case
- * );
- * return result;
- * }
- *
- * Example 2: without else case (i.e. conditionally prune code from assembly)
- * template<int arg> int decrement_if_positive() {
- * int result = arg;
- * if_constexpr<(arg > 0)>(
- * // This decrement operation is only present in the assembly for
- * // template instances with arg > 0.
- * [&] { --result; }
- * );
- * return result;
- * }
- *
- * Example 3: branch based on type (i.e. replacement for SFINAE)
- * struct MyClass1 {int value;};
- * struct MyClass2 {int val};
- * template <class T>
- * int func(T t) {
- * return if_constexpr<std::is_same<T, MyClass1>::value>(
- * [&](auto _) { return _(t).value; }, // this code is invalid for T ==
- * MyClass2, so a regular non-constexpr if statement wouldn't compile
- * [&](auto _) { return _(t).val; } // this code is invalid for T ==
- * MyClass1
- * );
- * }
- *
- * Note: The _ argument passed in Example 3 is the identity function, i.e. it
- * does nothing. It is used to force the compiler to delay type checking,
- * because the compiler doesn't know what kind of _ is passed in. Without it,
- * the compiler would fail when you try to access t.value but the member doesn't
- * exist.
- *
- * Note: In Example 3, both branches return int, so func() returns int. This is
- * not necessary. If func() had a return type of "auto", then both branches
- * could return different types, say func<MyClass1>() could return int and
- * func<MyClass2>() could return string.
- *
- * Note: if_constexpr<cond, t, f> is *eager* w.r.t. template expansion - meaning
- * this polyfill does not behave like a true "if statement at compilation time".
- * The `_` trick above only defers typechecking, which happens after
- * templates have been expanded. (Of course this is all that's necessary for
- * many use cases).
- */
- template <bool Condition, class ThenCallback, class ElseCallback>
- decltype(auto) if_constexpr(
- ThenCallback&& thenCallback,
- ElseCallback&& elseCallback) {
- #if defined(__cpp_if_constexpr)
- // If we have C++17, just use it's "if constexpr" feature instead of wrapping
- // it. This will give us better error messages.
- if constexpr (Condition) {
- if constexpr (detail::function_takes_identity_argument<
- ThenCallback>::value) {
- // Note that we use static_cast<T&&>(t) instead of std::forward (or
- // ::std::forward) because using the latter produces some compilation
- // errors about ambiguous `std` on MSVC when using C++17. This static_cast
- // is just what std::forward is doing under the hood, and is equivalent.
- 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
- // C++14 implementation of if constexpr
- 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 we have C++17, just use it's "if constexpr" feature instead of wrapping
- // it. This will give us better error messages.
- if constexpr (Condition) {
- if constexpr (detail::function_takes_identity_argument<
- ThenCallback>::value) {
- // Note that we use static_cast<T&&>(t) instead of std::forward (or
- // ::std::forward) because using the latter produces some compilation
- // errors about ambiguous `std` on MSVC when using C++17. This static_cast
- // is just what std::forward is doing under the hood, and is equivalent.
- return static_cast<ThenCallback&&>(thenCallback)(detail::_identity());
- } else {
- return static_cast<ThenCallback&&>(thenCallback)();
- }
- }
- #else
- // C++14 implementation of if constexpr
- return if_constexpr<Condition>(
- static_cast<ThenCallback&&>(thenCallback), [](auto) {});
- #endif
- }
- // GCC 4.8 doesn't define std::to_string, even though that's in C++11. Let's
- // define it.
- namespace detail {
- class DummyClassForToString final {};
- } // namespace detail
- } // namespace guts
- } // namespace c10
- namespace std {
- // We use SFINAE to detect if std::to_string exists for a type, but that only
- // works if the function name is defined. So let's define a std::to_string for a
- // dummy type. If you're getting an error here saying that this overload doesn't
- // match your std::to_string() call, then you're calling std::to_string() but
- // should be calling c10::guts::to_string().
- inline std::string to_string(c10::guts::detail::DummyClassForToString) {
- return "";
- }
- } // namespace std
- 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();
- }
- };
- // If a std::to_string exists, use that instead
- 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);
- }
- };
- } // namespace detail
- 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;
- }
- } // namespace guts
- } // namespace c10
- #endif // C10_UTIL_CPP17_H_
|