//============================================================================ // 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. //============================================================================ $# This file uses the pyexpander macro processing utility to build the $# FunctionInterface facilities that use a variable number of arguments. $# Information, documentation, and downloads for pyexpander can be found at: $# $# http://pyexpander.sourceforge.net/ $# $# To build the source code, execute the following (after installing $# pyexpander, of course): $# $# expander.py VariantDetail.h.in > VariantDetail.h $# $# Ignore the following comment. It is meant for the generated file. // **** 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 #include #include #include $py(max_expanded=20)\ $# Python commands used in template expansion. $py( def type_list(num_params): if num_params < 0: return '' result = 'T0' 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, typename_list)\ namespace vtkm { namespace VTK_M_NAMESPACE { namespace internal { namespace detail { template struct VariantDummyReturn { VTK_M_DEVICE static inline ReturnType F() noexcept { return ReturnType{}; } }; template <> struct VariantDummyReturn { VTK_M_DEVICE static inline void F() noexcept {} }; // clang-format off // -------------------------------------------------------------------------------- // Helper classes to determine if all Variant types are trivial. template struct AllTriviallyCopyable; $for(num_params in range(max_expanded))\ template <$typename_list(num_params)> struct AllTriviallyCopyable<$type_list(num_params)> : std::integral_constant::value && $for(param_index in range(1, num_params + 1))\ vtkmstd::is_trivially_copyable::value && $endfor\ true)> { }; $endfor\ template <$typename_list(max_expanded), typename... Ts> struct AllTriviallyCopyable<$type_list(max_expanded), Ts...> : std::integral_constant::value && $for(param_index in range(1, max_expanded))\ vtkmstd::is_trivially_copyable::value && $endfor\ AllTriviallyCopyable::value)> { }; template struct AllTriviallyConstructible; $for(num_params in range(max_expanded))\ template <$typename_list(num_params)> struct AllTriviallyConstructible<$type_list(num_params)> : std::integral_constant::value && $for(param_index in range(1, num_params + 1))\ vtkmstd::is_trivially_constructible::value && $endfor\ true)> { }; $endfor\ template <$typename_list(max_expanded), typename... Ts> struct AllTriviallyConstructible<$type_list(max_expanded), Ts...> : std::integral_constant::value && $for(param_index in range(1, max_expanded))\ vtkmstd::is_trivially_constructible::value && $endfor\ AllTriviallyConstructible::value)> { }; template struct AllTriviallyDestructible; $for(num_params in range(max_expanded))\ template <$typename_list(num_params)> struct AllTriviallyDestructible<$type_list(num_params)> : std::integral_constant::value && $for(param_index in range(1, num_params + 1))\ vtkmstd::is_trivially_destructible::value && $endfor\ true)> { }; $endfor\ template <$typename_list(max_expanded), typename... Ts> struct AllTriviallyDestructible<$type_list(max_expanded), Ts...> : std::integral_constant::value && $for(param_index in range(1, max_expanded))\ vtkmstd::is_trivially_destructible::value && $endfor\ AllTriviallyDestructible::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. 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 trick. 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 union VariantUnionTD; // NTD = non-trivially deconstructible template union VariantUnionNTD; $for(num_params in range(max_expanded))\ template <$typename_list(num_params)> union VariantUnionTD<$type_list(num_params)> { $for(param_index in range(num_params + 1))\ T$(param_index) V$(param_index); $endfor\ VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { } VariantUnionTD() = default; }; template <$typename_list(num_params)> union VariantUnionNTD<$type_list(num_params)> { $for(param_index in range(num_params + 1))\ T$(param_index) V$(param_index); $endfor\ VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { } VariantUnionNTD() = default; VTK_M_DEVICE ~VariantUnionNTD() { } }; $endfor\ template <$typename_list(max_expanded), typename... Ts> union VariantUnionTD<$type_list(max_expanded), Ts...> { vtkm::internal::NullType Dummy; $for(param_index in range(max_expanded))\ T$(param_index) V$(param_index); $endfor\ VariantUnionTD Remaining; VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { } VariantUnionTD() = default; }; template <$typename_list(max_expanded), typename... Ts> union VariantUnionNTD<$type_list(max_expanded), Ts...> { vtkm::internal::NullType Dummy; $for(param_index in range(max_expanded))\ T$(param_index) V$(param_index); $endfor\ VariantUnionNTD Remaining; VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { } VariantUnionNTD() = default; VTK_M_DEVICE ~VariantUnionNTD() { } }; template struct VariantUnionFinder; template struct VariantUnionFinder { using type = VariantUnionTD; }; template struct VariantUnionFinder { using type = VariantUnionNTD; }; template using VariantUnion = typename VariantUnionFinder::value, Ts...>::type; // -------------------------------------------------------------------------------- // Methods to get values out of the variant union template struct VariantUnionGetImpl; $for(param_index in range(max_expanded))\ template struct VariantUnionGetImpl<$(param_index), UnionType> { using ReturnType = decltype(std::declval().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 struct VariantUnionGetImpl { VTKM_STATIC_ASSERT(I >= $(max_expanded)); using RecursiveGet = VariantUnionGetImpl().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 VTK_M_DEVICE auto VariantUnionGet(UnionType& storage) noexcept -> decltype(VariantUnionGetImpl::type>::Get(storage))& { return VariantUnionGetImpl::type>::Get(storage); } // -------------------------------------------------------------------------------- // Internal implementation of CastAndCall for Variant $for(num_params in range(max_expanded))\ template VTK_M_DEVICE inline auto VariantCastAndCallImpl( std::integral_constant, vtkm::IdComponent index, Functor&& f, UnionType& storage, Args&&... args) noexcept(noexcept(f(storage.V0, args...))) -> decltype(f(storage.V0, args...)) { switch (index) { $for(param_index in range(num_params + 1))\ case $(param_index): return f(storage.V$(param_index), std::forward(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::F(); } } $endfor\ //clang-format on // Recurse for cases where Variant has more than $(max_expanded) types template VTK_M_DEVICE inline auto VariantCastAndCallImpl( std::integral_constant, vtkm::IdComponent index, Functor&& f, UnionType& storage, Args&&... args) noexcept(noexcept(f(storage.V0, args...))) -> decltype(f(storage.V0, args...)) { switch (index) { $for(param_index in range(max_expanded))\ case $(param_index): return f(storage.V$(param_index), std::forward(args)...); $endfor\ default: return VariantCastAndCallImpl(std::integral_constant{}, index - $(max_expanded), std::forward(f), storage.Remaining, std::forward(args)...); } } } } } } // vtkm::VTK_M_NAMESPACE::internal::detail