//============================================================================ // 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. //============================================================================ #if !defined(VTK_M_DEVICE) || !defined(VTK_M_NAMESPACE) #error VariantImpl.h must be included from Variant.h // Some defines to make my IDE happy. #define VTK_M_DEVICE #define VTK_M_NAMESPACE tmp #endif #include #include #include #include #include namespace vtkm { namespace VTK_M_NAMESPACE { namespace internal { // Forward declaration template class Variant; namespace detail { struct VariantCopyFunctor { template VTK_M_DEVICE void operator()(const T& src, void* destPointer) const noexcept { new (destPointer) T(src); } }; struct VariantDestroyFunctor { template VTK_M_DEVICE 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< bool, (vtkmstd::is_trivially_copyable::value && vtkmstd::is_trivially_copyable::value && vtkmstd::is_trivially_copyable::value && vtkmstd::is_trivially_copyable::value)> { }; template struct AllTriviallyCopyable : std::integral_constant< bool, (vtkmstd::is_trivially_copyable::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 AllTriviallyConstructible; template <> struct AllTriviallyConstructible<> : std::true_type { }; template struct AllTriviallyConstructible : std::integral_constant::value)> { }; template struct AllTriviallyConstructible : std::integral_constant::value && vtkmstd::is_trivially_constructible::value)> { }; template struct AllTriviallyConstructible : std::integral_constant::value && vtkmstd::is_trivially_constructible::value && vtkmstd::is_trivially_constructible::value)> { }; template struct AllTriviallyConstructible : std::integral_constant::value && vtkmstd::is_trivially_constructible::value && vtkmstd::is_trivially_constructible::value && vtkmstd::is_trivially_constructible::value)> { }; template struct AllTriviallyConstructible : std::integral_constant::value && vtkmstd::is_trivially_constructible::value && vtkmstd::is_trivially_constructible::value && vtkmstd::is_trivially_constructible::value && vtkmstd::is_trivially_constructible::value && AllTriviallyConstructible::value)> { }; template struct VariantTriviallyConstructible; template struct VariantTriviallyConstructible> : AllTriviallyConstructible { }; template struct VariantStorageImpl { typename vtkmstd::aligned_union<0, Ts...>::type Storage; vtkm::IdComponent Index; template using TypeAt = typename vtkm::ListAt, Index>; VTK_M_DEVICE void* GetPointer() { return reinterpret_cast(&this->Storage); } VTK_M_DEVICE const void* GetPointer() const { return reinterpret_cast(&this->Storage); } VTK_M_DEVICE vtkm::IdComponent GetIndex() const noexcept { return this->Index; } VTK_M_DEVICE bool IsValid() const noexcept { return (this->Index >= 0) && (this->Index < static_cast(sizeof...(Ts))); } VTK_M_DEVICE void Reset() noexcept { if (this->IsValid()) { this->CastAndCall(detail::VariantDestroyFunctor{}); this->Index = -1; } } template VTK_M_DEVICE 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 VTK_M_DEVICE 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, typename TriviallyCopyable = typename VariantTriviallyCopyable::type> struct VariantConstructorImpl; // Can trivially construct, deconstruct, and copy all data. (Probably all trivial classes.) template struct VariantConstructorImpl, std::true_type, std::true_type> : VariantStorageImpl { VariantConstructorImpl() = default; ~VariantConstructorImpl() = default; VariantConstructorImpl(const VariantConstructorImpl&) = default; VariantConstructorImpl(VariantConstructorImpl&&) = default; VariantConstructorImpl& operator=(const VariantConstructorImpl&) = default; VariantConstructorImpl& operator=(VariantConstructorImpl&&) = default; }; // Can trivially copy, but cannot trivially construct. Common if a class is simple but // initializes itself. template struct VariantConstructorImpl, std::false_type, std::true_type> : VariantStorageImpl { VTK_M_DEVICE VariantConstructorImpl() { this->Index = -1; } // Any trivially copyable class is trivially destructable. ~VariantConstructorImpl() = default; VariantConstructorImpl(const VariantConstructorImpl&) = default; VariantConstructorImpl(VariantConstructorImpl&&) = default; VariantConstructorImpl& operator=(const VariantConstructorImpl&) = default; VariantConstructorImpl& operator=(VariantConstructorImpl&&) = default; }; // Cannot trivially copy. We assume we cannot trivially construct/destruct. template struct VariantConstructorImpl, construct_type, std::false_type> : VariantStorageImpl { VTK_M_DEVICE VariantConstructorImpl() { this->Index = -1; } VTK_M_DEVICE ~VariantConstructorImpl() { this->Reset(); } VTK_M_DEVICE VariantConstructorImpl(const VariantConstructorImpl& src) noexcept { src.CastAndCall(VariantCopyFunctor{}, this->GetPointer()); this->Index = src.Index; } VTK_M_DEVICE VariantConstructorImpl& operator=(const VariantConstructorImpl& src) noexcept { this->Reset(); src.CastAndCall(detail::VariantCopyFunctor{}, this->GetPointer()); this->Index = src.Index; 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), an invalid is returned. /// VTK_M_DEVICE 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. /// /// Note that if this `Variant` was not initialized with an object, the result of `IsValid` /// is undefined. The `Variant` could report itself as validly containing an object that /// is trivially constructed. /// VTK_M_DEVICE 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 VTK_M_DEVICE 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; template VTK_M_DEVICE 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; } template VTK_M_DEVICE 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 VTK_M_DEVICE 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 VTK_M_DEVICE 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 VTK_M_DEVICE 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 VTK_M_DEVICE 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: template VTK_M_DEVICE T& EmplaceImpl(Args&&... args) { this->Reset(); T* value = new (this->GetPointer()) T{ args... }; this->Index = I; return *value; } template VTK_M_DEVICE 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 VTK_M_DEVICE TypeAt& Get() noexcept { VTKM_ASSERT(I == this->GetIndex()); return *reinterpret_cast*>(this->GetPointer()); } template VTK_M_DEVICE 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 VTK_M_DEVICE T& Get() noexcept { VTKM_ASSERT(this->GetIndexOf() == this->GetIndex()); return *reinterpret_cast(this->GetPointer()); } template VTK_M_DEVICE 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 VTK_M_DEVICE 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 VTK_M_DEVICE 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. /// VTK_M_DEVICE 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::VTK_M_NAMESPACE::internal #undef VTK_M_DEVICE #undef VTK_M_NAMESPACE