//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //============================================================================ #ifndef vtk_m_ArrayHandleDecorator_h #define vtk_m_ArrayHandleDecorator_h #include #include #include #include #include #include #include #include #include namespace vtkm { namespace cont { namespace internal { namespace decor { // Generic InverseFunctor implementation that does nothing. struct NoOpInverseFunctor { NoOpInverseFunctor() = default; template VTKM_EXEC_CONT NoOpInverseFunctor(Ts...) { } template VTKM_EXEC_CONT void operator()(vtkm::Id, VT) const { } }; } // namespace decor // The portal for ArrayHandleDecorator. Get calls FunctorType::operator(), and // Set calls InverseFunctorType::operator(), but only if the DecoratorImpl // provides an inverse. template class VTKM_ALWAYS_EXPORT ArrayPortalDecorator { public: using ValueType = ValueType_; using FunctorType = FunctorType_; using InverseFunctorType = InverseFunctorType_; using ReadOnly = std::is_same; VTKM_EXEC_CONT ArrayPortalDecorator() {} VTKM_CONT ArrayPortalDecorator(FunctorType func, InverseFunctorType iFunc, vtkm::Id numValues) : Functor{ func } , InverseFunctor{ iFunc } , NumberOfValues{ numValues } { } VTKM_EXEC_CONT vtkm::Id GetNumberOfValues() const { return this->NumberOfValues; } VTKM_EXEC_CONT ValueType Get(vtkm::Id index) const { return this->Functor(index); } template ::type> VTKM_EXEC_CONT void Set(vtkm::Id index, const ValueType& value) const { this->InverseFunctor(index, value); } private: FunctorType Functor; InverseFunctorType InverseFunctor; vtkm::Id NumberOfValues; }; namespace decor { // Ensures that all types in variadic container ArrayHandleList are subclasses // of ArrayHandleBase. template using AllAreArrayHandles = brigand::all>; namespace detail { // Tests whether DecoratorImplT has a CreateInverseFunctor(Portals...) method. template struct IsFunctorInvertibleImpl; template class List, typename... PortalTs> struct IsFunctorInvertibleImpl> { private: using PortalList = brigand::list::type...>; template < typename T, typename U = decltype(std::declval().CreateInverseFunctor(std::declval()...))> static std::true_type InverseExistsTest(int); template static std::false_type InverseExistsTest(...); public: using type = decltype(InverseExistsTest(0)); }; // Deduces the type returned by DecoratorImplT::CreateFunctor when given // the specified portals. template struct GetFunctorTypeImpl; template class List, typename... PortalTs> struct GetFunctorTypeImpl> { using type = decltype(std::declval().CreateFunctor(std::declval()...)); }; // Deduces the type returned by DecoratorImplT::CreateInverseFunctor when given // the specified portals. If DecoratorImplT doesn't have a CreateInverseFunctor // method, a NoOp functor will be used instead. template struct GetInverseFunctorTypeImpl; template class List, typename... PortalTs> struct GetInverseFunctorTypeImpl> { using type = decltype(std::declval().CreateInverseFunctor(std::declval()...)); }; template struct GetInverseFunctorTypeImpl { using type = NoOpInverseFunctor; }; // Get appropriate portals from a source array. // See note below about using non-writable portals in invertible functors. // We need to sub in const portals when writable ones don't exist. template typename std::decay::type::PortalControl GetPortalControlImpl(std::true_type, ArrayT&& array) { return array.GetPortalControl(); } template typename std::decay::type::PortalConstControl GetPortalControlImpl(std::false_type, ArrayT&& array) { return array.GetPortalConstControl(); } template typename std::decay::type::template ExecutionTypes::Portal GetPortalInPlaceImpl(std::true_type, ArrayT&& array, Device) { return array.PrepareForInPlace(Device{}); } template typename std::decay::type::template ExecutionTypes::PortalConst GetPortalInPlaceImpl(std::false_type, ArrayT&& array, Device) { // ArrayT is read-only -- prepare for input instead. return array.PrepareForInput(Device{}); } template typename std::decay::type::template ExecutionTypes::Portal GetPortalOutputImpl(std::true_type, ArrayT&& array, Device) { // Prepare these for inplace usage instead -- we'll likely need to read // from these in addition to writing. return array.PrepareForInPlace(Device{}); } template typename std::decay::type::template ExecutionTypes::PortalConst GetPortalOutputImpl(std::false_type, ArrayT&& array, Device) { // ArrayT is read-only -- prepare for input instead. return array.PrepareForInput(Device{}); } } // namespace detail // Get portal types: // We allow writing to an AHDecorator if *any* of the ArrayHandles are writable. // This means we have to avoid calling PrepareForOutput, etc on non-writable // array handles, since these may throw. On non-writable handles, use the // const array handles so we can at least read from them in the inverse // functors. template ::type::PortalControl, typename PortalConst = typename std::decay::type::PortalConstControl> using GetPortalControlType = typename brigand::if_, Portal, PortalConst>::type; template using GetPortalConstControlType = typename std::decay::type::PortalConstControl; template ::type::template ExecutionTypes::Portal, typename PortalConst = typename std::decay::type::template ExecutionTypes::PortalConst> using GetPortalExecutionType = typename brigand::if_, Portal, PortalConst>::type; template using GetPortalConstExecutionType = typename std::decay::type::template ExecutionTypes::PortalConst; // Get portal objects: // See note above -- we swap in const portals sometimes. template GetPortalControlType::type> GetPortalControl(ArrayT&& array) { return detail::GetPortalControlImpl(IsWritableArrayHandle{}, std::forward(array)); } template GetPortalConstControlType::type> GetPortalConstControl( const ArrayT& array) { return array.GetPortalConstControl(); } template GetPortalConstExecutionType::type, Device> GetPortalInput( const ArrayT& array, Device) { return array.PrepareForInput(Device{}); } template GetPortalExecutionType::type, Device> GetPortalInPlace(ArrayT&& array, Device) { return detail::GetPortalInPlaceImpl( IsWritableArrayHandle{}, std::forward(array), Device{}); } template GetPortalExecutionType::type, Device> GetPortalOutput(ArrayT&& array, Device) { return detail::GetPortalOutputImpl( IsWritableArrayHandle{}, std::forward(array), Device{}); } // Equivalent to std::true_type if *any* portal in PortalList can be written to. // If all are read-only, std::false_type is used instead. template using AnyPortalIsWritable = typename brigand::any>::type; // Set to std::true_type if DecoratorImplT::CreateInverseFunctor can be called // with the supplied portals, or std::false_type otherwise. template using IsFunctorInvertible = typename detail::IsFunctorInvertibleImpl::type; // std::true_type/std::false_type depending on whether the decorator impl has a // CreateInversePortal method AND any of the arrays are writable. template using CanWriteToFunctor = typename brigand::and_, AnyPortalIsWritable>::type; // The FunctorType for the provided implementation and portal types. template using GetFunctorType = typename detail::GetFunctorTypeImpl::type; // The InverseFunctorType for the provided implementation and portal types. // Will detect when inversion is not possible and return a NoOp functor instead. template using GetInverseFunctorType = typename detail::GetInverseFunctorTypeImpl, DecoratorImplT, PortalList>::type; // Convert a sequence of array handle types to a list of portals: // Some notes on this implementation: // - MSVC 2015 ICEs when using brigand::transform to convert a brigand::list // of arrayhandles to portals. So instead we pass the ArrayTs. // - Just using brigand::list...> fails, as // apparently that is an improper parameter pack expansion // - So we jump through some decltype/declval hoops here to get this to work: template using GetPortalConstControlList = brigand::list())))...>; template using GetPortalConstExecutionList = brigand::list(), Device{})))...>; template using GetPortalControlList = brigand::list())))...>; template using GetPortalExecutionList = brigand::list(), Device{})))...>; template struct DecoratorStorageTraits { using ArrayList = brigand::list; VTKM_STATIC_ASSERT_MSG(sizeof...(ArrayTs) > 0, "Must specify at least one source array handle for " "ArrayHandleDecorator. Consider using " "ArrayHandleImplicit instead."); // Need to check this here, since this traits struct is used in the // ArrayHandleDecorator superclass definition before any other // static_asserts could be used. VTKM_STATIC_ASSERT_MSG(decor::AllAreArrayHandles::value, "Trailing template parameters for " "ArrayHandleDecorator must be a list of ArrayHandle " "types."); using ArrayTupleType = vtkmstd::tuple; // size_t integral constants that index ArrayTs: using IndexList = brigand::make_sequence, sizeof...(ArrayTs)>; // Portal lists: // NOTE we have to pass the parameter pack here instead of using ArrayList // with brigand::transform, since that's causing MSVC 2015 to ice: using PortalControlList = GetPortalControlList; using PortalConstControlList = GetPortalConstControlList; template using PortalExecutionList = GetPortalExecutionList; template using PortalConstExecutionList = GetPortalConstExecutionList; // Functors: using FunctorControlType = GetFunctorType; using FunctorConstControlType = GetFunctorType; template using FunctorExecutionType = GetFunctorType>; template using FunctorConstExecutionType = GetFunctorType>; // Inverse functors: using InverseFunctorControlType = GetInverseFunctorType; using InverseFunctorConstControlType = NoOpInverseFunctor; template using InverseFunctorExecutionType = GetInverseFunctorType>; template using InverseFunctorConstExecutionType = NoOpInverseFunctor; // Misc: // ValueType is derived from DecoratorImplT::CreateFunctor(...)'s operator(). using ValueType = decltype(std::declval()(0)); // Decorator portals: using PortalControlType = ArrayPortalDecorator; using PortalConstControlType = ArrayPortalDecorator; template using PortalExecutionType = ArrayPortalDecorator, InverseFunctorExecutionType>; template using PortalConstExecutionType = ArrayPortalDecorator, InverseFunctorConstExecutionType>; // helper for constructing portals with the appropriate functors. This is // where we decide whether or not to call `CreateInverseFunctor` on the // implementation class. // Do not use these directly, they are helpers for the MakePortal[...] // methods below. template VTKM_CONT static typename std::enable_if::type CreatePortalDecorator(vtkm::Id numVals, const DecoratorImplT& impl, PortalTs&&... portals) { // Portal is read only: return { impl.CreateFunctor(std::forward(portals)...), typename DecoratorPortalType::InverseFunctorType{}, numVals }; } template VTKM_CONT static typename std::enable_if::type CreatePortalDecorator(vtkm::Id numVals, const DecoratorImplT& impl, PortalTs... portals) { // Portal is read/write: return { impl.CreateFunctor(portals...), impl.CreateInverseFunctor(portals...), numVals }; } // Portal construction methods. These actually create portals. template