Merge topic 'variant-safe-punning2'

6921b5cc3 Minor style changes in UnitTestVariant
6ccb32d27 Slight comment changes
647bc94fe Reduce the number of lines required to implement Variant::CastAndCall
991eeba9f Reduce the number of lines required to implement VariantUnion
7607736ef Reduce the number of lines required to implement AllTrivially*
9816c422b Add padding to Grid struct in CellLocatorTwoLevel
e480fd7a2 Support copying a Variant to itself
d2d9ba332 Make connectivity structures trivially copyable
...

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !2444
This commit is contained in:
Kenneth Moreland 2021-04-01 22:04:18 +00:00 committed by Kitware Robot
commit 2bd4805fe0
7 changed files with 1296 additions and 2373 deletions

@ -36,6 +36,10 @@ using FloatVec3 = vtkm::Vec3f;
struct Grid
{
DimVec3 Dimensions;
// Bug in CUDA 9.2 where having this gap for alignment was for some reason setting garbage
// in a union with other cell locators (or perhaps not properly copying data). This appears
// to be fixed by CUDA 10.2.
DimensionType Padding;
FloatVec3 Origin;
FloatVec3 BinSize;
};
@ -54,6 +58,7 @@ VTKM_EXEC inline vtkm::Id ComputeFlatIndex(const DimVec3& idx, const DimVec3 dim
VTKM_EXEC inline Grid ComputeLeafGrid(const DimVec3& idx, const DimVec3& dim, const Grid& l1Grid)
{
return { dim,
0,
l1Grid.Origin + (static_cast<FloatVec3>(idx) * l1Grid.BinSize),
l1Grid.BinSize / static_cast<FloatVec3>(dim) };
}

@ -27,13 +27,7 @@ class ConnectivityPermutedVisitCellsWithPoints
public:
using SchedulingRangeType = typename OriginalConnectivity::SchedulingRangeType;
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC_CONT
ConnectivityPermutedVisitCellsWithPoints()
: Portal()
, Connectivity()
{
}
ConnectivityPermutedVisitCellsWithPoints() = default;
VTKM_EXEC_CONT
ConnectivityPermutedVisitCellsWithPoints(const PermutationPortal& portal,
@ -43,12 +37,8 @@ public:
{
}
VTKM_EXEC_CONT
ConnectivityPermutedVisitCellsWithPoints(const ConnectivityPermutedVisitCellsWithPoints& src)
: Portal(src.Portal)
, Connectivity(src.Connectivity)
{
}
ConnectivityPermutedVisitCellsWithPoints(const ConnectivityPermutedVisitCellsWithPoints& src) =
default;
ConnectivityPermutedVisitCellsWithPoints& operator=(
const ConnectivityPermutedVisitCellsWithPoints& src) = default;

@ -34,11 +34,7 @@ class ConnectivityStructured
public:
using SchedulingRangeType = typename InternalsType::SchedulingRangeType;
VTKM_EXEC_CONT
ConnectivityStructured()
: Internals()
{
}
ConnectivityStructured() = default;
VTKM_EXEC_CONT
ConnectivityStructured(const InternalsType& src)
@ -46,11 +42,7 @@ public:
{
}
VTKM_EXEC_CONT
ConnectivityStructured(const ConnectivityStructured& src)
: Internals(src.Internals)
{
}
ConnectivityStructured(const ConnectivityStructured& src) = default;
VTKM_EXEC_CONT
ConnectivityStructured(

@ -34,7 +34,7 @@ static vtkm::Id g_NonTrivialCount;
// A class that must is not trivial to copy nor construct.
struct NonTrivial
{
vtkm::Id Value = 12345;
vtkm::Id Value;
NonTrivial* Self;
void CheckState() const
@ -44,17 +44,19 @@ struct NonTrivial
}
NonTrivial()
: Self(this)
: Value(12345)
, Self(this)
{
this->CheckState();
++g_NonTrivialCount;
}
NonTrivial(const NonTrivial& src)
: Self(this)
: Value(src.Value)
{
this->CheckState();
src.CheckState();
this->Self = this;
this->CheckState();
++g_NonTrivialCount;
}
@ -234,65 +236,66 @@ void TestTriviallyCopyable()
{
#ifdef VTKM_USE_STD_IS_TRIVIAL
// Make sure base types are behaving as expected
VTKM_STATIC_ASSERT(std::is_trivially_constructible<float>::value);
VTKM_STATIC_ASSERT(std::is_trivially_copyable<float>::value);
VTKM_STATIC_ASSERT(std::is_trivial<float>::value);
VTKM_STATIC_ASSERT(std::is_trivially_constructible<int>::value);
VTKM_STATIC_ASSERT(std::is_trivially_copyable<int>::value);
VTKM_STATIC_ASSERT(std::is_trivial<int>::value);
VTKM_STATIC_ASSERT(!std::is_trivially_constructible<NonTrivial>::value);
VTKM_STATIC_ASSERT(!std::is_trivially_copyable<NonTrivial>::value);
VTKM_STATIC_ASSERT(!std::is_trivial<NonTrivial>::value);
VTKM_STATIC_ASSERT(!std::is_trivially_constructible<TrivialCopy>::value);
VTKM_STATIC_ASSERT(std::is_trivially_copyable<TrivialCopy>::value);
VTKM_STATIC_ASSERT(!std::is_trivial<TrivialCopy>::value);
VTKM_STATIC_ASSERT(vtkmstd::is_trivially_constructible<float>::value);
VTKM_STATIC_ASSERT(vtkmstd::is_trivially_copyable<float>::value);
VTKM_STATIC_ASSERT(vtkmstd::is_trivial<float>::value);
VTKM_STATIC_ASSERT(vtkmstd::is_trivially_constructible<int>::value);
VTKM_STATIC_ASSERT(vtkmstd::is_trivially_copyable<int>::value);
VTKM_STATIC_ASSERT(vtkmstd::is_trivial<int>::value);
VTKM_STATIC_ASSERT(!vtkmstd::is_trivially_constructible<NonTrivial>::value);
VTKM_STATIC_ASSERT(!vtkmstd::is_trivially_copyable<NonTrivial>::value);
VTKM_STATIC_ASSERT(!vtkmstd::is_trivial<NonTrivial>::value);
VTKM_STATIC_ASSERT(!vtkmstd::is_trivially_constructible<TrivialCopy>::value);
VTKM_STATIC_ASSERT(vtkmstd::is_trivially_copyable<TrivialCopy>::value);
VTKM_STATIC_ASSERT(!vtkmstd::is_trivial<TrivialCopy>::value);
// A variant of trivially constructable things should be trivially constructable
VTKM_STATIC_ASSERT((vtkm::exec::internal::detail::AllTriviallyConstructible<float, int>::value));
VTKM_STATIC_ASSERT((vtkmstd::is_trivially_constructible<
vtkm::exec::internal::detail::VariantUnion<float, int>>::value));
VTKM_STATIC_ASSERT(
(std::is_trivially_constructible<vtkm::exec::internal::Variant<float, int>>::value));
(vtkmstd::is_trivially_constructible<vtkm::exec::internal::Variant<float, int>>::value));
// A variant of trivially copyable things should be trivially copyable
VTKM_STATIC_ASSERT(
(vtkm::exec::internal::detail::AllTriviallyCopyable<float, int, TrivialCopy>::value));
VTKM_STATIC_ASSERT(
(std::is_trivially_copyable<vtkm::exec::internal::Variant<float, int, TrivialCopy>>::value));
VTKM_STATIC_ASSERT((vtkmstd::is_trivially_copyable<
vtkm::exec::internal::detail::VariantUnion<float, int, TrivialCopy>>::value));
VTKM_STATIC_ASSERT((
vtkmstd::is_trivially_copyable<vtkm::exec::internal::Variant<float, int, TrivialCopy>>::value));
// A variant of any non-trivially constructable things is not trivially copyable
VTKM_STATIC_ASSERT(
(!vtkm::exec::internal::detail::AllTriviallyConstructible<NonTrivial, float, int>::value));
VTKM_STATIC_ASSERT(
(!vtkm::exec::internal::detail::AllTriviallyConstructible<float, NonTrivial, int>::value));
VTKM_STATIC_ASSERT(
(!vtkm::exec::internal::detail::AllTriviallyConstructible<float, int, NonTrivial>::value));
VTKM_STATIC_ASSERT((!std::is_trivially_constructible<
VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_constructible<
vtkm::exec::internal::detail::VariantUnion<NonTrivial, float, int>>::value));
VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_constructible<
vtkm::exec::internal::detail::VariantUnion<float, NonTrivial, int>>::value));
VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_constructible<
vtkm::exec::internal::detail::VariantUnion<float, int, NonTrivial>>::value));
VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_constructible<
vtkm::exec::internal::Variant<NonTrivial, float, int>>::value));
VTKM_STATIC_ASSERT((!std::is_trivially_constructible<
VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_constructible<
vtkm::exec::internal::Variant<float, NonTrivial, int>>::value));
VTKM_STATIC_ASSERT((!std::is_trivially_constructible<
VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_constructible<
vtkm::exec::internal::Variant<float, int, NonTrivial>>::value));
// A variant of any non-trivially copyable things is not trivially copyable
VTKM_STATIC_ASSERT(
(!vtkm::exec::internal::detail::AllTriviallyCopyable<NonTrivial, float, int>::value));
VTKM_STATIC_ASSERT(
(!vtkm::exec::internal::detail::AllTriviallyCopyable<float, NonTrivial, int>::value));
VTKM_STATIC_ASSERT(
(!vtkm::exec::internal::detail::AllTriviallyCopyable<float, int, NonTrivial>::value));
VTKM_STATIC_ASSERT(
(!std::is_trivially_copyable<vtkm::exec::internal::Variant<NonTrivial, float, int>>::value));
VTKM_STATIC_ASSERT(
(!std::is_trivially_copyable<vtkm::exec::internal::Variant<float, NonTrivial, int>>::value));
VTKM_STATIC_ASSERT(
(!std::is_trivially_copyable<vtkm::exec::internal::Variant<float, int, NonTrivial>>::value));
VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_copyable<
vtkm::exec::internal::detail::VariantUnion<NonTrivial, float, int>>::value));
VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_copyable<
vtkm::exec::internal::detail::VariantUnion<float, NonTrivial, int>>::value));
VTKM_STATIC_ASSERT((!vtkmstd::is_trivially_copyable<
vtkm::exec::internal::detail::VariantUnion<float, int, NonTrivial>>::value));
VTKM_STATIC_ASSERT((
!vtkmstd::is_trivially_copyable<vtkm::exec::internal::Variant<NonTrivial, float, int>>::value));
VTKM_STATIC_ASSERT((
!vtkmstd::is_trivially_copyable<vtkm::exec::internal::Variant<float, NonTrivial, int>>::value));
VTKM_STATIC_ASSERT((
!vtkmstd::is_trivially_copyable<vtkm::exec::internal::Variant<float, int, NonTrivial>>::value));
// A variant of trivial things should be trivial
VTKM_STATIC_ASSERT((std::is_trivial<vtkm::exec::internal::Variant<float, int>>::value));
VTKM_STATIC_ASSERT((vtkmstd::is_trivial<vtkm::exec::internal::Variant<float, int>>::value));
VTKM_STATIC_ASSERT(
(!std::is_trivial<vtkm::exec::internal::Variant<float, int, TrivialCopy>>::value));
(!vtkmstd::is_trivial<vtkm::exec::internal::Variant<float, int, TrivialCopy>>::value));
VTKM_STATIC_ASSERT(
(!std::is_trivial<vtkm::exec::internal::Variant<float, int, NonTrivial>>::value));
#endif // !VTKM_USING_GLIBCXX_4
(!vtkmstd::is_trivial<vtkm::exec::internal::Variant<float, int, NonTrivial>>::value));
#endif // VTKM_USE_STD_IS_TRIVIAL
}
struct TestFunctor
@ -434,6 +437,7 @@ struct CountConstructDestruct
++(*this->Count);
}
~CountConstructDestruct() { --(*this->Count); }
CountConstructDestruct& operator=(const CountConstructDestruct&) = delete;
};
void TestCopyDestroy()
@ -443,11 +447,11 @@ void TestCopyDestroy()
using VariantType = vtkm::exec::internal::Variant<TypePlaceholder<0>,
TypePlaceholder<1>,
CountConstructDestruct,
TypePlaceholder<2>,
TypePlaceholder<3>>;
#ifndef VTKM_USING_GLIBCXX_4
VTKM_STATIC_ASSERT(!std::is_trivially_copyable<VariantType>::value);
#endif // !VTKM_USING_GLIBCXX_4
TypePlaceholder<3>,
TypePlaceholder<4>>;
#ifdef VTKM_USE_STD_IS_TRIVIAL
VTKM_STATIC_ASSERT(!vtkmstd::is_trivially_copyable<VariantType>::value);
#endif // VTKM_USE_STD_IS_TRIVIAL
vtkm::Id count = 0;
VariantType variant1 = CountConstructDestruct(&count);
@ -545,6 +549,19 @@ void TestConstructDestruct()
VTKM_TEST_ASSERT(g_NonTrivialCount == 0);
}
void TestCopySelf()
{
std::cout << "Make sure copying a Variant to itself works" << std::endl;
using VariantType =
vtkm::exec::internal::Variant<TypePlaceholder<0>, NonTrivial, TypePlaceholder<2>>;
VariantType variant{ NonTrivial{} };
VariantType& variantRef = variant;
variant = variantRef;
variant = variant.Get<NonTrivial>();
}
void RunTest()
{
TestSize();
@ -555,6 +572,7 @@ void RunTest()
TestCopyDestroy();
TestEmplace();
TestConstructDestruct();
TestCopySelf();
}
} // namespace test_variant

@ -20,8 +20,7 @@
#include <vtkm/Deprecated.h>
#include <vtkm/List.h>
#include <vtkmstd/aligned_union.h>
#include <vtkmstd/is_trivial.h>
#include <vtkm/internal/Assume.h>
namespace vtkm
{
@ -37,12 +36,67 @@ class Variant;
namespace detail
{
// --------------------------------------------------------------------------------
// Helper classes for Variant
template <typename UnionType>
struct VariantUnionToListImpl;
template <typename... Ts>
struct VariantUnionToListImpl<detail::VariantUnionTD<Ts...>>
{
using type = vtkm::List<Ts...>;
};
template <typename... Ts>
struct VariantUnionToListImpl<detail::VariantUnionNTD<Ts...>>
{
using type = vtkm::List<Ts...>;
};
template <typename UnionType>
using VariantUnionToList =
typename VariantUnionToListImpl<typename std::decay<UnionType>::type>::type;
struct VariantCopyConstructFunctor
{
template <typename T, typename UnionType>
VTK_M_DEVICE void operator()(const T& src, UnionType& destUnion) const noexcept
{
constexpr vtkm::IdComponent Index = vtkm::ListIndexOf<VariantUnionToList<UnionType>, T>::value;
// If we are using this functor, we can assume the union does not hold a valid type.
new (&VariantUnionGet<Index>(destUnion)) T(src);
}
};
struct VariantCopyFunctor
{
template <typename T>
VTK_M_DEVICE void operator()(const T& src, void* destPointer) const noexcept
template <typename T, typename UnionType>
VTK_M_DEVICE void operator()(const T& src, UnionType& destUnion) const noexcept
{
new (destPointer) T(src);
constexpr vtkm::IdComponent Index = vtkm::ListIndexOf<VariantUnionToList<UnionType>, T>::value;
// If we are using this functor, we can assume the union holds type T.
this->DoCopy(
src, VariantUnionGet<Index>(destUnion), typename std::is_copy_assignable<T>::type{});
}
template <typename T>
VTK_M_DEVICE void DoCopy(const T& src, T& dest, std::true_type) const noexcept
{
dest = src;
}
template <typename T>
VTK_M_DEVICE void DoCopy(const T& src, T& dest, std::false_type) const noexcept
{
if (&src != &dest)
{
// Do not have an assignment operator, so destroy the old object and create a new one.
dest.~T();
new (&dest) T(src);
}
else
{
// Objects are already the same.
}
}
};
@ -68,56 +122,6 @@ struct VariantCheckType
VTKM_STATIC_ASSERT_MSG(!std::is_pointer<T>::value, "Pointers are not allowed in VTK-m Variant.");
};
template <typename... Ts>
struct AllTriviallyCopyable;
template <>
struct AllTriviallyCopyable<> : std::true_type
{
};
template <typename T0>
struct AllTriviallyCopyable<T0>
: std::integral_constant<bool, (vtkmstd::is_trivially_copyable<T0>::value)>
{
};
template <typename T0, typename T1>
struct AllTriviallyCopyable<T0, T1>
: std::integral_constant<bool,
(vtkmstd::is_trivially_copyable<T0>::value &&
vtkmstd::is_trivially_copyable<T1>::value)>
{
};
template <typename T0, typename T1, typename T2>
struct AllTriviallyCopyable<T0, T1, T2>
: std::integral_constant<bool,
(vtkmstd::is_trivially_copyable<T0>::value &&
vtkmstd::is_trivially_copyable<T1>::value &&
vtkmstd::is_trivially_copyable<T2>::value)>
{
};
template <typename T0, typename T1, typename T2, typename T3>
struct AllTriviallyCopyable<T0, T1, T2, T3>
: std::integral_constant<
bool,
(vtkmstd::is_trivially_copyable<T0>::value && vtkmstd::is_trivially_copyable<T1>::value &&
vtkmstd::is_trivially_copyable<T2>::value && vtkmstd::is_trivially_copyable<T3>::value)>
{
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename... Ts>
struct AllTriviallyCopyable<T0, T1, T2, T3, T4, Ts...>
: std::integral_constant<
bool,
(vtkmstd::is_trivially_copyable<T0>::value && vtkmstd::is_trivially_copyable<T1>::value &&
vtkmstd::is_trivially_copyable<T2>::value && vtkmstd::is_trivially_copyable<T3>::value &&
vtkmstd::is_trivially_copyable<T4>::value && AllTriviallyCopyable<Ts...>::value)>
{
};
template <typename VariantType>
struct VariantTriviallyCopyable;
@ -127,59 +131,6 @@ struct VariantTriviallyCopyable<vtkm::VTK_M_NAMESPACE::internal::Variant<Ts...>>
{
};
template <typename... Ts>
struct AllTriviallyConstructible;
template <>
struct AllTriviallyConstructible<> : std::true_type
{
};
template <typename T0>
struct AllTriviallyConstructible<T0>
: std::integral_constant<bool, (vtkmstd::is_trivially_constructible<T0>::value)>
{
};
template <typename T0, typename T1>
struct AllTriviallyConstructible<T0, T1>
: std::integral_constant<bool,
(vtkmstd::is_trivially_constructible<T0>::value &&
vtkmstd::is_trivially_constructible<T1>::value)>
{
};
template <typename T0, typename T1, typename T2>
struct AllTriviallyConstructible<T0, T1, T2>
: std::integral_constant<bool,
(vtkmstd::is_trivially_constructible<T0>::value &&
vtkmstd::is_trivially_constructible<T1>::value &&
vtkmstd::is_trivially_constructible<T2>::value)>
{
};
template <typename T0, typename T1, typename T2, typename T3>
struct AllTriviallyConstructible<T0, T1, T2, T3>
: std::integral_constant<bool,
(vtkmstd::is_trivially_constructible<T0>::value &&
vtkmstd::is_trivially_constructible<T1>::value &&
vtkmstd::is_trivially_constructible<T2>::value &&
vtkmstd::is_trivially_constructible<T3>::value)>
{
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename... Ts>
struct AllTriviallyConstructible<T0, T1, T2, T3, T4, Ts...>
: std::integral_constant<bool,
(vtkmstd::is_trivially_constructible<T0>::value &&
vtkmstd::is_trivially_constructible<T1>::value &&
vtkmstd::is_trivially_constructible<T2>::value &&
vtkmstd::is_trivially_constructible<T3>::value &&
vtkmstd::is_trivially_constructible<T4>::value &&
AllTriviallyConstructible<Ts...>::value)>
{
};
template <typename VariantType>
struct VariantTriviallyConstructible;
@ -189,22 +140,24 @@ struct VariantTriviallyConstructible<vtkm::VTK_M_NAMESPACE::internal::Variant<Ts
{
};
// --------------------------------------------------------------------------------
// Variant superclass that defines its storage
template <typename... Ts>
struct VariantStorageImpl
{
typename vtkmstd::aligned_union<0, Ts...>::type Storage;
VariantUnion<Ts...> Storage;
vtkm::IdComponent Index;
VariantStorageImpl() = default;
VTK_M_DEVICE VariantStorageImpl(vtkm::internal::NullType dummy)
: Storage({ dummy })
{
}
template <vtkm::IdComponent Index>
using TypeAt = typename vtkm::ListAt<vtkm::List<Ts...>, Index>;
VTK_M_DEVICE void* GetPointer() { return reinterpret_cast<void*>(&this->Storage); }
VTK_M_DEVICE const void* GetPointer() const
{
return reinterpret_cast<const void*>(&this->Storage);
}
VTK_M_DEVICE vtkm::IdComponent GetIndex() const noexcept { return this->Index; }
VTK_M_DEVICE bool IsValid() const noexcept
{
@ -226,12 +179,8 @@ struct VariantStorageImpl
-> decltype(f(std::declval<const TypeAt<0>&>(), args...))
{
VTKM_ASSERT(this->IsValid());
return detail::VariantCastAndCallImpl<decltype(f(std::declval<const TypeAt<0>&>(), args...))>(
brigand::list<Ts...>{},
this->GetIndex(),
std::forward<Functor>(f),
this->GetPointer(),
std::forward<Args>(args)...);
return detail::VariantCastAndCallImpl(
this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
}
template <typename Functor, typename... Args>
@ -240,15 +189,14 @@ struct VariantStorageImpl
-> decltype(f(std::declval<TypeAt<0>&>(), args...))
{
VTKM_ASSERT(this->IsValid());
return detail::VariantCastAndCallImpl<decltype(f(std::declval<TypeAt<0>&>(), args...))>(
brigand::list<Ts...>{},
this->GetIndex(),
std::forward<Functor>(f),
this->GetPointer(),
std::forward<Args>(args)...);
return detail::VariantCastAndCallImpl(
this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
}
};
// --------------------------------------------------------------------------------
// Variant superclass that helps preserve trivially copyable and trivially constructable
// properties where possible.
template <typename VariantType,
typename TriviallyConstructible =
typename VariantTriviallyConstructible<VariantType>::type,
@ -277,7 +225,11 @@ struct VariantConstructorImpl<vtkm::VTK_M_NAMESPACE::internal::Variant<Ts...>,
std::false_type,
std::true_type> : VariantStorageImpl<Ts...>
{
VTK_M_DEVICE VariantConstructorImpl() { this->Index = -1; }
VTK_M_DEVICE VariantConstructorImpl()
: VariantStorageImpl<Ts...>(vtkm::internal::NullType{})
{
this->Index = -1;
}
// Any trivially copyable class is trivially destructable.
~VariantConstructorImpl() = default;
@ -294,20 +246,32 @@ struct VariantConstructorImpl<vtkm::VTK_M_NAMESPACE::internal::Variant<Ts...>,
construct_type,
std::false_type> : VariantStorageImpl<Ts...>
{
VTK_M_DEVICE VariantConstructorImpl() { this->Index = -1; }
VTK_M_DEVICE VariantConstructorImpl()
: VariantStorageImpl<Ts...>(vtkm::internal::NullType{})
{
this->Index = -1;
}
VTK_M_DEVICE ~VariantConstructorImpl() { this->Reset(); }
VTK_M_DEVICE VariantConstructorImpl(const VariantConstructorImpl& src) noexcept
: VariantStorageImpl<Ts...>(vtkm::internal::NullType{})
{
src.CastAndCall(VariantCopyFunctor{}, this->GetPointer());
src.CastAndCall(VariantCopyConstructFunctor{}, this->Storage);
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;
if (this->GetIndex() == src.GetIndex())
{
src.CastAndCall(detail::VariantCopyFunctor{}, this->Storage);
}
else
{
this->Reset();
src.CastAndCall(detail::VariantCopyConstructFunctor{}, this->Storage);
this->Index = src.Index;
}
return *this;
}
};
@ -323,20 +287,6 @@ class Variant : detail::VariantConstructorImpl<Variant<Ts...>>
using CheckTypes = vtkm::List<detail::VariantCheckType<Ts>...>;
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 <typename T>
@ -353,12 +303,29 @@ public:
/// Type that converts to the type at the given index.
///
template <vtkm::IdComponent Index>
using TypeAt = typename Superclass::template TypeAt<Index>;
using TypeAt = typename vtkm::ListAt<vtkm::List<Ts...>, Index>;
/// The number of types representable by this Variant.
///
static constexpr vtkm::IdComponent NumberOfTypes = vtkm::IdComponent{ sizeof...(Ts) };
/// 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->Index; }
/// 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->Index >= 0) && (this->Index < NumberOfTypes);
}
Variant() = default;
~Variant() = default;
Variant(const Variant&) = default;
@ -373,19 +340,22 @@ public:
// 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;
new (&this->Get<index>()) T(src);
}
template <typename T>
VTK_M_DEVICE Variant(const T&& src) noexcept
VTK_M_DEVICE Variant& operator=(const T& src)
{
constexpr vtkm::IdComponent index = IndexOf<T>::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;
if (this->GetIndex() == this->GetIndexOf<T>())
{
this->Get<T>() = src;
}
else
{
this->Emplace<T>(src);
}
return *this;
}
template <typename T, typename... Args>
@ -425,18 +395,16 @@ private:
VTK_M_DEVICE T& EmplaceImpl(Args&&... args)
{
this->Reset();
T* value = new (this->GetPointer()) T{ args... };
this->Index = I;
return *value;
return *(new (&this->Get<I>()) T{ args... });
}
template <typename T, vtkm::IdComponent I, typename U, typename... Args>
VTK_M_DEVICE T& EmplaceImpl(std::initializer_list<U> il, Args&&... args)
{
this->Reset();
T* value = new (this->GetPointer()) T(il, args...);
this->Index = I;
return *value;
return *(new (&this->Get<I>()) T(il, args...));
}
public:
@ -448,14 +416,14 @@ public:
VTK_M_DEVICE TypeAt<I>& Get() noexcept
{
VTKM_ASSERT(I == this->GetIndex());
return *reinterpret_cast<TypeAt<I>*>(this->GetPointer());
return detail::VariantUnionGet<I>(this->Storage);
}
template <vtkm::IdComponent I>
VTK_M_DEVICE const TypeAt<I>& Get() const noexcept
{
VTKM_ASSERT(I == this->GetIndex());
return *reinterpret_cast<const TypeAt<I>*>(this->GetPointer());
return detail::VariantUnionGet<I>(this->Storage);
}
//@}
@ -467,14 +435,14 @@ public:
VTK_M_DEVICE T& Get() noexcept
{
VTKM_ASSERT(this->GetIndexOf<T>() == this->GetIndex());
return *reinterpret_cast<T*>(this->GetPointer());
return detail::VariantUnionGet<IndexOf<T>::value>(this->Storage);
}
template <typename T>
VTK_M_DEVICE const T& Get() const noexcept
{
VTKM_ASSERT(this->GetIndexOf<T>() == this->GetIndex());
return *reinterpret_cast<const T*>(this->GetPointer());
return detail::VariantUnionGet<IndexOf<T>::value>(this->Storage);
}
//@}
@ -490,7 +458,9 @@ public:
noexcept(noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
-> decltype(f(std::declval<const TypeAt<0>&>(), args...))
{
return this->Superclass::CastAndCall(std::forward<Functor>(f), std::forward<Args>(args)...);
VTKM_ASSERT(this->IsValid());
return detail::VariantCastAndCallImpl(
this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
}
template <typename Functor, typename... Args>
@ -498,13 +468,22 @@ public:
noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
-> decltype(f(std::declval<TypeAt<0>&>(), args...))
{
return this->Superclass::CastAndCall(std::forward<Functor>(f), std::forward<Args>(args)...);
VTKM_ASSERT(this->IsValid());
return detail::VariantCastAndCallImpl(
this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(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(); }
VTK_M_DEVICE void Reset() noexcept
{
if (this->IsValid())
{
this->CastAndCall(detail::VariantDestroyFunctor{});
this->Index = -1;
}
}
};
/// \brief Convert a ListTag to a Variant.

File diff suppressed because it is too large Load Diff

@ -31,7 +31,9 @@ $# Ignore the following comment. It is meant for the generated file.
#include <vtkm/Types.h>
#include <vtkm/internal/brigand.hpp>
#include <vtkm/internal/Assume.h>
#include <vtkmstd/is_trivial.h>
#include <type_traits>
@ -46,9 +48,17 @@ def type_list(num_params):
for param in range(1, num_params + 1):
result += ', T%d' % param
return result
def typename_list(num_params):
if num_params < 0:
return ''
result = 'typename T0'
for param in range(1, num_params + 1):
result += ', typename T%d' % param
return result
)\
$#
$extend(type_list)\
$extend(type_list, typename_list)\
namespace vtkm
{
@ -70,135 +80,315 @@ struct VariantDummyReturn<void>
VTK_M_DEVICE static inline void F() noexcept {}
};
template <typename ReturnType, typename Functor, typename... Args>
VTK_M_DEVICE inline ReturnType VariantCastAndCallImpl(brigand::list<>,
vtkm::IdComponent,
Functor&&,
const void*,
Args&&...) noexcept
// clang-format off
// --------------------------------------------------------------------------------
// Helper classes to determine if all Variant types are trivial.
template <typename T0 = vtkm::internal::NullType,
$for(param_index in range(1, max_expanded))\
typename T$(param_index) = vtkm::internal::NullType,
$endfor\
typename... Ts>
struct AllTriviallyCopyable;
template <$typename_list(max_expanded - 1)>
struct AllTriviallyCopyable<$type_list(max_expanded - 1)>
: std::integral_constant<bool, (vtkmstd::is_trivially_copyable<T0>::value &&
$for(param_index in range(1, max_expanded - 1))\
vtkmstd::is_trivially_copyable<T$(param_index)>::value &&
$endfor\
vtkmstd::is_trivially_copyable<T$(max_expanded - 1)>::value)> { };
template <$typename_list(max_expanded), typename... Ts>
struct AllTriviallyCopyable<$type_list(max_expanded), Ts...>
: std::integral_constant<bool, (
AllTriviallyCopyable<$type_list(max_expanded - 1)>::value &&
AllTriviallyCopyable<T$(max_expanded), Ts...>::value)> { };
template <typename T0 = vtkm::internal::NullType,
$for(param_index in range(1, max_expanded))\
typename T$(param_index) = vtkm::internal::NullType,
$endfor\
typename... Ts>
struct AllTriviallyConstructible;
template <$typename_list(max_expanded - 1)>
struct AllTriviallyConstructible<$type_list(max_expanded - 1)>
: std::integral_constant<bool, (vtkmstd::is_trivially_constructible<T0>::value &&
$for(param_index in range(1, max_expanded - 1))\
vtkmstd::is_trivially_constructible<T$(param_index)>::value &&
$endfor\
vtkmstd::is_trivially_constructible<T$(max_expanded - 1)>::value)> { };
template <$typename_list(max_expanded), typename... Ts>
struct AllTriviallyConstructible<$type_list(max_expanded), Ts...>
: std::integral_constant<bool, (
AllTriviallyConstructible<$type_list(max_expanded - 1)>::value &&
AllTriviallyConstructible<T$(max_expanded), Ts...>::value)> { };
template <typename T0 = vtkm::internal::NullType,
$for(param_index in range(1, max_expanded))\
typename T$(param_index) = vtkm::internal::NullType,
$endfor\
typename... Ts>
struct AllTriviallyDestructible;
template <$typename_list(max_expanded - 1)>
struct AllTriviallyDestructible<$type_list(max_expanded - 1)>
: std::integral_constant<bool, (vtkmstd::is_trivially_destructible<T0>::value &&
$for(param_index in range(1, max_expanded - 1))\
vtkmstd::is_trivially_destructible<T$(param_index)>::value &&
$endfor\
vtkmstd::is_trivially_destructible<T$(max_expanded - 1)>::value)> { };
template <$typename_list(max_expanded), typename... Ts>
struct AllTriviallyDestructible<$type_list(max_expanded), Ts...>
: std::integral_constant<bool, (
AllTriviallyDestructible<$type_list(max_expanded - 1)>::value &&
AllTriviallyDestructible<T$(max_expanded), Ts...>::value)> { };
// --------------------------------------------------------------------------------
// Union type used inside of Variant
//
// You may be asking yourself, why not just use an std::aligned_union rather than a real union
// type? That was our first implementation, but the problem is that the std::aligned_union
// reference needs to be recast to the actual type. Typically you would do that with
// reinterpret_cast. However, doing that leads to undefined behavior. The C++ compiler assumes that
// 2 pointers of different types point to different memory (even if it is clear that they are set
// to the same address). That means optimizers can remove code because it "knows" that data in one
// type cannot affect data in another type. (See Shafik Yaghmour's excellent writeup at
// https://gist.github.com/shafik/848ae25ee209f698763cffee272a58f8 for more details.) To safely
// change the type of an std::aligned_union, you really have to do an std::memcpy. This is
// problematic for types that cannot be trivially copied. Another problem is that we found that
// device compilers do not optimize the memcpy as well as most CPU compilers. Likely, memcpy is
// used much less frequently on GPU devices.
//
// Part of the trickiness of the union implementation is trying to preserve when the type is
// trivially constructible and copyable. The trick is that if members of the union are not trivial,
// then the default constructors are deleted. To get around that, a non-default constructor is
// added, which we can use to construct the union for non-trivial types. Working with types with
// non-trivial destructors are particularly tricky. Again, if any member of the union has a
// non-trivial destructor, the destructor is deleted. Unlike a constructor, you cannot just say to
// use a different destructor. Thus, we have to define our own destructor for the union.
// Technically, the destructor here does not do anything, but the actual destruction should be
// handled by the Variant class that contains this VariantUnion. We actually need two separate
// implementations of our union, one that defines a destructor and one that use the default
// destructor. If you define your own destructor, you can lose the trivial constructor and trivial
// copy properties.
//
// TD = trivially deconstructible
template <typename T0 = vtkm::internal::NullType,
$for(param_index in range(1, max_expanded))\
typename T$(param_index) = vtkm::internal::NullType,
$endfor\
typename... Ts>
union VariantUnionTD;
// NTD = non-trivially deconstructible
template <typename T0 = vtkm::internal::NullType,
$for(param_index in range(1, max_expanded))\
typename T$(param_index) = vtkm::internal::NullType,
$endfor\
typename... Ts>
union VariantUnionNTD;
template <$typename_list(max_expanded - 1)>
union VariantUnionTD<$type_list(max_expanded - 1)>
{
// If we are here, it means we failed to find the appropriate type in a variant
$for(param_index in range(max_expanded))\
T$(param_index) V$(param_index);
$endfor\
vtkm::internal::NullType Remaining;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <$typename_list(max_expanded - 1)>
union VariantUnionNTD<$type_list(max_expanded - 1)>
{
$for(param_index in range(max_expanded))\
T$(param_index) V$(param_index);
$endfor\
vtkm::internal::NullType Remaining;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
template <$typename_list(max_expanded), typename... Ts>
union VariantUnionTD<$type_list(max_expanded), Ts...>
{
$for(param_index in range(max_expanded))\
T$(param_index) V$(param_index);
$endfor\
VariantUnionTD<T$(max_expanded), Ts...> Remaining;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <$typename_list(max_expanded), typename... Ts>
union VariantUnionNTD<$type_list(max_expanded), Ts...>
{
$for(param_index in range(max_expanded))\
T$(param_index) V$(param_index);
$endfor\
VariantUnionNTD<T$(max_expanded), Ts...> Remaining;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
template <bool TrivialConstructor, typename... Ts>
struct VariantUnionFinder;
template <typename... Ts>
struct VariantUnionFinder<true, Ts...>
{
using type = VariantUnionTD<Ts...>;
};
template <typename... Ts>
struct VariantUnionFinder<false, Ts...>
{
using type = VariantUnionNTD<Ts...>;
};
template <typename... Ts>
using VariantUnion =
typename VariantUnionFinder<AllTriviallyDestructible<Ts...>::value, Ts...>::type;
// --------------------------------------------------------------------------------
// Methods to get values out of the variant union
template <vtkm::IdComponent I, typename UnionType>
struct VariantUnionGetImpl;
$for(param_index in range(max_expanded))\
template <typename UnionType>
struct VariantUnionGetImpl<$(param_index), UnionType>
{
using ReturnType = decltype(std::declval<UnionType>().V$(param_index));
VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
{
return storage.V$(param_index);
}
VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
{
return storage.V$(param_index);
}
};
$endfor\
template <vtkm::IdComponent I, typename UnionType>
struct VariantUnionGetImpl
{
VTKM_STATIC_ASSERT(I >= $(max_expanded));
using RecursiveGet = VariantUnionGetImpl<I - $(max_expanded), decltype(std::declval<UnionType&>().Remaining)>;
using ReturnType = typename RecursiveGet::ReturnType;
VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
{
return RecursiveGet::Get(storage.Remaining);
}
VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
{
return RecursiveGet::Get(storage.Remaining);
}
};
template <vtkm::IdComponent I, typename UnionType>
VTK_M_DEVICE auto VariantUnionGet(UnionType& storage) noexcept
-> decltype(VariantUnionGetImpl<I, typename std::decay<UnionType>::type>::Get(storage))&
{
return VariantUnionGetImpl<I, typename std::decay<UnionType>::type>::Get(storage);
}
//clang-format on
// --------------------------------------------------------------------------------
// Internal implementation of CastAndCall for Variant
template <typename ReturnType,
typename Functor,
typename CastMember,
typename... Args>
VTK_M_DEVICE inline ReturnType VariantCallFunctor(
std::false_type,
Functor&& f,
CastMember& value,
Args&&... args) noexcept(noexcept(f(value, args...)))
{
// If you get a compile error here, it probably means that you have called Variant::CastAndCall
// with a functor that does not accept one of the types in the Variant. The functor you provide
// must be callable with all types in the Variant, not just the one that it currently holds.
return f(value, std::forward<Args>(args)...);
}
template <typename ReturnType,
typename Functor,
typename... Args>
VTK_M_DEVICE inline ReturnType VariantCallFunctor(
std::true_type, Functor&&, vtkm::internal::NullType, Args&&...) noexcept
{
// If we are here, it means a Variant had an inappropriate index.
VTKM_ASSERT(false && "Internal error, bad Variant state.");
return VariantDummyReturn<ReturnType>::F();
}
// clang-format off
// Terminating condition in recursive template
template <typename ReturnType, typename Functor, typename... Args>
VTK_M_DEVICE inline ReturnType VariantCastAndCallImplR(
std::true_type,
vtkm::IdComponent vtkmNotUsed(index),
Functor&& vtkmNotUsed(f),
vtkm::internal::NullType,
Args&&... vtkmNotUsed(args)) noexcept
{
// If we are here, it means a Variant had an inappropriate index.
VTKM_ASSERT(false && "Internal error, bad Variant state.");
return VariantDummyReturn<ReturnType>::F();
}
$for(num_params in range(0, max_expanded))\
template <typename ReturnType,
$for(param_index in range(0, num_params + 1))\
typename T$(param_index),
$endfor\
typename Functor,
typename... Args>
VTK_M_DEVICE inline ReturnType VariantCastAndCallImpl(
brigand::list<$type_list(num_params)>,
template <typename ReturnType, typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE inline ReturnType VariantCastAndCallImplR(
std::false_type,
vtkm::IdComponent index,
Functor&& f,
const void* storage,
Args&&... args) noexcept(noexcept(f(std::declval<const T0&>(), args...)))
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
{
switch (index)
{
$for(param_index in range(0, num_params + 1))\
$for(param_index in range(max_expanded))\
case $(param_index):
return f(*reinterpret_cast<const T$(param_index)*>(storage), std::forward<Args>(args)...);
return VariantCallFunctor<ReturnType>(
typename std::is_same<decltype(storage.V$(param_index)), vtkm::internal::NullType>::type{},
std::forward<Functor>(f),
storage.V$(param_index),
std::forward<Args>(args)...);
$endfor\
default:
// If we are here, it means we failed to find the appropriate type in a variant
VTKM_ASSERT(false && "Internal error, bad Variant state.");
return VariantDummyReturn<ReturnType>::F();
return VariantCastAndCallImplR<ReturnType>(
typename std::is_same<decltype(storage.Remaining), vtkm::internal::NullType>::type{},
index - $(max_expanded),
std::forward<Functor>(f),
storage.Remaining,
std::forward<Args>(args)...);
}
}
template <typename ReturnType,
$for(param_index in range(0, num_params + 1))\
typename T$(param_index),
$endfor\
typename Functor,
typename... Args>
VTK_M_DEVICE inline ReturnType VariantCastAndCallImpl(
brigand::list<$type_list(num_params)>,
template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE inline auto VariantCastAndCallImpl(
vtkm::IdComponent index,
Functor&& f,
void* storage,
Args&&... args) noexcept(noexcept(f(std::declval<const T0&>(), args...)))
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
switch (index)
{
$for(param_index in range(0, num_params + 1))\
case $(param_index):
return f(*reinterpret_cast<T$(param_index)*>(storage), std::forward<Args>(args)...);
$endfor\
default:
// If we are here, it means we failed to find the appropriate type in a variant
VTKM_ASSERT(false && "Internal error, bad Variant state.");
return VariantDummyReturn<ReturnType>::F();
}
}
$endfor\
//clang-format on
// Recurse for cases where Variant has more than $(max_expanded) types
template <typename ReturnType,
$for(param_index in range(0, max_expanded + 1))\
typename T$(param_index),
$endfor\
typename... RemainingT,
typename Functor,
typename... Args>
VTK_M_DEVICE inline ReturnType VariantCastAndCallImpl(
brigand::list<$type_list(max_expanded), RemainingT...>,
vtkm::IdComponent index,
Functor&& f,
const void* storage,
Args&&... args) noexcept(noexcept(f(std::declval<const T0&>(), args...)))
{
if (index < $(max_expanded))
{
return VariantCastAndCallImpl<ReturnType>(
brigand::list<$type_list(max_expanded - 1)>{},
index,
f,
storage,
args...);
}
else
{
return VariantCastAndCallImpl<ReturnType>(
brigand::list<T$(max_expanded), RemainingT...>{}, index - $(max_expanded), f, storage, args...);
}
}
template <typename ReturnType,
$for(param_index in range(0, max_expanded + 1))\
typename T$(param_index),
$endfor\
typename... RemainingT,
typename Functor,
typename... Args>
VTK_M_DEVICE inline ReturnType VariantCastAndCallImpl(
brigand::list<$type_list(max_expanded), RemainingT...>,
vtkm::IdComponent index,
Functor&& f,
void* storage,
Args&&... args) noexcept(noexcept(f(std::declval<const T0&>(), args...)))
{
if (index < $(max_expanded))
{
return VariantCastAndCallImpl<ReturnType>(
brigand::list<$type_list(max_expanded - 1)>{},
index,
f,
storage,
args...);
}
else
{
return VariantCastAndCallImpl<ReturnType>(
brigand::list<T$(max_expanded), RemainingT...>{}, index - $(max_expanded), f, storage, args...);
}
return VariantCastAndCallImplR<decltype(f(storage.V0, args...))>(
std::false_type{}, index, std::forward<Functor>(f), storage, std::forward<Args>(args)...);
}
}