e3dfa48910
The `Variant` class has separate implementations for its move and copy constructors/assignment operators depending on whether the classes it holds can be trivially moved. If the objects are trivial, Variant is trivial as well. However, in the case where the objects are not trivial, special construction and copying needs to be done. Previously, the non-trivial `Variant` defined a move constructor that did a byte copy of the contained object and reset the right hand side object so that it did not attempt to destroy the object. That usually works because it guarantees that only one version of the `Variant` will attempt to destroy the object and its resources should be cleaned up correctly. But C++ is a funny language that lets you do weird things. Turns out there are cases where moving the location of memory for an object without calling the proper copy method can invalidate the object. For example, if the object holds a pointer to one of its own members, that pointer will become invalid. Also, if it points to something that points back, then the object will need to update those pointers when it is moved. GCC's version of `std::string` seems to be a type like this. Solve the problem by simply deleting the move constructors. The copy constructors and destructor will be called instead to properly manage the object. A test for these conditions is added to `UnitTestVariant`.
208 lines
6.0 KiB
C
208 lines
6.0 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.
|
|
//============================================================================
|
|
$# 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 <vtkm/Types.h>
|
|
|
|
#include <vtkm/internal/brigand.hpp>
|
|
|
|
#include <type_traits>
|
|
|
|
$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
|
|
)\
|
|
$#
|
|
$extend(type_list)\
|
|
|
|
namespace vtkm
|
|
{
|
|
namespace VTK_M_NAMESPACE
|
|
{
|
|
namespace internal
|
|
{
|
|
namespace detail
|
|
{
|
|
|
|
template <typename ReturnType>
|
|
struct VariantDummyReturn
|
|
{
|
|
VTK_M_DEVICE static inline ReturnType F() noexcept { return ReturnType{}; }
|
|
};
|
|
template <>
|
|
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
|
|
{
|
|
// 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();
|
|
}
|
|
|
|
// clang-format off
|
|
|
|
$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)>,
|
|
vtkm::IdComponent index,
|
|
Functor&& f,
|
|
const void* storage,
|
|
Args&&... args) noexcept(noexcept(f(std::declval<const T0&>(), args...)))
|
|
{
|
|
switch (index)
|
|
{
|
|
$for(param_index in range(0, num_params + 1))\
|
|
case $(param_index):
|
|
return f(*reinterpret_cast<const 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();
|
|
}
|
|
}
|
|
|
|
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)>,
|
|
vtkm::IdComponent index,
|
|
Functor&& f,
|
|
void* storage,
|
|
Args&&... args) noexcept(noexcept(f(std::declval<const T0&>(), 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...);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
} // vtkm::VTK_M_NAMESPACE::internal::detail
|