#pragma once #ifndef C10_UTIL_CPP17_H_ #define C10_UTIL_CPP17_H_ #include #include #include #include #include #include #include #include #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 #if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L using invoke_result = typename std::invoke_result; #else using invoke_result = typename std::result_of; #endif template using invoke_result_t = typename invoke_result::type; namespace guts { template typename std::enable_if< !std::is_array::value && !std::is_array::value && std::is_base_of::value, std::unique_ptr>::type make_unique_base(Args&&... args) { return std::unique_ptr(new Child(std::forward(args)...)); } #if defined(__cpp_lib_logical_traits) && !(defined(_MSC_VER) && _MSC_VER < 1920) template using conjunction = std::conjunction; template using disjunction = std::disjunction; template using bool_constant = std::bool_constant; template using negation = std::negation; #else // Implementation taken from http://en.cppreference.com/w/cpp/types/conjunction template struct conjunction : std::true_type {}; template struct conjunction : B1 {}; template struct conjunction : std::conditional_t, B1> {}; // Implementation taken from http://en.cppreference.com/w/cpp/types/disjunction template struct disjunction : std::false_type {}; template struct disjunction : B1 {}; template struct disjunction : std::conditional_t> {}; // Implementation taken from // http://en.cppreference.com/w/cpp/types/integral_constant template using bool_constant = std::integral_constant; // Implementation taken from http://en.cppreference.com/w/cpp/types/negation template struct negation : bool_constant {}; #endif #ifdef __cpp_lib_void_t template using void_t = std::void_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 struct make_void { typedef void type; }; template using void_t = typename make_void::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 #ifdef __cpp_lib_apply template CUDA_HOST_DEVICE inline constexpr decltype(auto) apply(F&& f, Tuple&& t) { return std::apply(std::forward(f), std::forward(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 #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) #else // GCC/Clang need the decltype() return type CUDA_HOST_DEVICE constexpr decltype(auto) apply_impl( F&& f, Tuple&& t, std::index_sequence) #endif { return std::forward(f)(std::get(std::forward(t))...); } } // namespace detail template CUDA_HOST_DEVICE constexpr decltype(auto) apply(F&& f, Tuple&& t) { return detail::apply_impl( std::forward(f), std::forward(t), std::make_index_sequence< std::tuple_size>::value>{}); } #endif #undef CUDA_HOST_DEVICE template typename std::enable_if< std::is_member_pointer::type>::value, typename c10::invoke_result_t>::type invoke(Functor&& f, Args&&... args) { return std::mem_fn(std::forward(f))(std::forward(args)...); } template typename std::enable_if< !std::is_member_pointer::type>::value, typename c10::invoke_result_t>::type invoke(Functor&& f, Args&&... args) { return std::forward(f)(std::forward(args)...); } namespace detail { struct _identity final { template using type_identity = T; template decltype(auto) operator()(T&& arg) { return std::forward(arg); } }; template 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 struct function_takes_identity_argument< Func, std::void_t()(_identity()))>> : std::true_type { }; #else template struct function_takes_identity_argument< Func, void_t()(_identity()))>> : std::true_type {}; #endif template struct _if_constexpr; template <> struct _if_constexpr final { template < class ThenCallback, class ElseCallback, std::enable_if_t< function_takes_identity_argument::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::value, void*> = nullptr> static decltype(auto) call( ThenCallback&& thenCallback, ElseCallback&& /* elseCallback */) { return thenCallback(); } }; template <> struct _if_constexpr final { template < class ThenCallback, class ElseCallback, std::enable_if_t< function_takes_identity_argument::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::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 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 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 * int func(T t) { * return if_constexpr::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() could return int and * func() could return string. * * Note: if_constexpr 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 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) 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)(detail::_identity()); } else { return static_cast(thenCallback)(); } } else { if constexpr (detail::function_takes_identity_argument< ElseCallback>::value) { return static_cast(elseCallback)(detail::_identity()); } else { return static_cast(elseCallback)(); } } #else // C++14 implementation of if constexpr return detail::_if_constexpr::call( static_cast(thenCallback), static_cast(elseCallback)); #endif } template 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) 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)(detail::_identity()); } else { return static_cast(thenCallback)(); } } #else // C++14 implementation of if constexpr return if_constexpr( static_cast(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 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 struct to_string_()))>> final { static std::string call(T value) { return std::to_string(value); } }; } // namespace detail template inline std::string to_string(T value) { return detail::to_string_::call(value); } template constexpr const T& min(const T& a, const T& b) { return (b < a) ? b : a; } template constexpr const T& max(const T& a, const T& b) { return (a < b) ? b : a; } } // namespace guts } // namespace c10 #endif // C10_UTIL_CPP17_H_