2019-07-14 23:38:30 +00:00
|
|
|
//============================================================================
|
|
|
|
// Copyright (c) Kitware, Inc.
|
|
|
|
// All rights reserved.
|
|
|
|
// See LICENSE.txt for details.
|
|
|
|
//
|
|
|
|
// This software is distributed WITHOUT ANY WARRANTY; without even
|
|
|
|
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
|
|
// PURPOSE. See the above copyright notice for more information.
|
|
|
|
//============================================================================
|
|
|
|
#ifndef vtk_m_internal_Variant_h
|
|
|
|
#define vtk_m_internal_Variant_h
|
|
|
|
|
2019-07-22 19:56:35 +00:00
|
|
|
#include <vtkm/internal/VariantDetail.h>
|
2019-07-14 23:38:30 +00:00
|
|
|
|
2019-12-05 02:17:19 +00:00
|
|
|
#include <vtkm/Deprecated.h>
|
|
|
|
#include <vtkm/List.h>
|
2019-07-14 23:38:30 +00:00
|
|
|
|
2019-12-12 20:42:11 +00:00
|
|
|
#if defined(VTKM_USING_GLIBCXX_4)
|
2019-07-29 18:57:21 +00:00
|
|
|
// It would make sense to put this in its own header file, but it is hard to imagine needing
|
|
|
|
// aligned_union anywhere else.
|
|
|
|
#include <algorithm>
|
|
|
|
namespace vtkmstd
|
|
|
|
{
|
2019-08-08 13:59:43 +00:00
|
|
|
|
2019-07-29 18:57:21 +00:00
|
|
|
template <std::size_t... Xs>
|
|
|
|
struct max_size;
|
|
|
|
template <std::size_t X>
|
|
|
|
struct max_size<X>
|
|
|
|
{
|
|
|
|
static constexpr std::size_t value = X;
|
|
|
|
};
|
|
|
|
template <std::size_t X0, std::size_t... Xs>
|
|
|
|
struct max_size<X0, Xs...>
|
|
|
|
{
|
|
|
|
static constexpr std::size_t other_value = max_size<Xs...>::value;
|
|
|
|
static constexpr std::size_t value = (other_value > X0) ? other_value : X0;
|
|
|
|
};
|
2019-08-08 13:59:43 +00:00
|
|
|
|
|
|
|
// This is to get around an apparent bug in GCC 4.8 where alianas(x) does not
|
|
|
|
// seem to work when x is a constexpr. See
|
|
|
|
// https://stackoverflow.com/questions/29879609/g-complains-constexpr-function-is-not-a-constant-expression
|
|
|
|
template <std::size_t Alignment, std::size_t Size>
|
|
|
|
struct aligned_data_block
|
|
|
|
{
|
|
|
|
alignas(Alignment) char _s[Size];
|
|
|
|
};
|
|
|
|
|
2019-07-29 18:57:21 +00:00
|
|
|
template <std::size_t Len, class... Types>
|
|
|
|
struct aligned_union
|
|
|
|
{
|
|
|
|
static constexpr std::size_t alignment_value = vtkmstd::max_size<alignof(Types)...>::value;
|
|
|
|
|
2019-08-08 13:59:43 +00:00
|
|
|
using type =
|
|
|
|
vtkmstd::aligned_data_block<alignment_value, vtkmstd::max_size<Len, sizeof(Types)...>::value>;
|
2019-07-29 18:57:21 +00:00
|
|
|
};
|
2019-08-08 13:59:43 +00:00
|
|
|
|
2019-12-12 20:42:11 +00:00
|
|
|
// GCC 4.8 and 4.9 standard library does not support std::is_trivially_copyable.
|
2019-10-30 21:53:20 +00:00
|
|
|
// There is no relyable way to get this information (since it has to come special from
|
|
|
|
// the compiler). For our purposes, we will report as nothing being trivially copyable,
|
|
|
|
// which causes us to call the constructors with everything. This should be fine unless
|
|
|
|
// some other part of the compiler is trying to check for trivial copies (perhaps nvcc
|
|
|
|
// on top of GCC 4.8).
|
|
|
|
template <typename>
|
|
|
|
struct is_trivially_copyable : std::false_type
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace vtkmstd
|
|
|
|
|
2019-12-12 20:42:11 +00:00
|
|
|
#else // NOT VTKM_USING_GLIBCXX_4
|
2019-10-30 21:53:20 +00:00
|
|
|
namespace vtkmstd
|
|
|
|
{
|
|
|
|
|
2019-12-12 20:42:11 +00:00
|
|
|
using std::aligned_union;
|
2019-10-30 21:53:20 +00:00
|
|
|
using std::is_trivially_copyable;
|
|
|
|
|
|
|
|
} // namespace vtkmstd
|
2019-12-12 20:42:11 +00:00
|
|
|
#endif
|
2019-10-30 21:53:20 +00:00
|
|
|
|
2019-07-14 23:38:30 +00:00
|
|
|
namespace vtkm
|
|
|
|
{
|
|
|
|
namespace internal
|
|
|
|
{
|
|
|
|
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
// Forward declaration
|
|
|
|
template <typename... Ts>
|
|
|
|
class Variant;
|
|
|
|
|
2019-07-14 23:38:30 +00:00
|
|
|
namespace detail
|
|
|
|
{
|
|
|
|
|
|
|
|
struct VariantCopyFunctor
|
|
|
|
{
|
2019-07-16 21:41:27 +00:00
|
|
|
VTKM_SUPPRESS_EXEC_WARNINGS
|
2019-07-14 23:38:30 +00:00
|
|
|
template <typename T>
|
2019-07-25 21:25:29 +00:00
|
|
|
VTKM_EXEC_CONT void operator()(const T& src, void* destPointer) const noexcept
|
2019-07-14 23:38:30 +00:00
|
|
|
{
|
|
|
|
new (destPointer) T(src);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VariantDestroyFunctor
|
|
|
|
{
|
2019-07-16 21:41:27 +00:00
|
|
|
VTKM_SUPPRESS_EXEC_WARNINGS
|
2019-07-14 23:38:30 +00:00
|
|
|
template <typename T>
|
2019-07-25 21:25:29 +00:00
|
|
|
VTKM_EXEC_CONT void operator()(T& src) const noexcept
|
2019-07-14 23:38:30 +00:00
|
|
|
{
|
|
|
|
src.~T();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
template <typename... Ts>
|
|
|
|
struct AllTriviallyCopyable;
|
|
|
|
|
|
|
|
template <>
|
|
|
|
struct AllTriviallyCopyable<> : std::true_type
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T0>
|
|
|
|
struct AllTriviallyCopyable<T0>
|
2019-10-30 21:53:20 +00:00
|
|
|
: std::integral_constant<bool, (vtkmstd::is_trivially_copyable<T0>::value)>
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T0, typename T1>
|
|
|
|
struct AllTriviallyCopyable<T0, T1>
|
|
|
|
: std::integral_constant<bool,
|
2019-10-30 21:53:20 +00:00
|
|
|
(vtkmstd::is_trivially_copyable<T0>::value &&
|
|
|
|
vtkmstd::is_trivially_copyable<T1>::value)>
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T0, typename T1, typename T2>
|
|
|
|
struct AllTriviallyCopyable<T0, T1, T2>
|
|
|
|
: std::integral_constant<bool,
|
2019-10-30 21:53:20 +00:00
|
|
|
(vtkmstd::is_trivially_copyable<T0>::value &&
|
|
|
|
vtkmstd::is_trivially_copyable<T1>::value &&
|
|
|
|
vtkmstd::is_trivially_copyable<T2>::value)>
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T0, typename T1, typename T2, typename T3>
|
|
|
|
struct AllTriviallyCopyable<T0, T1, T2, T3>
|
|
|
|
: std::integral_constant<bool,
|
2019-10-30 21:53:20 +00:00
|
|
|
(vtkmstd::is_trivially_copyable<T0>::value &&
|
|
|
|
vtkmstd::is_trivially_copyable<T1>::value &&
|
|
|
|
vtkmstd::is_trivially_copyable<T2>::value &&
|
|
|
|
vtkmstd::is_trivially_copyable<T3>::value)>
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
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,
|
2019-10-30 21:53:20 +00:00
|
|
|
(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 &&
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
AllTriviallyCopyable<Ts...>::value)>
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename VariantType>
|
|
|
|
struct VariantTriviallyCopyable;
|
2019-07-14 23:38:30 +00:00
|
|
|
|
|
|
|
template <typename... Ts>
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
struct VariantTriviallyCopyable<vtkm::internal::Variant<Ts...>> : AllTriviallyCopyable<Ts...>
|
2019-07-14 23:38:30 +00:00
|
|
|
{
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
};
|
2019-07-14 23:38:30 +00:00
|
|
|
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
template <typename... Ts>
|
|
|
|
struct VariantStorageImpl
|
|
|
|
{
|
2019-07-29 18:57:21 +00:00
|
|
|
typename vtkmstd::aligned_union<0, Ts...>::type Storage;
|
2019-07-14 23:38:30 +00:00
|
|
|
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
vtkm::IdComponent Index = -1;
|
|
|
|
|
|
|
|
template <vtkm::IdComponent Index>
|
2019-12-05 02:17:19 +00:00
|
|
|
using TypeAt = typename vtkm::ListAt<vtkm::List<Ts...>, Index>;
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
|
2019-07-14 23:38:30 +00:00
|
|
|
VTKM_EXEC_CONT void* GetPointer() { return reinterpret_cast<void*>(&this->Storage); }
|
|
|
|
VTKM_EXEC_CONT const void* GetPointer() const
|
|
|
|
{
|
|
|
|
return reinterpret_cast<const void*>(&this->Storage);
|
|
|
|
}
|
|
|
|
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
VTKM_EXEC_CONT vtkm::IdComponent GetIndex() const noexcept { return this->Index; }
|
|
|
|
VTKM_EXEC_CONT bool IsValid() const noexcept { return this->GetIndex() >= 0; }
|
|
|
|
|
|
|
|
VTKM_EXEC_CONT void Reset() noexcept
|
|
|
|
{
|
|
|
|
if (this->IsValid())
|
|
|
|
{
|
|
|
|
this->CastAndCall(detail::VariantDestroyFunctor{});
|
|
|
|
this->Index = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Functor, typename... Args>
|
|
|
|
VTKM_EXEC_CONT auto CastAndCall(Functor&& f, Args&&... args) const
|
|
|
|
noexcept(noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
|
|
|
|
-> 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)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Functor, typename... Args>
|
|
|
|
VTKM_EXEC_CONT auto CastAndCall(Functor&& f, Args&&... args) noexcept(
|
|
|
|
noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
|
|
|
|
-> 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)...);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename VariantType,
|
|
|
|
typename TriviallyCopyable = typename VariantTriviallyCopyable<VariantType>::type>
|
|
|
|
struct VariantConstructorImpl;
|
|
|
|
|
|
|
|
template <typename... Ts>
|
|
|
|
struct VariantConstructorImpl<vtkm::internal::Variant<Ts...>, std::true_type>
|
|
|
|
: VariantStorageImpl<Ts...>
|
|
|
|
{
|
|
|
|
VariantConstructorImpl() = default;
|
|
|
|
~VariantConstructorImpl() = default;
|
|
|
|
|
|
|
|
VariantConstructorImpl(const VariantConstructorImpl&) = default;
|
|
|
|
VariantConstructorImpl(VariantConstructorImpl&&) = default;
|
|
|
|
VariantConstructorImpl& operator=(const VariantConstructorImpl&) = default;
|
|
|
|
VariantConstructorImpl& operator=(VariantConstructorImpl&&) = default;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename... Ts>
|
|
|
|
struct VariantConstructorImpl<vtkm::internal::Variant<Ts...>, std::false_type>
|
|
|
|
: VariantStorageImpl<Ts...>
|
|
|
|
{
|
|
|
|
VariantConstructorImpl() = default;
|
|
|
|
|
|
|
|
VTKM_EXEC_CONT ~VariantConstructorImpl() { this->Reset(); }
|
|
|
|
|
|
|
|
VTKM_EXEC_CONT VariantConstructorImpl(const VariantConstructorImpl& src) noexcept
|
|
|
|
{
|
|
|
|
src.CastAndCall(VariantCopyFunctor{}, this->GetPointer());
|
|
|
|
this->Index = src.Index;
|
|
|
|
}
|
|
|
|
|
|
|
|
VTKM_EXEC_CONT VariantConstructorImpl(VariantConstructorImpl&& rhs) noexcept
|
|
|
|
{
|
|
|
|
this->Storage = std::move(rhs.Storage);
|
|
|
|
this->Index = std::move(rhs.Index);
|
|
|
|
rhs.Index = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
VTKM_EXEC_CONT VariantConstructorImpl& operator=(const VariantConstructorImpl& src) noexcept
|
|
|
|
{
|
|
|
|
this->Reset();
|
|
|
|
src.CastAndCall(detail::VariantCopyFunctor{}, this->GetPointer());
|
|
|
|
this->Index = src.Index;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
VTKM_EXEC_CONT VariantConstructorImpl& operator=(VariantConstructorImpl&& rhs) noexcept
|
|
|
|
{
|
|
|
|
this->Reset();
|
|
|
|
this->Storage = std::move(rhs.Storage);
|
|
|
|
this->Index = std::move(rhs.Index);
|
|
|
|
rhs.Index = -1;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
template <typename... Ts>
|
|
|
|
class Variant : detail::VariantConstructorImpl<Variant<Ts...>>
|
|
|
|
{
|
|
|
|
using Superclass = detail::VariantConstructorImpl<Variant<Ts...>>;
|
2019-07-14 23:38:30 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
/// Returns the index of the type of object this variant is storing. If no object is currently
|
|
|
|
/// stored (i.e. the Variant is invalid), -1 is returned.
|
|
|
|
///
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
VTKM_EXEC_CONT vtkm::IdComponent GetIndex() const noexcept
|
|
|
|
{
|
|
|
|
return this->Superclass::GetIndex();
|
|
|
|
}
|
2019-07-14 23:38:30 +00:00
|
|
|
|
|
|
|
/// Returns true if this Variant is storing an object from one of the types in the template
|
|
|
|
/// list, false otherwise.
|
|
|
|
///
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
VTKM_EXEC_CONT bool IsValid() const noexcept { return this->Superclass::IsValid(); }
|
2019-07-14 23:38:30 +00:00
|
|
|
|
|
|
|
/// 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>
|
2019-12-05 02:17:19 +00:00
|
|
|
using IndexOf =
|
|
|
|
std::integral_constant<vtkm::IdComponent, vtkm::ListIndexOf<vtkm::List<Ts...>, T>::value>;
|
2019-07-14 23:38:30 +00:00
|
|
|
|
|
|
|
/// Returns the index for the given type (or -1 if that type is not in the list).
|
|
|
|
///
|
|
|
|
template <typename T>
|
|
|
|
VTKM_EXEC_CONT static constexpr vtkm::IdComponent GetIndexOf()
|
|
|
|
{
|
|
|
|
return IndexOf<T>::value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Type that converts to the type at the given index.
|
|
|
|
///
|
|
|
|
template <vtkm::IdComponent Index>
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
using TypeAt = typename Superclass::template TypeAt<Index>;
|
2019-07-14 23:38:30 +00:00
|
|
|
|
|
|
|
/// The number of types representable by this Variant.
|
|
|
|
///
|
|
|
|
static constexpr vtkm::IdComponent NumberOfTypes = vtkm::IdComponent{ sizeof...(Ts) };
|
|
|
|
|
2019-07-16 21:41:27 +00:00
|
|
|
Variant() = default;
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
~Variant() = default;
|
|
|
|
Variant(const Variant&) = default;
|
|
|
|
Variant(Variant&&) = default;
|
|
|
|
Variant& operator=(const Variant&) = default;
|
|
|
|
Variant& operator=(Variant&&) = default;
|
2019-07-14 23:38:30 +00:00
|
|
|
|
|
|
|
template <typename T>
|
2019-07-25 21:25:29 +00:00
|
|
|
VTKM_EXEC_CONT Variant(const T& src) noexcept
|
2019-07-14 23:38:30 +00:00
|
|
|
{
|
2019-07-15 04:51:28 +00:00
|
|
|
constexpr vtkm::IdComponent index = GetIndexOf<T>();
|
2019-07-14 23:38:30 +00:00
|
|
|
// 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 <typename T>
|
2019-07-25 21:25:29 +00:00
|
|
|
VTKM_EXEC_CONT Variant(const T&& src) noexcept
|
2019-07-14 23:38:30 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename... Args>
|
|
|
|
VTKM_EXEC_CONT T& Emplace(Args&&... args)
|
|
|
|
{
|
|
|
|
constexpr vtkm::IdComponent I = GetIndexOf<T>();
|
|
|
|
VTKM_STATIC_ASSERT_MSG(I >= 0, "Variant::Emplace called with invalid type.");
|
|
|
|
return this->EmplaceImpl<T, I>(std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename U, typename... Args>
|
|
|
|
VTKM_EXEC_CONT T& Emplace(std::initializer_list<U> il, Args&&... args)
|
|
|
|
{
|
|
|
|
constexpr vtkm::IdComponent I = GetIndexOf<T>();
|
|
|
|
VTKM_STATIC_ASSERT_MSG(I >= 0, "Variant::Emplace called with invalid type.");
|
|
|
|
return this->EmplaceImpl<T, I>(il, std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <vtkm::IdComponent I, typename... Args>
|
|
|
|
VTKM_EXEC_CONT TypeAt<I>& Emplace(Args&&... args)
|
|
|
|
{
|
|
|
|
VTKM_STATIC_ASSERT_MSG((I >= 0) && (I < NumberOfTypes),
|
|
|
|
"Variant::Emplace called with invalid index");
|
|
|
|
return this->EmplaceImpl<TypeAt<I>, I>(std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <vtkm::IdComponent I, typename U, typename... Args>
|
|
|
|
VTKM_EXEC_CONT TypeAt<I>& Emplace(std::initializer_list<U> il, Args&&... args)
|
|
|
|
{
|
|
|
|
VTKM_STATIC_ASSERT_MSG((I >= 0) && (I < NumberOfTypes),
|
|
|
|
"Variant::Emplace called with invalid index");
|
|
|
|
return this->EmplaceImpl<TypeAt<I>, I>(il, std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <typename T, vtkm::IdComponent I, typename... Args>
|
|
|
|
VTKM_EXEC_CONT T& EmplaceImpl(Args&&... args)
|
|
|
|
{
|
|
|
|
this->Reset();
|
|
|
|
T* value = new (this->GetPointer()) T{ args... };
|
|
|
|
this->Index = I;
|
|
|
|
return *value;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, vtkm::IdComponent I, typename U, typename... Args>
|
|
|
|
VTKM_EXEC_CONT T& EmplaceImpl(std::initializer_list<U> 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.
|
|
|
|
///
|
2019-07-16 21:41:27 +00:00
|
|
|
template <vtkm::IdComponent I>
|
2019-07-25 21:25:29 +00:00
|
|
|
VTKM_EXEC_CONT TypeAt<I>& Get() noexcept
|
2019-07-14 23:38:30 +00:00
|
|
|
{
|
2019-07-16 21:41:27 +00:00
|
|
|
VTKM_ASSERT(I == this->GetIndex());
|
|
|
|
return *reinterpret_cast<TypeAt<I>*>(this->GetPointer());
|
2019-07-14 23:38:30 +00:00
|
|
|
}
|
|
|
|
|
2019-07-16 21:41:27 +00:00
|
|
|
template <vtkm::IdComponent I>
|
2019-07-25 21:25:29 +00:00
|
|
|
VTKM_EXEC_CONT const TypeAt<I>& Get() const noexcept
|
2019-07-14 23:38:30 +00:00
|
|
|
{
|
2019-07-16 21:41:27 +00:00
|
|
|
VTKM_ASSERT(I == this->GetIndex());
|
|
|
|
return *reinterpret_cast<const TypeAt<I>*>(this->GetPointer());
|
2019-07-14 23:38:30 +00:00
|
|
|
}
|
|
|
|
//@}
|
|
|
|
|
|
|
|
//@{
|
|
|
|
/// Returns the value as the given type. The behavior is undefined if the variant does not
|
|
|
|
/// contain a value of the given type.
|
|
|
|
///
|
|
|
|
template <typename T>
|
2019-07-25 21:25:29 +00:00
|
|
|
VTKM_EXEC_CONT T& Get() noexcept
|
2019-07-14 23:38:30 +00:00
|
|
|
{
|
|
|
|
VTKM_ASSERT(this->GetIndexOf<T>() == this->GetIndex());
|
|
|
|
return *reinterpret_cast<T*>(this->GetPointer());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2019-07-25 21:25:29 +00:00
|
|
|
VTKM_EXEC_CONT const T& Get() const noexcept
|
2019-07-14 23:38:30 +00:00
|
|
|
{
|
|
|
|
VTKM_ASSERT(this->GetIndexOf<T>() == this->GetIndex());
|
|
|
|
return *reinterpret_cast<const T*>(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 <typename Functor, typename... Args>
|
|
|
|
VTKM_EXEC_CONT auto CastAndCall(Functor&& f, Args&&... args) const
|
2019-07-25 21:25:29 +00:00
|
|
|
noexcept(noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
|
|
|
|
-> decltype(f(std::declval<const TypeAt<0>&>(), args...))
|
2019-07-14 23:38:30 +00:00
|
|
|
{
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
return this->Superclass::CastAndCall(std::forward<Functor>(f), std::forward<Args>(args)...);
|
2019-07-14 23:38:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Functor, typename... Args>
|
2019-07-25 21:25:29 +00:00
|
|
|
VTKM_EXEC_CONT auto CastAndCall(Functor&& f, Args&&... args) noexcept(
|
|
|
|
noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
|
2019-07-14 23:38:30 +00:00
|
|
|
-> decltype(f(std::declval<TypeAt<0>&>(), args...))
|
|
|
|
{
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
return this->Superclass::CastAndCall(std::forward<Functor>(f), std::forward<Args>(args)...);
|
2019-07-14 23:38:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Destroys any object the Variant is holding and sets the Variant to an invalid state. This
|
|
|
|
/// method is not thread safe.
|
|
|
|
///
|
Register Variant as trivially copyable if possible
The Variant template can hold any type. If it is holding a type that is
non-copyable, then it has to make sure that appropriate constructors,
copiers, movers, and destructors are called.
Previously, these were called even the Variant was holding a trivially
copyable class because no harm no foul. If you were holding a trivially
copyable class and did a memcpy, that work work, which should make it
possible to copy between host and device, right?
In theory yes, but in practice no. The problem is that Cuda is
outsmarting the code. It is checking that Variant is not trivially-
copyable by C++ semantics and refusing to push it.
So, change Variant to check to see if all its supported classes are
trivially copyable. If they are, then it use the default constructors,
destructors, movers, and copiers so that C++ recognizes it as trivially
copyable.
2019-10-26 03:43:22 +00:00
|
|
|
VTKM_EXEC_CONT void Reset() noexcept { this->Superclass::Reset(); }
|
2019-07-14 23:38:30 +00:00
|
|
|
};
|
2019-07-15 03:35:45 +00:00
|
|
|
|
|
|
|
/// \brief Convert a ListTag to a Variant.
|
|
|
|
///
|
2019-12-05 02:17:19 +00:00
|
|
|
/// Depricated. Use ListAsVariant instead.
|
|
|
|
///
|
2019-07-15 03:35:45 +00:00
|
|
|
template <typename ListTag>
|
2019-12-05 02:17:19 +00:00
|
|
|
using ListTagAsVariant VTKM_DEPRECATED(
|
|
|
|
1.6,
|
|
|
|
"vtkm::ListTag is no longer supported. Use vtkm::List instead.") =
|
|
|
|
vtkm::ListApply<ListTag, vtkm::internal::Variant>;
|
|
|
|
|
|
|
|
/// \brief Convert a `List` to a `Variant`.
|
|
|
|
///
|
|
|
|
template <typename List>
|
|
|
|
using ListAsVariant = vtkm::ListApply<List, vtkm::internal::Variant>;
|
2019-07-14 23:38:30 +00:00
|
|
|
}
|
|
|
|
} // namespace vtkm::internal
|
|
|
|
|
|
|
|
#endif //vtk_m_internal_Variant_h
|