vtk-m/vtkm/internal/VariantImplDetail.h
Kenneth Moreland 0182eb9d9d Test variant arguments to worklets
Add a regression test for passing a variant as an argument to a worklet.
Variants are tricky objects, and the compiler might make some strange
assumptions during optimization.

One case that popped up in particular was when a variant contained two
objects with the same `sizeof` but the first object had padding. When the
variant contained the second object, the value under the padded part of
the first object was not set.
2023-04-19 11:30:47 -06:00

1155 lines
43 KiB
C++

//============================================================================
// 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.
//============================================================================
// **** DO NOT EDIT THIS FILE!!! ****
// This file is automatically generated by VariantDetail.h.in
#if !defined(VTK_M_DEVICE) || !defined(VTK_M_NAMESPACE)
#error VarianImplDetail.h must be included from VariantImpl.h
// Some defines to make my IDE happy.
#define VTK_M_DEVICE
#define VTK_M_NAMESPACE tmp
#endif
#include <vtkm/List.h>
#include <vtkm/Types.h>
#include <vtkm/internal/Assume.h>
#include <vtkmstd/is_trivial.h>
#include <algorithm>
#include <type_traits>
namespace vtkm
{
namespace VTK_M_NAMESPACE
{
namespace detail
{
// --------------------------------------------------------------------------------
// Helper classes to determine if all Variant types are trivial.
template <typename... Ts>
using AllTriviallyCopyable = vtkm::ListAll<vtkm::List<Ts...>, vtkmstd::is_trivially_copyable>;
// Single argument version of is_trivially_constructible
template <typename T>
using Constructible = vtkmstd::is_trivially_constructible<T>;
template <typename... Ts>
using AllTriviallyConstructible = vtkm::ListAll<vtkm::List<Ts...>, Constructible>;
template <typename... Ts>
using AllTriviallyDestructible =
vtkm::ListAll<vtkm::List<Ts...>, vtkmstd::is_trivially_destructible>;
// --------------------------------------------------------------------------------
// Helper functions to determine the maximum type size.
#if defined(VTKM_GCC) && (__GNUC__ == 5)
// GCC5 gives an error with `sizeof(Ts)...` for an unexpanded parameter pack.
template <typename T0>
constexpr std::size_t MaxSizeOf()
{
return sizeof(T0);
}
template <typename T0, typename T1, typename... Ts>
constexpr std::size_t MaxSizeOf()
{
return std::max(sizeof(T0), MaxSizeOf<T1, Ts...>());
}
#else
template <typename... Ts>
constexpr std::size_t MaxSizeOf()
{
return std::max({ sizeof(Ts)... });
}
#endif
// --------------------------------------------------------------------------------
// Placeholder for a fully used structure of the given type.
template <std::size_t Size, bool = (Size > 8)>
struct SizedPlaceholder
{
VTKM_STATIC_ASSERT(Size > 0);
vtkm::Int8 A;
SizedPlaceholder<Size - 1> B;
};
template <>
struct SizedPlaceholder<1, false>
{
vtkm::Int8 A;
};
template <std::size_t Size>
struct SizedPlaceholder<Size, true>
{
vtkm::Int8 A;
vtkm::Int8 B;
vtkm::Int8 C;
vtkm::Int8 D;
vtkm::Int8 E;
vtkm::Int8 F;
vtkm::Int8 G;
vtkm::Int8 H;
SizedPlaceholder<Size - 8> I;
};
// clang-format off
// --------------------------------------------------------------------------------
// 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, typename... Ts>
union VariantUnionTD;
// NTD = non-trivially deconstructible
template <typename T0, typename... Ts>
union VariantUnionNTD;
template <typename T0>
union VariantUnionTD<T0>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0>()> Placeholder;
T0 V0;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <typename T0>
union VariantUnionNTD<T0>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0>()> Placeholder;
T0 V0;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
template <typename T0, typename T1>
union VariantUnionTD<T0, T1>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1>()> Placeholder;
T0 V0;
T1 V1;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <typename T0, typename T1>
union VariantUnionNTD<T0, T1>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1>()> Placeholder;
T0 V0;
T1 V1;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
template <typename T0, typename T1, typename T2>
union VariantUnionTD<T0, T1, T2>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <typename T0, typename T1, typename T2>
union VariantUnionNTD<T0, T1, T2>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
template <typename T0, typename T1, typename T2, typename T3>
union VariantUnionTD<T0, T1, T2, T3>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <typename T0, typename T1, typename T2, typename T3>
union VariantUnionNTD<T0, T1, T2, T3>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
template <typename T0, typename T1, typename T2, typename T3, typename T4>
union VariantUnionTD<T0, T1, T2, T3, T4>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
T4 V4;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <typename T0, typename T1, typename T2, typename T3, typename T4>
union VariantUnionNTD<T0, T1, T2, T3, T4>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
T4 V4;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
union VariantUnionTD<T0, T1, T2, T3, T4, T5>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
T4 V4;
T5 V5;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
union VariantUnionNTD<T0, T1, T2, T3, T4, T5>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
T4 V4;
T5 V5;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
T4 V4;
T5 V5;
T6 V6;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
T4 V4;
T5 V5;
T6 V6;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6, T7>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6, T7>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
T4 V4;
T5 V5;
T6 V6;
T7 V7;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6, T7>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6, T7>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
T4 V4;
T5 V5;
T6 V6;
T7 V7;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename... Ts>
union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
T4 V4;
T5 V5;
T6 V6;
T7 V7;
VariantUnionTD<T8, Ts...> Remaining;
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
VariantUnionTD() = default;
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename... Ts>
union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>
{
// Work around issue where CUDA sometimes seems to miss initializing some struct members
// if another entry in the varient has a struct with padding. Place an item that requires
// everthing to be copied.
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>()> Placeholder;
T0 V0;
T1 V1;
T2 V2;
T3 V3;
T4 V4;
T5 V5;
T6 V6;
T7 V7;
VariantUnionNTD<T8, Ts...> Remaining;
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
VariantUnionNTD() = default;
VTK_M_DEVICE ~VariantUnionNTD() { }
};
//clang-format on
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;
template <typename UnionType>
struct VariantUnionGetImpl<0, UnionType>
{
using ReturnType = decltype(std::declval<UnionType>().V0);
VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
{
return storage.V0;
}
VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
{
return storage.V0;
}
};
template <typename UnionType>
struct VariantUnionGetImpl<1, UnionType>
{
using ReturnType = decltype(std::declval<UnionType>().V1);
VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
{
return storage.V1;
}
VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
{
return storage.V1;
}
};
template <typename UnionType>
struct VariantUnionGetImpl<2, UnionType>
{
using ReturnType = decltype(std::declval<UnionType>().V2);
VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
{
return storage.V2;
}
VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
{
return storage.V2;
}
};
template <typename UnionType>
struct VariantUnionGetImpl<3, UnionType>
{
using ReturnType = decltype(std::declval<UnionType>().V3);
VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
{
return storage.V3;
}
VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
{
return storage.V3;
}
};
template <typename UnionType>
struct VariantUnionGetImpl<4, UnionType>
{
using ReturnType = decltype(std::declval<UnionType>().V4);
VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
{
return storage.V4;
}
VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
{
return storage.V4;
}
};
template <typename UnionType>
struct VariantUnionGetImpl<5, UnionType>
{
using ReturnType = decltype(std::declval<UnionType>().V5);
VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
{
return storage.V5;
}
VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
{
return storage.V5;
}
};
template <typename UnionType>
struct VariantUnionGetImpl<6, UnionType>
{
using ReturnType = decltype(std::declval<UnionType>().V6);
VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
{
return storage.V6;
}
VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
{
return storage.V6;
}
};
template <typename UnionType>
struct VariantUnionGetImpl<7, UnionType>
{
using ReturnType = decltype(std::declval<UnionType>().V7);
VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
{
return storage.V7;
}
VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
{
return storage.V7;
}
};
template <vtkm::IdComponent I, typename UnionType>
struct VariantUnionGetImpl
{
VTKM_STATIC_ASSERT(I >= 8);
using RecursiveGet = VariantUnionGetImpl<I - 8, 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);
}
// --------------------------------------------------------------------------------
// Internal implementation of CastAndCall for Variant
template <std::size_t NumCases>
struct VariantCases
{
template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE static
#ifdef VTKM_HIP
// this is a temporary solution to improve Kokkos/HIP compile times for
// ConnectivityTracer in Rendering.
//
// This function currently gets inlined many times, which dramatically increases
// both compile time and the size of the resulting code-object
__attribute__((noinline))
#else
inline
#endif
auto CastAndCall(
vtkm::IdComponent index,
Functor&& f,
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
VTKM_ASSERT((index >= 0) && (index < static_cast<vtkm::IdComponent>(NumCases)));
switch (index)
{
case 0:
// 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(storage.V0, std::forward<Args>(args)...);
case 1:
// 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(storage.V1, std::forward<Args>(args)...);
case 2:
// 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(storage.V2, std::forward<Args>(args)...);
case 3:
// 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(storage.V3, std::forward<Args>(args)...);
case 4:
// 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(storage.V4, std::forward<Args>(args)...);
case 5:
// 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(storage.V5, std::forward<Args>(args)...);
case 6:
// 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(storage.V6, std::forward<Args>(args)...);
case 7:
// 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(storage.V7, std::forward<Args>(args)...);
default:
return VariantCases<NumCases - 8>::template CastAndCall(
index - 8, std::forward<Functor>(f), storage.Remaining, std::forward<Args>(args)...);
}
}
};
template<>
struct VariantCases<1>
{
template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE static inline auto CastAndCall(
vtkm::IdComponent index,
Functor&& f,
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
// Assume index is 0. Saves us some conditionals.
VTKM_ASSERT(index == 0);
(void)index;
return f(storage.V0, std::forward<Args>(args)...);
}
};
template<>
struct VariantCases<2>
{
template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE static
#ifdef VTKM_HIP
// this is a temporary solution to improve Kokkos/HIP compile times for
// ConnectivityTracer in Rendering.
//
// This function currently gets inlined many times, which dramatically increases
// both compile time and the size of the resulting code-object
__attribute__((noinline))
#else
inline
#endif
auto CastAndCall(
vtkm::IdComponent index,
Functor&& f,
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
// Assume index is 0. Saves us some conditionals.
VTKM_ASSERT((index >= 0) && (index < 2));
switch (index)
{
default:
case 0:
// 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(storage.V0, std::forward<Args>(args)...);
case 1:
// 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(storage.V1, std::forward<Args>(args)...);
}
}
};
template<>
struct VariantCases<3>
{
template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE static
#ifdef VTKM_HIP
// this is a temporary solution to improve Kokkos/HIP compile times for
// ConnectivityTracer in Rendering.
//
// This function currently gets inlined many times, which dramatically increases
// both compile time and the size of the resulting code-object
__attribute__((noinline))
#else
inline
#endif
auto CastAndCall(
vtkm::IdComponent index,
Functor&& f,
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
// Assume index is 0. Saves us some conditionals.
VTKM_ASSERT((index >= 0) && (index < 3));
switch (index)
{
default:
case 0:
// 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(storage.V0, std::forward<Args>(args)...);
case 1:
// 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(storage.V1, std::forward<Args>(args)...);
case 2:
// 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(storage.V2, std::forward<Args>(args)...);
}
}
};
template<>
struct VariantCases<4>
{
template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE static
#ifdef VTKM_HIP
// this is a temporary solution to improve Kokkos/HIP compile times for
// ConnectivityTracer in Rendering.
//
// This function currently gets inlined many times, which dramatically increases
// both compile time and the size of the resulting code-object
__attribute__((noinline))
#else
inline
#endif
auto CastAndCall(
vtkm::IdComponent index,
Functor&& f,
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
// Assume index is 0. Saves us some conditionals.
VTKM_ASSERT((index >= 0) && (index < 4));
switch (index)
{
default:
case 0:
// 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(storage.V0, std::forward<Args>(args)...);
case 1:
// 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(storage.V1, std::forward<Args>(args)...);
case 2:
// 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(storage.V2, std::forward<Args>(args)...);
case 3:
// 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(storage.V3, std::forward<Args>(args)...);
}
}
};
template<>
struct VariantCases<5>
{
template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE static
#ifdef VTKM_HIP
// this is a temporary solution to improve Kokkos/HIP compile times for
// ConnectivityTracer in Rendering.
//
// This function currently gets inlined many times, which dramatically increases
// both compile time and the size of the resulting code-object
__attribute__((noinline))
#else
inline
#endif
auto CastAndCall(
vtkm::IdComponent index,
Functor&& f,
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
// Assume index is 0. Saves us some conditionals.
VTKM_ASSERT((index >= 0) && (index < 5));
switch (index)
{
default:
case 0:
// 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(storage.V0, std::forward<Args>(args)...);
case 1:
// 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(storage.V1, std::forward<Args>(args)...);
case 2:
// 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(storage.V2, std::forward<Args>(args)...);
case 3:
// 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(storage.V3, std::forward<Args>(args)...);
case 4:
// 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(storage.V4, std::forward<Args>(args)...);
}
}
};
template<>
struct VariantCases<6>
{
template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE static
#ifdef VTKM_HIP
// this is a temporary solution to improve Kokkos/HIP compile times for
// ConnectivityTracer in Rendering.
//
// This function currently gets inlined many times, which dramatically increases
// both compile time and the size of the resulting code-object
__attribute__((noinline))
#else
inline
#endif
auto CastAndCall(
vtkm::IdComponent index,
Functor&& f,
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
// Assume index is 0. Saves us some conditionals.
VTKM_ASSERT((index >= 0) && (index < 6));
switch (index)
{
default:
case 0:
// 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(storage.V0, std::forward<Args>(args)...);
case 1:
// 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(storage.V1, std::forward<Args>(args)...);
case 2:
// 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(storage.V2, std::forward<Args>(args)...);
case 3:
// 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(storage.V3, std::forward<Args>(args)...);
case 4:
// 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(storage.V4, std::forward<Args>(args)...);
case 5:
// 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(storage.V5, std::forward<Args>(args)...);
}
}
};
template<>
struct VariantCases<7>
{
template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE static
#ifdef VTKM_HIP
// this is a temporary solution to improve Kokkos/HIP compile times for
// ConnectivityTracer in Rendering.
//
// This function currently gets inlined many times, which dramatically increases
// both compile time and the size of the resulting code-object
__attribute__((noinline))
#else
inline
#endif
auto CastAndCall(
vtkm::IdComponent index,
Functor&& f,
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
// Assume index is 0. Saves us some conditionals.
VTKM_ASSERT((index >= 0) && (index < 7));
switch (index)
{
default:
case 0:
// 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(storage.V0, std::forward<Args>(args)...);
case 1:
// 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(storage.V1, std::forward<Args>(args)...);
case 2:
// 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(storage.V2, std::forward<Args>(args)...);
case 3:
// 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(storage.V3, std::forward<Args>(args)...);
case 4:
// 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(storage.V4, std::forward<Args>(args)...);
case 5:
// 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(storage.V5, std::forward<Args>(args)...);
case 6:
// 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(storage.V6, std::forward<Args>(args)...);
}
}
};
template<>
struct VariantCases<8>
{
template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE static
#ifdef VTKM_HIP
// this is a temporary solution to improve Kokkos/HIP compile times for
// ConnectivityTracer in Rendering.
//
// This function currently gets inlined many times, which dramatically increases
// both compile time and the size of the resulting code-object
__attribute__((noinline))
#else
inline
#endif
auto CastAndCall(
vtkm::IdComponent index,
Functor&& f,
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
// Assume index is 0. Saves us some conditionals.
VTKM_ASSERT((index >= 0) && (index < 8));
switch (index)
{
default:
case 0:
// 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(storage.V0, std::forward<Args>(args)...);
case 1:
// 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(storage.V1, std::forward<Args>(args)...);
case 2:
// 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(storage.V2, std::forward<Args>(args)...);
case 3:
// 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(storage.V3, std::forward<Args>(args)...);
case 4:
// 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(storage.V4, std::forward<Args>(args)...);
case 5:
// 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(storage.V5, std::forward<Args>(args)...);
case 6:
// 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(storage.V6, std::forward<Args>(args)...);
case 7:
// 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(storage.V7, std::forward<Args>(args)...);
}
}
};
template <std::size_t UnionSize, typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE inline auto VariantCastAndCallImpl(
vtkm::IdComponent index,
Functor&& f,
UnionType& storage,
Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
-> decltype(f(storage.V0, args...))
{
return VariantCases<UnionSize>::CastAndCall(
index, std::forward<Functor>(f), storage, std::forward<Args>(args)...);
}
}
}
} // vtkm::VTK_M_NAMESPACE::detail