//@HEADER // ************************************************************************ // // Kokkos v. 4.0 // Copyright (2022) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. // See https://kokkos.org/LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //@HEADER #ifndef KOKKOS_FUNCTORANALYSIS_HPP #define KOKKOS_FUNCTORANALYSIS_HPP #include #include #include //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- namespace Kokkos { namespace Impl { struct FunctorPatternInterface { struct FOR {}; struct REDUCE {}; struct SCAN {}; }; template struct DeduceFunctorPatternInterface; template struct DeduceFunctorPatternInterface< ParallelFor> { using type = FunctorPatternInterface::FOR; }; template struct DeduceFunctorPatternInterface< ParallelReduce> { using type = FunctorPatternInterface::REDUCE; }; template struct DeduceFunctorPatternInterface< ParallelScan> { using type = FunctorPatternInterface::SCAN; }; template struct DeduceFunctorPatternInterface> { using type = FunctorPatternInterface::SCAN; }; /** \brief Query Functor and execution policy argument tag for value type. * * If 'value_type' is not explicitly declared in the functor and * OverrideValueType is void, then attempt to deduce the type from * FunctorType::operator() interface used by the pattern and policy. * * For the REDUCE pattern generate a Reducer and finalization function * derived from what is available within the functor. */ template struct FunctorAnalysis { private: using FOR = FunctorPatternInterface::FOR; using REDUCE = FunctorPatternInterface::REDUCE; using SCAN = FunctorPatternInterface::SCAN; //---------------------------------------- struct void_tag {}; template struct has_work_tag { using type = void; using wtag = void_tag; }; template struct has_work_tag::type> { using type = typename P::work_tag; using wtag = typename P::work_tag; }; using Tag = typename has_work_tag<>::type; using WTag = typename has_work_tag<>::wtag; //---------------------------------------- // Check for T::execution_space template struct has_execution_space { using type = void; enum : bool { value = false }; }; template struct has_execution_space< T, typename std::is_void::type> { using type = typename T::execution_space; enum : bool { value = true }; }; using policy_has_space = has_execution_space; using functor_has_space = has_execution_space; static_assert(!policy_has_space::value || !functor_has_space::value || std::is_same::value, "Execution Policy and Functor execution space must match"); //---------------------------------------- // Check for Functor::value_type, which is either a simple type T or T[] // If the functor doesn't have a value_type alias, use OverrideValueType. template struct has_value_type { using type = OverrideValueType; }; template struct has_value_type::type> { using type = typename F::value_type; static_assert(!std::is_reference::value && std::rank::value <= 1 && std::extent::value == 0, "Kokkos Functor::value_type is T or T[]"); }; //---------------------------------------- // If Functor::value_type does not exist and OverrideValueType is void, then // evaluate operator(), depending upon the pattern and whether the policy has // a work tag, to determine the reduction or scan value_type. template ::type, bool T = std::is_void::value> struct deduce_value_type { using type = V; }; template struct deduce_value_type { template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(M, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(M, M, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(M, M, M, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(M, M, M, M, M, M, M, M, A&) const); using type = decltype(deduce(&F::operator())); }; template struct deduce_value_type { template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag, M, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag, M, M, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag, M, M, M, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag, M, M, M, M, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag const&, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag const&, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag const&, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag const&, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag const&, M, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag const&, M, M, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag const&, M, M, M, M, M, M, M, A&) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag const&, M, M, M, M, M, M, M, M, A&) const); using type = decltype(deduce(&F::operator())); }; template struct deduce_value_type { template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(M, A&, I) const); using type = decltype(deduce(&F::operator())); }; template struct deduce_value_type { template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag, M, A&, I) const); template KOKKOS_INLINE_FUNCTION static A deduce(void (Functor::*)(WTag const&, M, A&, I) const); using type = decltype(deduce(&F::operator())); }; //---------------------------------------- using candidate_type = typename deduce_value_type::type; enum { candidate_is_void = std::is_void::value, candidate_is_array = std::rank::value == 1 }; //---------------------------------------- public: using execution_space = std::conditional_t>; using value_type = std::remove_extent_t; static_assert(!std::is_const::value, "Kokkos functor operator reduce argument cannot be const"); private: // Stub to avoid defining a type 'void &' using ValueType = std::conditional_t; public: using pointer_type = std::conditional_t; using reference_type = std::conditional_t< candidate_is_array, ValueType*, std::conditional_t>; private: template KOKKOS_INLINE_FUNCTION static constexpr std::enable_if_t get_length(FF const& f) { return f.value_count; } template KOKKOS_INLINE_FUNCTION static constexpr std::enable_if_t get_length(FF const&) { return candidate_is_void ? 0 : 1; } public: enum { StaticValueSize = !candidate_is_void && !candidate_is_array ? sizeof(ValueType) : 0 }; KOKKOS_FORCEINLINE_FUNCTION static constexpr unsigned int value_count( const Functor& f) { return FunctorAnalysis::template get_length(f); } KOKKOS_FORCEINLINE_FUNCTION static constexpr unsigned int value_size( const Functor& f) { return FunctorAnalysis::template get_length(f) * sizeof(ValueType); } //---------------------------------------- template KOKKOS_FORCEINLINE_FUNCTION static constexpr unsigned int value_count( const Unknown&) { return candidate_is_void ? 0 : 1; } template KOKKOS_FORCEINLINE_FUNCTION static constexpr unsigned int value_size( const Unknown&) { return candidate_is_void ? 0 : sizeof(ValueType); } private: //---------------------------------------- // parallel_reduce join operator template struct has_join_no_tag_function; template struct has_join_no_tag_function { using ref_type = ValueType&; using cref_type = const ValueType&; KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(ref_type, cref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ref_type, cref_type)); KOKKOS_INLINE_FUNCTION static void join(F const* const f, ValueType* dst, ValueType const* src) { f->join(*dst, *src); } }; template struct has_join_no_tag_function { using ref_type = ValueType*; using cref_type = const ValueType*; KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(ref_type, cref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ref_type, cref_type)); KOKKOS_INLINE_FUNCTION static void join(F const* const f, ValueType* dst, ValueType const* src) { f->join(dst, src); } }; template struct has_volatile_join_no_tag_function; template struct KOKKOS_DEPRECATED_WITH_COMMENT( "Reduce/scan join() taking `volatile`-qualified parameters is " "deprecated. Remove the `volatile` qualifier.") has_volatile_join_no_tag_function { using vref_type = volatile ValueType&; using cvref_type = const volatile ValueType&; KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(vref_type, cvref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(vref_type, cvref_type)); KOKKOS_INLINE_FUNCTION static void join(F const* const f, ValueType* dst, ValueType const* src) { f->join(*dst, *src); } }; template struct KOKKOS_DEPRECATED_WITH_COMMENT( "Reduce/scan join() taking `volatile`-qualified parameters is " "deprecated. Remove the `volatile` qualifier.") has_volatile_join_no_tag_function { using vref_type = volatile ValueType*; using cvref_type = const volatile ValueType*; KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(vref_type, cvref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(vref_type, cvref_type)); KOKKOS_INLINE_FUNCTION static void join(F const* const f, ValueType* dst, ValueType const* src) { f->join(dst, src); } }; template struct has_join_tag_function; template struct has_join_tag_function { using ref_type = ValueType&; using cref_type = const ValueType&; KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag, ref_type, cref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag, ref_type, cref_type)); KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag const&, ref_type, cref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag const&, ref_type, cref_type)); KOKKOS_INLINE_FUNCTION static void join(F const* const f, ValueType* dst, ValueType const* src) { f->join(WTag(), *dst, *src); } }; template struct has_join_tag_function { using ref_type = ValueType*; using cref_type = const ValueType*; KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag, ref_type, cref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag, ref_type, cref_type)); KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag const&, ref_type, cref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag const&, ref_type, cref_type)); KOKKOS_INLINE_FUNCTION static void join(F const* const f, ValueType* dst, ValueType const* src) { f->join(WTag(), dst, src); } }; template struct has_volatile_join_tag_function; template struct KOKKOS_DEPRECATED_WITH_COMMENT( "Reduce/scan join() taking `volatile`-qualified parameters is " "deprecated. Remove the `volatile` qualifier.") has_volatile_join_tag_function { using vref_type = volatile ValueType&; using cvref_type = const volatile ValueType&; KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag, vref_type, cvref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag, vref_type, cvref_type)); KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag const&, vref_type, cvref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag const&, vref_type, cvref_type)); KOKKOS_INLINE_FUNCTION static void join(F const* const f, ValueType* dst, ValueType const* src) { f->join(WTag(), *dst, *src); } }; template struct KOKKOS_DEPRECATED_WITH_COMMENT( "Reduce/scan join() taking `volatile`-qualified parameters is " "deprecated. Remove the `volatile` qualifier.") has_volatile_join_tag_function { using vref_type = volatile ValueType*; using cvref_type = const volatile ValueType*; KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag, vref_type, cvref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag, vref_type, cvref_type)); KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag const&, vref_type, cvref_type) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag const&, vref_type, cvref_type)); KOKKOS_INLINE_FUNCTION static void join(F const* const f, ValueType* dst, ValueType const* src) { f->join(WTag(), dst, src); } }; template struct detected_join_no_tag { enum : bool { value = false }; }; template struct detected_join_no_tag< F, decltype(has_join_no_tag_function::enable_if(&F::join))> { enum : bool { value = true }; }; template struct detected_volatile_join_no_tag { enum : bool { value = false }; }; template struct detected_volatile_join_no_tag< F, decltype(has_volatile_join_no_tag_function::enable_if(&F::join))> { enum : bool { value = true }; }; template struct detected_join_tag { enum : bool { value = false }; }; template struct detected_join_tag::enable_if( &F::join))> { enum : bool { value = true }; }; template struct detected_volatile_join_tag { enum : bool { value = false }; }; template struct detected_volatile_join_tag< F, decltype(has_volatile_join_tag_function::enable_if(&F::join))> { enum : bool { value = true }; }; template struct DeduceJoinNoTag { enum : bool { value = false }; KOKKOS_INLINE_FUNCTION static void join(F const* const f, ValueType* dst, ValueType const* src) { const int n = FunctorAnalysis::value_count(*f); for (int i = 0; i < n; ++i) dst[i] += src[i]; } }; template struct DeduceJoinNoTag::value || (!is_reducer::value && std::is_void::value)) && detected_join_no_tag::value>> : public has_join_no_tag_function { enum : bool { value = true }; }; template struct DeduceJoinNoTag< F, std::enable_if_t<(is_reducer::value || (!is_reducer::value && std::is_void::value)) && (!detected_join_no_tag::value && detected_volatile_join_no_tag::value)>> : public has_volatile_join_no_tag_function { enum : bool { value = true }; static_assert(Impl::dependent_false_v, "Reducer with a join() operator taking " "volatile-qualified parameters is no longer supported"); }; template struct DeduceJoin : public DeduceJoinNoTag {}; template struct DeduceJoin< F, std::enable_if_t::value && detected_join_tag::value>> : public has_join_tag_function { enum : bool { value = true }; }; template struct DeduceJoin::value && (!detected_join_tag::value && detected_volatile_join_tag::value)>> : public has_volatile_join_tag_function { enum : bool { value = true }; static_assert(Impl::dependent_false_v, "Reducer with a join() operator taking " "volatile-qualified parameters is no longer supported"); }; //---------------------------------------- template struct has_init_no_tag_function; template struct has_init_no_tag_function { KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(ValueType&) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ValueType&)); KOKKOS_INLINE_FUNCTION static void init(F const* const f, ValueType* dst) { f->init(*dst); } }; template struct has_init_no_tag_function { KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(ValueType*) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ValueType*)); KOKKOS_INLINE_FUNCTION static void init(F const* const f, ValueType* dst) { f->init(dst); } }; template struct has_init_tag_function; template struct has_init_tag_function { KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag, ValueType&) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag const&, ValueType&) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag, ValueType&)); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag const&, ValueType&)); KOKKOS_INLINE_FUNCTION static void init(F const* const f, ValueType* dst) { f->init(WTag(), *dst); } }; template struct has_init_tag_function { KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag, ValueType*) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag const&, ValueType*) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag, ValueType*)); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag const&, ValueType*)); KOKKOS_INLINE_FUNCTION static void init(F const* const f, ValueType* dst) { f->init(WTag(), dst); } }; template struct DeduceInitNoTag { enum : bool { value = false }; KOKKOS_INLINE_FUNCTION static void init(F const* const f, ValueType* dst) { const int n = FunctorAnalysis::value_count(*f); for (int i = 0; i < n; ++i) new (&dst[i]) ValueType(); } }; template struct DeduceInitNoTag< F, std::enable_if_t::value || (!is_reducer::value && std::is_void::value), decltype(has_init_no_tag_function::enable_if( &F::init))>> : public has_init_no_tag_function { enum : bool { value = true }; }; template struct DeduceInit : public DeduceInitNoTag {}; template struct DeduceInit< F, std::enable_if_t::value, decltype(has_init_tag_function::enable_if(&F::init))>> : public has_init_tag_function { enum : bool { value = true }; }; //---------------------------------------- template struct has_final_no_tag_function; // No tag, not array template struct has_final_no_tag_function { KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(ValueType&) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ValueType&)); KOKKOS_INLINE_FUNCTION static void final(F const* const f, ValueType* dst) { f->final(*dst); } }; // No tag, is array template struct has_final_no_tag_function { KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(ValueType*) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ValueType*)); KOKKOS_INLINE_FUNCTION static void final(F const* const f, ValueType* dst) { f->final(dst); } }; template struct has_final_tag_function; // Has tag, not array template struct has_final_tag_function { KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag, ValueType&) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag const&, ValueType&) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag, ValueType&)); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag const&, ValueType&)); KOKKOS_INLINE_FUNCTION static void final(F const* const f, ValueType* dst) { f->final(WTag(), *dst); } }; // Has tag, is array template struct has_final_tag_function { KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag, ValueType*) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (F::*)(WTag const&, ValueType*) const); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag, ValueType*)); KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(WTag const&, ValueType*)); KOKKOS_INLINE_FUNCTION static void final(F const* const f, ValueType* dst) { f->final(WTag(), dst); } }; template struct DeduceFinalNoTag { enum : bool { value = false }; KOKKOS_INLINE_FUNCTION static void final(F const* const, ValueType*) {} }; template struct DeduceFinalNoTag< F, std::enable_if_t::value || (!is_reducer::value && std::is_void::value), decltype(has_final_no_tag_function::enable_if( &F::final))>> : public has_final_no_tag_function { enum : bool { value = true }; }; template struct DeduceFinal : public DeduceFinalNoTag {}; template struct DeduceFinal::value, decltype(has_final_tag_function< F>::enable_if(&F::final))>> : public has_final_tag_function { enum : bool { value = true }; }; //---------------------------------------- template struct DeduceTeamShmem { enum : bool { value = false }; static size_t team_shmem_size(F const&, int) { return 0; } }; template struct DeduceTeamShmem> { enum : bool { value = true }; static size_t team_shmem_size(F const* const f, int team_size) { return f->team_shmem_size(team_size); } }; template struct DeduceTeamShmem> { enum : bool { value = true }; static size_t team_shmem_size(F const* const f, int team_size) { return f->shmem_size(team_size); } }; //---------------------------------------- public: inline static size_t team_shmem_size(Functor const& f) { return DeduceTeamShmem<>::team_shmem_size(f); } //---------------------------------------- enum { has_join_member_function = DeduceJoin<>::value }; enum { has_init_member_function = DeduceInit<>::value }; enum { has_final_member_function = DeduceFinal<>::value }; static_assert((Kokkos::is_reducer::value && has_join_member_function) || !Kokkos::is_reducer::value, "Reducer must have a join member function!"); struct Reducer { private: Functor m_functor; template KOKKOS_INLINE_FUNCTION constexpr std::enable_if_t len() const noexcept { return m_functor.value_count; } template KOKKOS_INLINE_FUNCTION constexpr std::enable_if_t len() const noexcept { return candidate_is_void ? 0 : 1; } public: using reducer = Reducer; using value_type = std::remove_const_t; using pointer_type = value_type*; using reference_type = FunctorAnalysis::reference_type; using functor_type = Functor; // Adapts a functor static constexpr bool has_join_member_function() { return DeduceJoin<>::value; } static constexpr bool has_init_member_function() { return DeduceInit<>::value; } static constexpr bool has_final_member_function() { return DeduceFinal<>::value; } KOKKOS_FUNCTION unsigned int value_size() const { return FunctorAnalysis::value_size(m_functor); } KOKKOS_FUNCTION unsigned int value_count() const { return FunctorAnalysis::value_count(m_functor); } KOKKOS_FUNCTION static constexpr unsigned int static_value_size() { return StaticValueSize; } template KOKKOS_INLINE_FUNCTION static std::enable_if_t reference(ValueType* dst) noexcept { return dst; } template KOKKOS_INLINE_FUNCTION static std::enable_if_t reference(ValueType* dst) noexcept { return *dst; } KOKKOS_INLINE_FUNCTION constexpr int length() const noexcept { return Reducer::template len(); } KOKKOS_INLINE_FUNCTION void copy(ValueType* const dst, ValueType const* const src) const noexcept { for (int i = 0; i < Reducer::template len(); ++i) dst[i] = src[i]; } KOKKOS_INLINE_FUNCTION void join(ValueType* dst, ValueType const* src) const noexcept { DeduceJoin<>::join(&m_functor, dst, src); } KOKKOS_INLINE_FUNCTION reference_type init(ValueType* const dst) const noexcept { DeduceInit<>::init(&m_functor, dst); return reference(dst); } KOKKOS_INLINE_FUNCTION void final(ValueType* dst) const noexcept { DeduceFinal<>::final(&m_functor, dst); } KOKKOS_INLINE_FUNCTION const Functor& get_functor() const { return m_functor; } Reducer(Reducer const&) = default; Reducer(Reducer&&) = default; Reducer& operator=(Reducer const&) = delete; Reducer& operator=(Reducer&&) = delete; ~Reducer() = default; KOKKOS_INLINE_FUNCTION explicit constexpr Reducer( Functor const& arg_functor) noexcept : m_functor(arg_functor) {} }; }; } // namespace Impl } // namespace Kokkos //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- #endif /* KOKKOS_FUNCTORANALYSIS_HPP */