//============================================================================ // 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_internal_Variant_h #define vtk_m_internal_Variant_h #include #include #include #include #include namespace vtkm { namespace internal { // Forward declaration template class Variant; namespace detail { struct VariantCopyFunctor { VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT void operator()(const T& src, void* destPointer) const noexcept { new (destPointer) T(src); } }; struct VariantDestroyFunctor { VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT void operator()(T& src) const noexcept { src.~T(); } }; template struct AllTriviallyCopyable; template <> struct AllTriviallyCopyable<> : std::true_type { }; template struct AllTriviallyCopyable : std::integral_constant::value)> { }; template struct AllTriviallyCopyable : std::integral_constant::value && vtkmstd::is_trivially_copyable::value)> { }; template struct AllTriviallyCopyable : std::integral_constant::value && vtkmstd::is_trivially_copyable::value && vtkmstd::is_trivially_copyable::value)> { }; template struct AllTriviallyCopyable : std::integral_constant::value && vtkmstd::is_trivially_copyable::value && vtkmstd::is_trivially_copyable::value && vtkmstd::is_trivially_copyable::value)> { }; template struct AllTriviallyCopyable : std::integral_constant::value && vtkmstd::is_trivially_copyable::value && vtkmstd::is_trivially_copyable::value && vtkmstd::is_trivially_copyable::value && vtkmstd::is_trivially_copyable::value && AllTriviallyCopyable::value)> { }; template struct VariantTriviallyCopyable; template struct VariantTriviallyCopyable> : AllTriviallyCopyable { }; template struct VariantStorageImpl { typename vtkmstd::aligned_union<0, Ts...>::type Storage; vtkm::IdComponent Index = -1; template using TypeAt = typename vtkm::ListAt, Index>; VTKM_EXEC_CONT void* GetPointer() { return reinterpret_cast(&this->Storage); } VTKM_EXEC_CONT const void* GetPointer() const { return reinterpret_cast(&this->Storage); } VTKM_EXEC_CONT vtkm::IdComponent GetIndex() const noexcept { return this->Index; } VTKM_EXEC_CONT bool IsValid() const noexcept { return this->GetIndex() >= 0; } VTKM_EXEC_CONT void Reset() noexcept { if (this->IsValid()) { this->CastAndCall(detail::VariantDestroyFunctor{}); this->Index = -1; } } template VTKM_EXEC_CONT auto CastAndCall(Functor&& f, Args&&... args) const noexcept(noexcept(f(std::declval&>(), args...))) -> decltype(f(std::declval&>(), args...)) { VTKM_ASSERT(this->IsValid()); return detail::VariantCastAndCallImpl&>(), args...))>( brigand::list{}, this->GetIndex(), std::forward(f), this->GetPointer(), std::forward(args)...); } template VTKM_EXEC_CONT auto CastAndCall(Functor&& f, Args&&... args) noexcept( noexcept(f(std::declval&>(), args...))) -> decltype(f(std::declval&>(), args...)) { VTKM_ASSERT(this->IsValid()); return detail::VariantCastAndCallImpl&>(), args...))>( brigand::list{}, this->GetIndex(), std::forward(f), this->GetPointer(), std::forward(args)...); } }; template ::type> struct VariantConstructorImpl; template struct VariantConstructorImpl, std::true_type> : VariantStorageImpl { VariantConstructorImpl() = default; ~VariantConstructorImpl() = default; VariantConstructorImpl(const VariantConstructorImpl&) = default; VariantConstructorImpl(VariantConstructorImpl&&) = default; VariantConstructorImpl& operator=(const VariantConstructorImpl&) = default; VariantConstructorImpl& operator=(VariantConstructorImpl&&) = default; }; template struct VariantConstructorImpl, std::false_type> : VariantStorageImpl { VariantConstructorImpl() = default; VTKM_EXEC_CONT ~VariantConstructorImpl() { this->Reset(); } VTKM_EXEC_CONT VariantConstructorImpl(const VariantConstructorImpl& src) noexcept { src.CastAndCall(VariantCopyFunctor{}, this->GetPointer()); this->Index = src.Index; } VTKM_EXEC_CONT VariantConstructorImpl(VariantConstructorImpl&& rhs) noexcept { this->Storage = std::move(rhs.Storage); this->Index = std::move(rhs.Index); rhs.Index = -1; } VTKM_EXEC_CONT VariantConstructorImpl& operator=(const VariantConstructorImpl& src) noexcept { this->Reset(); src.CastAndCall(detail::VariantCopyFunctor{}, this->GetPointer()); this->Index = src.Index; return *this; } VTKM_EXEC_CONT VariantConstructorImpl& operator=(VariantConstructorImpl&& rhs) noexcept { this->Reset(); this->Storage = std::move(rhs.Storage); this->Index = std::move(rhs.Index); rhs.Index = -1; return *this; } }; } // namespace detail template class Variant : detail::VariantConstructorImpl> { using Superclass = detail::VariantConstructorImpl>; public: /// Returns the index of the type of object this variant is storing. If no object is currently /// stored (i.e. the Variant is invalid), -1 is returned. /// VTKM_EXEC_CONT vtkm::IdComponent GetIndex() const noexcept { return this->Superclass::GetIndex(); } /// Returns true if this Variant is storing an object from one of the types in the template /// list, false otherwise. /// VTKM_EXEC_CONT bool IsValid() const noexcept { return this->Superclass::IsValid(); } /// Type that converts to a std::integral_constant containing the index of the given type (or /// -1 if that type is not in the list). template using IndexOf = vtkm::ListIndexOf, T>; /// Returns the index for the given type (or -1 if that type is not in the list). /// template VTKM_EXEC_CONT static constexpr vtkm::IdComponent GetIndexOf() { return IndexOf::value; } /// Type that converts to the type at the given index. /// template using TypeAt = typename Superclass::template TypeAt; /// The number of types representable by this Variant. /// static constexpr vtkm::IdComponent NumberOfTypes = vtkm::IdComponent{ sizeof...(Ts) }; Variant() = default; ~Variant() = default; Variant(const Variant&) = default; Variant(Variant&&) = default; Variant& operator=(const Variant&) = default; Variant& operator=(Variant&&) = default; VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT Variant(const T& src) noexcept { constexpr vtkm::IdComponent index = GetIndexOf(); // Might be a way to use an enable_if to enforce a proper type. VTKM_STATIC_ASSERT_MSG(index >= 0, "Attempting to put invalid type into a Variant"); new (this->GetPointer()) T(src); this->Index = index; } VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT Variant(const T&& src) noexcept { constexpr vtkm::IdComponent index = IndexOf::value; // Might be a way to use an enable_if to enforce a proper type. VTKM_STATIC_ASSERT_MSG(index >= 0, "Attempting to put invalid type into a Variant"); new (this->GetPointer()) T(std::move(src)); this->Index = index; } template VTKM_EXEC_CONT T& Emplace(Args&&... args) { constexpr vtkm::IdComponent I = GetIndexOf(); VTKM_STATIC_ASSERT_MSG(I >= 0, "Variant::Emplace called with invalid type."); return this->EmplaceImpl(std::forward(args)...); } template VTKM_EXEC_CONT T& Emplace(std::initializer_list il, Args&&... args) { constexpr vtkm::IdComponent I = GetIndexOf(); VTKM_STATIC_ASSERT_MSG(I >= 0, "Variant::Emplace called with invalid type."); return this->EmplaceImpl(il, std::forward(args)...); } template VTKM_EXEC_CONT TypeAt& Emplace(Args&&... args) { VTKM_STATIC_ASSERT_MSG((I >= 0) && (I < NumberOfTypes), "Variant::Emplace called with invalid index"); return this->EmplaceImpl, I>(std::forward(args)...); } template VTKM_EXEC_CONT TypeAt& Emplace(std::initializer_list il, Args&&... args) { VTKM_STATIC_ASSERT_MSG((I >= 0) && (I < NumberOfTypes), "Variant::Emplace called with invalid index"); return this->EmplaceImpl, I>(il, std::forward(args)...); } private: VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT T& EmplaceImpl(Args&&... args) { this->Reset(); T* value = new (this->GetPointer()) T{ args... }; this->Index = I; return *value; } VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT T& EmplaceImpl(std::initializer_list il, Args&&... args) { this->Reset(); T* value = new (this->GetPointer()) T(il, args...); this->Index = I; return *value; } public: //@{ /// Returns the value as the type at the given index. The behavior is undefined if the /// variant does not contain the value at the given index. /// template VTKM_EXEC_CONT TypeAt& Get() noexcept { VTKM_ASSERT(I == this->GetIndex()); return *reinterpret_cast*>(this->GetPointer()); } template VTKM_EXEC_CONT const TypeAt& Get() const noexcept { VTKM_ASSERT(I == this->GetIndex()); return *reinterpret_cast*>(this->GetPointer()); } //@} //@{ /// Returns the value as the given type. The behavior is undefined if the variant does not /// contain a value of the given type. /// template VTKM_EXEC_CONT T& Get() noexcept { VTKM_ASSERT(this->GetIndexOf() == this->GetIndex()); return *reinterpret_cast(this->GetPointer()); } template VTKM_EXEC_CONT const T& Get() const noexcept { VTKM_ASSERT(this->GetIndexOf() == this->GetIndex()); return *reinterpret_cast(this->GetPointer()); } //@} //@{ /// Given a functor object, calls the functor with the contained object cast to the appropriate /// type. If extra \c args are given, then those are also passed to the functor after the cast /// object. If the functor returns a value, that value is returned from \c CastAndCall. /// /// The results are undefined if the Variant is not valid. /// template VTKM_EXEC_CONT auto CastAndCall(Functor&& f, Args&&... args) const noexcept(noexcept(f(std::declval&>(), args...))) -> decltype(f(std::declval&>(), args...)) { return this->Superclass::CastAndCall(std::forward(f), std::forward(args)...); } template VTKM_EXEC_CONT auto CastAndCall(Functor&& f, Args&&... args) noexcept( noexcept(f(std::declval&>(), args...))) -> decltype(f(std::declval&>(), args...)) { return this->Superclass::CastAndCall(std::forward(f), std::forward(args)...); } /// Destroys any object the Variant is holding and sets the Variant to an invalid state. This /// method is not thread safe. /// VTKM_EXEC_CONT void Reset() noexcept { this->Superclass::Reset(); } }; /// \brief Convert a ListTag to a Variant. /// /// Depricated. Use ListAsVariant instead. /// template using ListTagAsVariant VTKM_DEPRECATED( 1.6, "vtkm::ListTag is no longer supported. Use vtkm::List instead.") = vtkm::ListApply; /// \brief Convert a `List` to a `Variant`. /// template using ListAsVariant = vtkm::ListApply; } } // namespace vtkm::internal #endif //vtk_m_internal_Variant_h