//============================================================================ // 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_cont_UnknownCellSet_h #define vtk_m_cont_UnknownCellSet_h #include #include #include #include #include namespace vtkm { namespace cont { // Forward declaration. template class UncertainCellSet; /// \brief A CellSet of an unknown type. /// /// `UnknownCellSet` holds a `CellSet` object using runtime polymorphism to manage /// the dynamic type rather than compile-time templates. This adds a programming /// convenience that helps avoid a proliferation of templates. /// /// To interface between the runtime polymorphism and the templated algorithms /// in VTK-m, `UnknownCellSet` contains a method named `CastAndCallForTypes` that /// determines the correct type from some known list of types. This mechanism is /// used internally by VTK-m's worklet invocation mechanism to determine the type /// when running algorithms. /// /// If the `UnknownCellSet` is used in a context where the possible cell set types /// can be whittled down to a finite list, you can specify lists of cell set types /// using the `ResetCellSetList` method. This will convert this object to an /// `UncertainCellSet` of the given types. In cases where a finite set of types /// are needed but there is no subset, `VTKM_DEFAULT_CELL_SET_LIST` /// class VTKM_CONT_EXPORT UnknownCellSet { std::shared_ptr Container; void InitializeKnownOrUnknownCellSet(const UnknownCellSet& cellSet, std::true_type vtkmNotUsed(isUnknownCellSet)) { *this = cellSet; } template void InitializeKnownOrUnknownCellSet(const CellSetType& cellSet, std::false_type vtkmNotUsed(isUnknownCellSet)) { VTKM_IS_CELL_SET(CellSetType); this->Container = std::shared_ptr(new CellSetType(cellSet)); } public: VTKM_CONT UnknownCellSet() = default; template VTKM_CONT UnknownCellSet(const CellSetType& cellSet) { this->InitializeKnownOrUnknownCellSet( cellSet, typename std::is_base_of::type{}); } /// \brief Returns whether a cell set is stored in this `UnknownCellSet`. /// /// If the `UnknownCellSet` is constructed without a `CellSet`, it will not /// have an underlying type, and therefore the operations will be invalid. /// VTKM_CONT bool IsValid() const { return static_cast(this->Container); } /// \brief Returns a pointer to the `CellSet` base class. /// VTKM_CONT vtkm::cont::CellSet* GetCellSetBase() { return this->Container.get(); } VTKM_CONT const vtkm::cont::CellSet* GetCellSetBase() const { return this->Container.get(); } /// \brief Create a new cell set of the same type as this cell set. /// /// This method creates a new cell set that is the same type as this one /// and returns a new `UnknownCellSet` for it. This method is convenient /// when creating output cell sets that should be the same type as the /// input cell set. /// VTKM_CONT UnknownCellSet NewInstance() const; /// \brief Returns the name of the cell set type stored in this class. /// /// Returns an empty string if no cell set is stored. /// VTKM_CONT std::string GetCellSetName() const; /// \brief Returns true if this cell set matches the `CellSetType` template argument. /// template VTKM_CONT bool IsType() const { return (dynamic_cast(this->Container.get()) != nullptr); } VTKM_CONT vtkm::Id GetNumberOfCells() const { return this->Container ? this->Container->GetNumberOfCells() : 0; } VTKM_CONT vtkm::Id GetNumberOfFaces() const { return this->Container ? this->Container->GetNumberOfFaces() : 0; } VTKM_CONT vtkm::Id GetNumberOfEdges() const { return this->Container ? this->Container->GetNumberOfEdges() : 0; } VTKM_CONT vtkm::Id GetNumberOfPoints() const { return this->Container ? this->Container->GetNumberOfPoints() : 0; } VTKM_CONT vtkm::UInt8 GetCellShape(vtkm::Id id) const { return this->GetCellSetBase()->GetCellShape(id); } VTKM_CONT vtkm::IdComponent GetNumberOfPointsInCell(vtkm::Id id) const { return this->GetCellSetBase()->GetNumberOfPointsInCell(id); } VTKM_CONT void GetCellPointIds(vtkm::Id id, vtkm::Id* ptids) const { return this->GetCellSetBase()->GetCellPointIds(id, ptids); } VTKM_CONT void DeepCopyFrom(const CellSet* src) { this->GetCellSetBase()->DeepCopy(src); } VTKM_CONT void PrintSummary(std::ostream& os) const; VTKM_CONT void ReleaseResourcesExecution() { if (this->Container) { this->Container->ReleaseResourcesExecution(); } } /// \brief Returns true if this cell set can be retrieved as the given type. /// /// This method will return true if calling `AsCellSet` of the given type will /// succeed. This result is similar to `IsType`, and if `IsType` returns true, /// then this will return true. However, this method will also return true for /// other types where automatic conversions are made. /// template VTKM_CONT bool CanConvert() const { // TODO: Currently, these are the same. But in the future we expect to support // special CellSet types that can convert back and forth such as multiplexed // cell sets or a cell set that can hold structured grids of any dimension. return this->IsType(); } ///@{ /// \brief Get the cell set as a known type. /// /// Returns this cell set cast appropriately and stored in the given `CellSet` /// type. Throws an `ErrorBadType` if the stored cell set cannot be stored in /// the given cell set type. Use the `CanConvert` method to determine if the /// cell set can be returned with the given type. /// template VTKM_CONT void AsCellSet(CellSetType& cellSet) const { VTKM_IS_CELL_SET(CellSetType); CellSetType* cellSetPointer = dynamic_cast(this->Container.get()); if (cellSetPointer == nullptr) { VTKM_LOG_CAST_FAIL(*this, CellSetType); throwFailedDynamicCast(this->GetCellSetName(), vtkm::cont::TypeToString(cellSet)); } VTKM_LOG_CAST_SUCC(*this, *cellSetPointer); cellSet = *cellSetPointer; } template VTKM_CONT CellSetType AsCellSet() const { CellSetType cellSet; this->AsCellSet(cellSet); return cellSet; } ///@} /// \brief Assigns potential cell set types. /// /// Calling this method will return an `UncertainCellSet` with the provided /// cell set list. The returned object will hold the same `CellSet`, but /// `CastAndCall`'s on the returned object will be constrained to the given /// types. /// // Defined in UncertainCellSet.h template VTKM_CONT vtkm::cont::UncertainCellSet ResetCellSetList(CellSetList) const; template VTKM_CONT vtkm::cont::UncertainCellSet ResetCellSetList() const; /// \brief Call a functor using the underlying cell set type. /// /// `CastAndCallForTypes` attemts to cast the held cell set to a specific type /// and then calls the given functor with the cast cell set. You must specify /// the `CellSetList` (in a `vtkm::List`) as a template argument. /// /// After the functor argument, you may add any number of arguments that will /// be passed to the functor after the converted cell set. /// template VTKM_CONT void CastAndCallForTypes(Functor&& functor, Args&&... args) const; }; //============================================================================= // Free function casting helpers // (Not sure if these should be deprecated.) /// Returns true if `unknownCellSet` matches the type of `CellSetType`. /// template VTKM_CONT inline bool IsType(const vtkm::cont::UnknownCellSet& unknownCellSet) { return unknownCellSet.IsType(); } /// Returns `unknownCellSet` cast to the given `CellSet` type. Throws /// `ErrorBadType` if the cast does not work. Use `IsType` /// to check if the cast can happen. /// template VTKM_CONT inline CellSetType Cast(const vtkm::cont::UnknownCellSet& unknownCellSet) { return unknownCellSet.AsCellSet(); } namespace internal { VTKM_CONT_EXPORT void ThrowCastAndCallException(const vtkm::cont::UnknownCellSet&, const std::type_info&); template <> struct DynamicTransformTraits { using DynamicTag = vtkm::cont::internal::DynamicTransformTagCastAndCall; }; } // namespace internal template VTKM_CONT void UnknownCellSet::CastAndCallForTypes(Functor&& functor, Args&&... args) const { VTKM_IS_LIST(CellSetList); bool called = false; vtkm::ListForEach( [&](auto cellSet) { if (!called && this->CanConvert()) { called = true; this->AsCellSet(cellSet); VTKM_LOG_CAST_SUCC(*this, cellSet); // If you get a compile error here, it means that you have called CastAndCall for a // vtkm::cont::UnknownCellSet and the arguments of the functor do not match those // being passed. This is often because it is calling the functor with a CellSet // type that was not expected. Either add overloads to the functor to accept all // possible cell set types or constrain the types tried for the CastAndCall. functor(cellSet, args...); } }, CellSetList{}); if (!called) { VTKM_LOG_CAST_FAIL(*this, CellSetList); internal::ThrowCastAndCallException(*this, typeid(CellSetList)); } } /// A specialization of `CastAndCall` for `UnknownCellSet`. /// Since we have no hints on the types, use `VTKM_DEFAULT_CELL_SET_LIST`. template void CastAndCall(const vtkm::cont::UnknownCellSet& cellSet, Functor&& f, Args&&... args) { cellSet.CastAndCallForTypes(std::forward(f), std::forward(args)...); } namespace internal { /// Checks to see if the given object is an unknown (or uncertain) cell set. It /// resolves to either `std::true_type` or `std::false_type`. /// template using UnknownCellSetCheck = typename std::is_base_of::type; #define VTKM_IS_UNKNOWN_CELL_SET(T) \ VTKM_STATIC_ASSERT(::vtkm::cont::internal::UnknownCellSetCheck::value) #define VTKM_IS_KNOWN_OR_UNKNOWN_CELL_SET(T) \ VTKM_STATIC_ASSERT(::vtkm::cont::internal::CellSetCheck::type::value || \ ::vtkm::cont::internal::UnknownCellSetCheck::value) } // namespace internal } // namespace vtkm::cont } // namespace vtkm //============================================================================= // Specializations of serialization related classes /// @cond SERIALIZATION namespace vtkm { namespace cont { template <> struct VTKM_CONT_EXPORT SerializableTypeString { static VTKM_CONT std::string Get(); }; } } // namespace vtkm::cont namespace mangled_diy_namespace { template <> struct VTKM_CONT_EXPORT Serialization { public: static VTKM_CONT void save(BinaryBuffer& bb, const vtkm::cont::UnknownCellSet& obj); static VTKM_CONT void load(BinaryBuffer& bb, vtkm::cont::UnknownCellSet& obj); }; } // namespace mangled_diy_namespace /// @endcond SERIALIZATION // Include the implementation of UncertainCellSet. This should be included because there // are methods in UnknownCellSet that produce objects of this type. It has to be included // at the end to resolve the circular dependency. #include #endif //vtk_m_cont_UnknownCellSet_h