/*! \file thrust/zip_function.h * \brief Adaptor type that turns an N-ary function object into one that takes * a tuple of size N so it can easily be used with algorithms taking zip * iterators */ #pragma once #include #if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) # pragma GCC system_header #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) # pragma clang system_header #elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) # pragma system_header #endif // no system header #include #include #if !defined(THRUST_LEGACY_GCC) # include # include # include THRUST_NAMESPACE_BEGIN /*! \addtogroup function_objects Function Objects * \{ */ /*! \addtogroup function_object_adaptors Function Object Adaptors * \ingroup function_objects * \{ */ namespace detail { namespace zip_detail { // Add workaround for decltype(auto) on C++11-only compilers: # if _CCCL_STD_VER >= 2014 _CCCL_EXEC_CHECK_DISABLE template _CCCL_HOST_DEVICE decltype(auto) apply_impl(Function&& func, Tuple&& args, index_sequence) { return func(thrust::get(THRUST_FWD(args))...); } template _CCCL_HOST_DEVICE decltype(auto) apply(Function&& func, Tuple&& args) { constexpr auto tuple_size = thrust::tuple_size::type>::value; return apply_impl(THRUST_FWD(func), THRUST_FWD(args), make_index_sequence{}); } # else // _CCCL_STD_VER _CCCL_EXEC_CHECK_DISABLE template _CCCL_HOST_DEVICE auto apply_impl(Function&& func, Tuple&& args, index_sequence) THRUST_DECLTYPE_RETURNS(func(thrust::get(THRUST_FWD(args))...)) template _CCCL_HOST_DEVICE auto apply(Function&& func, Tuple&& args) THRUST_DECLTYPE_RETURNS(apply_impl( THRUST_FWD(func), THRUST_FWD(args), make_index_sequence::type>::value>{})) # endif // _CCCL_STD_VER } // namespace zip_detail } // namespace detail /*! \p zip_function is a function object that allows the easy use of N-ary * function objects with \p zip_iterators without redefining them to take a * \p tuple instead of N arguments. * * This means that if a functor that takes 2 arguments which could be used with * the \p transform function and \p device_iterators can be extended to take 3 * arguments and \p zip_iterators without rewriting the functor in terms of * \p tuple. * * The \p make_zip_function convenience function is provided to avoid having * to explicitely define the type of the functor when creating a \p zip_function, * whic is especially helpful when using lambdas as the functor. * * \code * #include * #include * #include * #include * * struct SumTuple { * float operator()(auto tup) const { * return thrust::get<0>(tup) + thrust::get<1>(tup) + thrust::get<2>(tup); * } * }; * struct SumArgs { * float operator()(float a, float b, float c) const { * return a + b + c; * } * }; * * int main() { * thrust::device_vector A{0.f, 1.f, 2.f}; * thrust::device_vector B{1.f, 2.f, 3.f}; * thrust::device_vector C{2.f, 3.f, 4.f}; * thrust::device_vector D(3); * * auto begin = thrust::make_zip_iterator(thrust::make_tuple(A.begin(), B.begin(), C.begin())); * auto end = thrust::make_zip_iterator(thrust::make_tuple(A.end(), B.end(), C.end())); * * // The following four invocations of transform are equivalent: * // Transform with 3-tuple * thrust::transform(begin, end, D.begin(), SumTuple{}); * * // Transform with 3 parameters * thrust::zip_function adapted{}; * thrust::transform(begin, end, D.begin(), adapted); * * // Transform with 3 parameters with convenience function * thrust::transform(begin, end, D.begin(), thrust::make_zip_function(SumArgs{})); * * // Transform with 3 parameters with convenience function and lambda * thrust::transform(begin, end, D.begin(), thrust::make_zip_function([] (float a, float b, float c) { * return a + b + c; * })); * return 0; * } * \endcode * * \see make_zip_function * \see zip_iterator */ template class zip_function { public: //! Default constructs the contained function object. zip_function() = default; _CCCL_HOST_DEVICE zip_function(Function func) : func(std::move(func)) {} // Add workaround for decltype(auto) on C++11-only compilers: # if _CCCL_STD_VER >= 2014 template _CCCL_HOST_DEVICE decltype(auto) operator()(Tuple&& args) const { return detail::zip_detail::apply(func, THRUST_FWD(args)); } # else // _CCCL_STD_VER // Can't just use THRUST_DECLTYPE_RETURNS here since we need to use // std::declval for the signature components: template _CCCL_HOST_DEVICE auto operator()(Tuple&& args) const noexcept(noexcept(detail::zip_detail::apply(std::declval(), THRUST_FWD(args)))) THRUST_TRAILING_RETURN(decltype(detail::zip_detail::apply(std::declval(), THRUST_FWD(args)))) { return detail::zip_detail::apply(func, THRUST_FWD(args)); } # endif // _CCCL_STD_VER //! Returns a reference to the underlying function. _CCCL_HOST_DEVICE Function& underlying_function() const { return func; } private: mutable Function func; }; /*! \p make_zip_function creates a \p zip_function from a function object. * * \param fun The N-ary function object. * \return A \p zip_function that takes a N-tuple. * * \see zip_function */ template _CCCL_HOST_DEVICE zip_function::type> make_zip_function(Function&& fun) { using func_t = typename std::decay::type; return zip_function(THRUST_FWD(fun)); } /*! \} // end function_object_adaptors */ /*! \} // end function_objects */ THRUST_NAMESPACE_END #endif