//============================================================================ // 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. // // Copyright 2015 National Technology & Engineering Solutions of Sandia, LLC (NTESS). // Copyright 2015 UT-Battelle, LLC. // Copyright 2015 Los Alamos National Security. // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National // Laboratory (LANL), the U.S. Government retains certain rights in // this software. //============================================================================ #ifndef vtk_m_cont_DynamicCellSet_h #define vtk_m_cont_DynamicCellSet_h #include #include #include #include #include #include namespace vtkm { namespace cont { // Forward declaration. template class DynamicCellSetBase; namespace detail { // One instance of a template class cannot access the private members of // another instance of a template class. However, I want to be able to copy // construct a DynamicCellSet from another DynamicCellSet of any other type. // Since you cannot partially specialize friendship, use this accessor class to // get at the internals for the copy constructor. struct DynamicCellSetCopyHelper { template VTKM_CONT static const std::shared_ptr& GetCellSetContainer(const vtkm::cont::DynamicCellSetBase& src) { return src.CellSetContainer; } }; // A simple function to downcast a CellSet encapsulated in a // SimplePolymorphicContainerBase to the given subclass of CellSet. If the // conversion cannot be done, nullptr is returned. template VTKM_CONT CellSetType* DynamicCellSetTryCast( vtkm::cont::internal::SimplePolymorphicContainerBase* cellSetContainer) { vtkm::cont::internal::SimplePolymorphicContainer* downcastContainer = dynamic_cast*>(cellSetContainer); if (downcastContainer != nullptr) { return &downcastContainer->Item; } else { return nullptr; } } template VTKM_CONT CellSetType* DynamicCellSetTryCast( const std::shared_ptr& cellSetContainer) { return detail::DynamicCellSetTryCast(cellSetContainer.get()); } } // namespace detail /// \brief Holds a cell set without having to specify concrete type. /// /// \c DynamicCellSet holds a \c CellSet object using runtime polymorphism to /// manage different subclass types and template parameters of the subclasses /// rather than compile-time templates. This adds a programming convenience /// that helps avoid a proliferation of templates. It also provides the /// management necessary to interface VTK-m with data sources where types will /// not be known until runtime. /// /// To interface between the runtime polymorphism and the templated algorithms /// in VTK-m, \c DynamicCellSet contains a method named \c CastAndCall that /// will determine the correct type from some known list of cell set types. /// This mechanism is used internally by VTK-m's worklet invocation mechanism /// to determine the type when running algorithms. /// /// By default, \c DynamicCellSet will assume that the value type in the array /// matches one of the types specified by \c VTKM_DEFAULT_CELL_SET_LIST_TAG. /// This list can be changed by using the \c ResetTypeList method. It is /// worthwhile to match these lists closely to the possible types that might be /// used. If a type is missing you will get a runtime error. If there are more /// types than necessary, then the template mechanism will create a lot of /// object code that is never used, and keep in mind that the number of /// combinations grows exponentially when using multiple \c Dynamic* objects. /// /// The actual implementation of \c DynamicCellSet is in a templated class /// named \c DynamicCellSetBase, which is templated on the list of cell set /// types. \c DynamicCellSet is really just a typedef of \c DynamicCellSetBase /// with the default cell set list. /// template class VTKM_ALWAYS_EXPORT DynamicCellSetBase { VTKM_IS_LIST_TAG(CellSetList); public: VTKM_CONT DynamicCellSetBase() {} template VTKM_CONT DynamicCellSetBase(const CellSetType& cellSet) : CellSetContainer(new vtkm::cont::internal::SimplePolymorphicContainer(cellSet)) { VTKM_IS_CELL_SET(CellSetType); } VTKM_CONT DynamicCellSetBase(const DynamicCellSetBase& src) : CellSetContainer(src.CellSetContainer) { } template VTKM_CONT explicit DynamicCellSetBase(const DynamicCellSetBase& src) : CellSetContainer(detail::DynamicCellSetCopyHelper::GetCellSetContainer(src)) { } VTKM_CONT ~DynamicCellSetBase() {} VTKM_CONT vtkm::cont::DynamicCellSetBase& operator=( const vtkm::cont::DynamicCellSetBase& src) { this->CellSetContainer = src.CellSetContainer; return *this; } /// Returns true if this cell set is of the provided type. /// template VTKM_CONT bool IsType() const { return (detail::DynamicCellSetTryCast(this->CellSetContainer) != nullptr); } /// Returns true if this cell set is the same (or equivalent) type as the /// object provided. /// template VTKM_CONT bool IsSameType(const CellSetType&) const { return this->IsType(); } /// Returns the contained cell set as the abstract \c CellSet type. /// VTKM_CONT const vtkm::cont::CellSet& CastToBase() const { return *reinterpret_cast(this->CellSetContainer->GetVoidPointer()); } /// Returns this cell set cast to the given \c CellSet type. Throws \c /// ErrorBadType if the cast does not work. Use \c IsType to check if /// the cast can happen. /// template VTKM_CONT CellSetType& Cast() const { CellSetType* cellSetPointer = detail::DynamicCellSetTryCast(this->CellSetContainer); if (cellSetPointer == nullptr) { VTKM_LOG_CAST_FAIL(*this, CellSetType); throw vtkm::cont::ErrorBadType("Bad cast of dynamic cell set."); } VTKM_LOG_CAST_SUCC(*this, *cellSetPointer); return *cellSetPointer; } /// Given a reference to a concrete \c CellSet object, attempt to downcast /// the contain cell set to the provided type and copy into the given \c /// CellSet object. Throws \c ErrorBadType if the cast does not work. /// Use \c IsType to check if the cast can happen. /// /// Note that this is a shallow copy. Any data in associated arrays are not /// copied. /// template VTKM_CONT void CopyTo(CellSetType& cellSet) const { cellSet = this->Cast(); } /// Changes the cell set types to try casting to when resolving this dynamic /// cell set, which is specified with a list tag like those in /// CellSetListTag.h. Since C++ does not allow you to actually change the /// template arguments, this method returns a new dynamic cell setobject. /// This method is particularly useful to narrow down (or expand) the types /// when using a cell set of particular constraints. /// template VTKM_CONT DynamicCellSetBase ResetCellSetList( NewCellSetList = NewCellSetList()) const { VTKM_IS_LIST_TAG(NewCellSetList); return DynamicCellSetBase(*this); } /// Attempts to cast the held cell set to a specific concrete type, then call /// the given functor with the cast cell set. The cell sets tried in the cast /// are those in the \c CellSetList template argument of the \c /// DynamicCellSetBase class (or \c VTKM_DEFAULT_CELL_SET_LIST_TAG for \c /// DynamicCellSet). You can use \c ResetCellSetList to get different /// behavior from \c CastAndCall. /// template VTKM_CONT void CastAndCall(Functor&& f, Args&&...) const; /// \brief Create a new cell set of the same type as this cell set. /// /// This method creates a new cell setthat is the same type as this one and /// returns a new dynamic cell set for it. This method is convenient when /// creating output data sets that should be the same type as some input cell /// set. /// VTKM_CONT DynamicCellSetBase NewInstance() const { DynamicCellSetBase newCellSet; newCellSet.CellSetContainer = this->CellSetContainer->NewInstance(); return newCellSet; } VTKM_CONT std::string GetName() const { return this->CastToBase().GetName(); } VTKM_CONT vtkm::Id GetNumberOfCells() const { return this->CastToBase().GetNumberOfCells(); } VTKM_CONT vtkm::Id GetNumberOfFaces() const { return this->CastToBase().GetNumberOfFaces(); } VTKM_CONT vtkm::Id GetNumberOfEdges() const { return this->CastToBase().GetNumberOfEdges(); } VTKM_CONT vtkm::Id GetNumberOfPoints() const { return this->CastToBase().GetNumberOfPoints(); } VTKM_CONT void PrintSummary(std::ostream& stream) const { return this->CastToBase().PrintSummary(stream); } private: std::shared_ptr CellSetContainer; friend struct detail::DynamicCellSetCopyHelper; }; namespace detail { struct DynamicCellSetTry { DynamicCellSetTry( const vtkm::cont::internal::SimplePolymorphicContainerBase* const cellSetContainer) : Container(cellSetContainer) { } template void operator()(CellSetType, Functor&& f, bool& called, Args&&... args) const { using downcastType = const vtkm::cont::internal::SimplePolymorphicContainer* const; if (!called) { downcastType downcastContainer = dynamic_cast(this->Container); if (downcastContainer) { VTKM_LOG_CAST_SUCC(*this->Container, *downcastContainer); f(downcastContainer->Item, std::forward(args)...); called = true; } } } const vtkm::cont::internal::SimplePolymorphicContainerBase* const Container; }; } // namespace detail template template VTKM_CONT void DynamicCellSetBase::CastAndCall(Functor&& f, Args&&... args) const { bool called = false; detail::DynamicCellSetTry tryCellSet(this->CellSetContainer.get()); vtkm::ListForEach( tryCellSet, CellSetList{}, std::forward(f), called, std::forward(args)...); if (!called) { VTKM_LOG_CAST_FAIL(*this, CellSetList); throw vtkm::cont::ErrorBadValue("Could not find appropriate cast for cell set."); } } using DynamicCellSet = DynamicCellSetBase; namespace internal { template struct DynamicTransformTraits> { using DynamicTag = vtkm::cont::internal::DynamicTransformTagCastAndCall; }; } // namespace internal namespace internal { /// Checks to see if the given object is a dynamic cell set. It contains a /// typedef named \c type that is either std::true_type or std::false_type. /// Both of these have a typedef named value with the respective boolean value. /// template struct DynamicCellSetCheck { using type = std::false_type; }; template struct DynamicCellSetCheck> { using type = std::true_type; }; #define VTKM_IS_DYNAMIC_CELL_SET(T) \ VTKM_STATIC_ASSERT(::vtkm::cont::internal::DynamicCellSetCheck::type::value) #define VTKM_IS_DYNAMIC_OR_STATIC_CELL_SET(T) \ VTKM_STATIC_ASSERT(::vtkm::cont::internal::CellSetCheck::type::value || \ ::vtkm::cont::internal::DynamicCellSetCheck::type::value) } // namespace internal } } // namespace vtkm::cont //============================================================================= // Specializations of serialization related classes namespace diy { namespace internal { struct DynamicCellSetSerializeFunctor { template void operator()(const CellSetType& cs, BinaryBuffer& bb) const { diy::save(bb, vtkm::cont::TypeString::Get()); diy::save(bb, cs); } }; template struct DynamicCellSetDeserializeFunctor { template void operator()(CellSetType, vtkm::cont::DynamicCellSetBase& dh, const std::string& typeString, bool& success, BinaryBuffer& bb) const { if (!success && (typeString == vtkm::cont::TypeString::Get())) { CellSetType cs; diy::load(bb, cs); dh = vtkm::cont::DynamicCellSetBase(cs); success = true; } } }; } // internal template struct Serialization> { private: using Type = vtkm::cont::DynamicCellSetBase; public: static VTKM_CONT void save(BinaryBuffer& bb, const Type& obj) { obj.CastAndCall(internal::DynamicCellSetSerializeFunctor{}, bb); } static VTKM_CONT void load(BinaryBuffer& bb, Type& obj) { std::string typeString; diy::load(bb, typeString); bool success = false; vtkm::ListForEach(internal::DynamicCellSetDeserializeFunctor{}, CellSetTypes{}, obj, typeString, success, bb); if (!success) { throw vtkm::cont::ErrorBadType("Error deserializing DynamicCellSet. Message TypeString: " + typeString); } } }; } // diy #endif //vtk_m_cont_DynamicCellSet_h