VariantArrayHandle.h 17.8 KB
Newer Older
1 2 3 4
//============================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
5
//
6 7 8 9
//  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.
//============================================================================
10 11
#ifndef vtk_m_cont_VariantArrayHandle_h
#define vtk_m_cont_VariantArrayHandle_h
12 13 14

#include <vtkm/cont/vtkm_cont_export.h>

15
#include <vtkm/TypeList.h>
16 17
#include <vtkm/VecTraits.h>

18 19
#include <vtkm/cont/ArrayHandleMultiplexer.h>
#include <vtkm/cont/ArrayHandleTransform.h>
20
#include <vtkm/cont/CastAndCall.h>
21
#include <vtkm/cont/DefaultTypes.h>
22 23
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/cont/Logging.h>
24
#include <vtkm/cont/StorageList.h>
25 26
#include <vtkm/cont/UncertainArrayHandle.h>
#include <vtkm/cont/UnknownArrayHandle.h>
27

28 29 30 31
#ifndef VTKM_NO_DEPRECATED_VIRTUAL
#include <vtkm/cont/ArrayHandleVirtual.h>
#endif //VTKM_NO_DEPRECATED_VIRTUAL

32 33 34 35
namespace vtkm
{
namespace cont
{
36

37
#ifndef VTKM_NO_DEPRECATED_VIRTUAL
38 39 40 41 42
namespace internal
{
namespace variant
{

43
VTKM_DEPRECATED_SUPPRESS_BEGIN
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
struct ForceCastToVirtual
{
  template <typename SrcValueType, typename Storage, typename DstValueType>
  VTKM_CONT typename std::enable_if<std::is_same<SrcValueType, DstValueType>::value>::type
  operator()(const vtkm::cont::ArrayHandle<SrcValueType, Storage>& input,
             vtkm::cont::ArrayHandleVirtual<DstValueType>& output) const
  { // ValueTypes match
    output = vtkm::cont::make_ArrayHandleVirtual<DstValueType>(input);
  }

  template <typename SrcValueType, typename Storage, typename DstValueType>
  VTKM_CONT typename std::enable_if<!std::is_same<SrcValueType, DstValueType>::value>::type
  operator()(const vtkm::cont::ArrayHandle<SrcValueType, Storage>& input,
             vtkm::cont::ArrayHandleVirtual<DstValueType>& output) const
  { // ValueTypes do not match
    this->ValidateWidthAndCast<SrcValueType, DstValueType>(input, output);
  }

private:
  template <typename S,
            typename D,
            typename InputType,
            vtkm::IdComponent SSize = vtkm::VecTraits<S>::NUM_COMPONENTS,
            vtkm::IdComponent DSize = vtkm::VecTraits<D>::NUM_COMPONENTS>
  VTKM_CONT typename std::enable_if<SSize == DSize>::type ValidateWidthAndCast(
    const InputType& input,
    vtkm::cont::ArrayHandleVirtual<D>& output) const
  { // number of components match
    auto casted = vtkm::cont::make_ArrayHandleCast<D>(input);
    output = vtkm::cont::make_ArrayHandleVirtual<D>(casted);
  }

  template <typename S,
            typename D,
            vtkm::IdComponent SSize = vtkm::VecTraits<S>::NUM_COMPONENTS,
            vtkm::IdComponent DSize = vtkm::VecTraits<D>::NUM_COMPONENTS>
  VTKM_CONT typename std::enable_if<SSize != DSize>::type ValidateWidthAndCast(
    const ArrayHandleBase&,
    ArrayHandleBase&) const
  { // number of components do not match
    std::ostringstream str;
    str << "VariantArrayHandle::AsVirtual: Cannot cast from " << vtkm::cont::TypeToString<S>()
        << " to " << vtkm::cont::TypeToString<D>()
        << "; "
           "number of components must match exactly.";
    throw vtkm::cont::ErrorBadType(str.str());
  }
};
92
VTKM_DEPRECATED_SUPPRESS_END
93 94 95

}
} // namespace internal::variant
96
#endif //VTKM_NO_DEPRECATED_VIRTUAL
97

98
/// \brief VariantArrayHandle superclass holding common operations.
99
///
100 101 102 103
/// `VariantArrayHandleCommon` is a superclass to all `VariantArrayHandleBase`
/// and `VariantArrayHandle` classes. It contains all operations that are
/// independent of the type lists defined for these templated class or has
/// versions of methods that allow you to specify type lists.
104
///
105
/// See the documentation of `VariantArrayHandleBase` for more information.
106
///
107
class VTKM_ALWAYS_EXPORT VariantArrayHandleCommon : public vtkm::cont::UnknownArrayHandle
108
{
109
  using Superclass = vtkm::cont::UnknownArrayHandle;
110

111
public:
112
  using Superclass::Superclass;
113

114
  VTKM_CONT VariantArrayHandleCommon() = default;
115

116 117
  VTKM_CONT VariantArrayHandleCommon(const vtkm::cont::UnknownArrayHandle& array)
    : Superclass(array)
118 119 120
  {
  }

121 122 123 124 125 126 127 128
  // MSVC will issue deprecation warnings here if this template is instantiated with
  // a deprecated class even if the template is used from a section of code where
  // deprecation warnings are suppressed. This is annoying behavior since this template
  // has no control over what class it is used with. To get around it, we have to
  // suppress all deprecation warnings here.
#ifdef VTKM_MSVC
  VTKM_DEPRECATED_SUPPRESS_BEGIN
#endif
129 130 131 132 133 134 135
  /// Returns this array cast to the given \c ArrayHandle type. Throws \c
  /// ErrorBadType if the cast does not work. Use \c IsType
  /// to check if the cast can happen.
  ///
  template <typename ArrayHandleType>
  VTKM_CONT ArrayHandleType Cast() const
  {
136
    return this->AsArrayHandle<ArrayHandleType>();
137
  }
138 139 140
#ifdef VTKM_MSVC
  VTKM_DEPRECATED_SUPPRESS_END
#endif
141

142 143 144 145 146 147 148 149
  /// \brief Call a functor using the underlying array type.
  ///
  /// `CastAndCall` attempts to cast the held array to a specific value type,
  /// and then calls the given functor with the cast array. You must specify
  /// the `TypeList` and `StorageList` as template arguments. (Note that this
  /// calling differs from that of the `CastAndCall` methods of subclasses.)
  ///
  template <typename TypeList, typename StorageList, typename Functor, typename... Args>
150 151 152 153 154
  VTKM_CONT void CastAndCall(Functor&& functor, Args&&... args) const
  {
    this->CastAndCallForTypes<TypeList, StorageList>(std::forward<Functor>(functor),
                                                     std::forward<Args>(args)...);
  }
155

156
#ifndef VTKM_NO_DEPRECATED_VIRTUAL
157
  /// Returns this array cast to a `ArrayHandleVirtual` of the given type.
158 159
  /// This will perform type conversions as necessary, and will log warnings
  /// if the conversion is lossy.
160
  ///
161
  /// This method internally uses `CastAndCall`. A custom storage tag list may
162
  /// be specified in the second template parameter, which will be passed to
163 164
  /// the CastAndCall. You can also specify a list of types to try as the optional
  /// third template argument.
165
  ///
166
  VTKM_DEPRECATED_SUPPRESS_BEGIN
167 168 169
  template <typename T,
            typename StorageList = VTKM_DEFAULT_STORAGE_LIST,
            typename TypeList = vtkm::List<T>>
170 171
  VTKM_CONT VTKM_DEPRECATED(1.6, "ArrayHandleVirtual is no longer supported.")
    vtkm::cont::ArrayHandleVirtual<T> AsVirtual() const
172
  {
173 174
    VTKM_IS_LIST(StorageList);
    VTKM_IS_LIST(TypeList);
175 176
    vtkm::cont::internal::variant::ForceCastToVirtual caster;
    vtkm::cont::ArrayHandleVirtual<T> output;
177
    this->CastAndCall<TypeList, StorageList>(caster, output);
178
    return output;
179
  }
180
  VTKM_DEPRECATED_SUPPRESS_END
181
#endif //VTKM_NO_DEPRECATED_VIRTUAL
182

183
  /// Returns this array cast to a `ArrayHandleMultiplexer` of the given type.
184 185 186 187
  /// This will attempt to cast the internal array to each supported type of
  /// the multiplexer. If none are supported, an invalid ArrayHandleMultiplexer
  /// is returned.
  ///
188 189
  /// As a special case, if one of the arrays in the `ArrayHandleMultiplexer`'s
  /// type list is an `ArrayHandleCast`, then the multiplexer will look for type
190 191 192 193
  /// type of array being cast rather than an actual cast array.
  ///
  ///@{
  template <typename... T>
194 195 196 197
  VTKM_CONT void AsMultiplexer(vtkm::cont::ArrayHandleMultiplexer<T...>& result) const
  {
    this->AsArrayHandle(result);
  }
198 199 200 201 202 203 204 205 206 207

  template <typename ArrayHandleMultiplexerType>
  VTKM_CONT ArrayHandleMultiplexerType AsMultiplexer() const
  {
    ArrayHandleMultiplexerType result;
    this->AsMultiplexer(result);
    return result;
  }
  ///@}

208 209
  /// Given a references to an ArrayHandle object, casts this array to the
  /// ArrayHandle's type and sets the given ArrayHandle to this array. Throws
210 211
  /// `ErrorBadType` if the cast does not work. Use `IsType` to check
  /// if the cast can happen.
212 213 214 215 216 217 218 219 220 221 222
  ///
  /// Note that this is a shallow copy. The data are not copied and a change
  /// in the data in one array will be reflected in the other.
  ///
  template <typename ArrayHandleType>
  VTKM_CONT void CopyTo(ArrayHandleType& array) const
  {
    VTKM_IS_ARRAY_HANDLE(ArrayHandleType);
    array = this->Cast<ArrayHandleType>();
  }

223 224 225 226 227 228 229 230
  /// \brief Create a new array of the same type as this array.
  ///
  /// This method creates a new array that is the same type as this one and
  /// returns a new variant array handle for it. This method is convenient when
  /// creating output arrays that should be the same type as some input array.
  ///
  VTKM_CONT VariantArrayHandleCommon NewInstance() const
  {
231
    return VariantArrayHandleCommon(this->Superclass::NewInstance());
232 233 234 235 236
  }
};

/// \brief Holds an array handle without having to specify template parameters.
///
237
/// `VariantArrayHandle` holds an `ArrayHandle`
238 239 240 241 242 243 244
/// object using runtime polymorphism to manage different value types and
/// storage rather than compile-time templates. This adds a programming
/// convenience that helps avoid a proliferation of templates. It also provides
/// the management necessary to interface VTK-m with data sources where types
/// will not be known until runtime.
///
/// To interface between the runtime polymorphism and the templated algorithms
245 246
/// in VTK-m, `VariantArrayHandle` contains a method named `CastAndCall` that
/// will determine the correct type from some known list of types.
247 248 249
/// This mechanism is used internally by VTK-m's worklet invocation
/// mechanism to determine the type when running algorithms.
///
250 251 252
/// By default, `VariantArrayHandle` will assume that the value type in the
/// array matches one of the types specified by `VTKM_DEFAULT_TYPE_LIST`
/// This list can be changed by using the `ResetTypes`. It is
253 254 255 256
/// worthwhile to match these lists closely to the possible types that might be
/// used. If a type is missing you will get a runtime error. If there are more
/// types than necessary, then the template mechanism will create a lot of
/// object code that is never used, and keep in mind that the number of
257
/// combinations grows exponentially when using multiple `VariantArrayHandle`
258 259
/// objects.
///
260 261
/// The actual implementation of `VariantArrayHandle` is in a templated class
/// named `VariantArrayHandleBase`, which is templated on the list of
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
/// component types.
///
template <typename TypeList>
class VTKM_ALWAYS_EXPORT VariantArrayHandleBase : public VariantArrayHandleCommon
{
  VTKM_STATIC_ASSERT_MSG((!std::is_same<TypeList, vtkm::ListUniversal>::value),
                         "Cannot use vtkm::ListUniversal with VariantArrayHandle.");

  using Superclass = VariantArrayHandleCommon;

public:
  VTKM_CONT
  VariantArrayHandleBase() = default;

  template <typename T, typename Storage>
  VTKM_CONT VariantArrayHandleBase(const vtkm::cont::ArrayHandle<T, Storage>& array)
    : Superclass(array)
  {
  }

  VTKM_CONT explicit VariantArrayHandleBase(const VariantArrayHandleCommon& src)
    : Superclass(src)
  {
  }

287 288 289 290 291 292 293 294 295 296 297 298
  VTKM_CONT explicit VariantArrayHandleBase(const vtkm::cont::UnknownArrayHandle& src)
    : Superclass(src)
  {
  }

  template <typename StorageList>
  VTKM_CONT VariantArrayHandleBase(
    const vtkm::cont::UncertainArrayHandle<TypeList, StorageList>& src)
    : Superclass(src)
  {
  }

299 300 301 302 303 304 305 306 307 308 309 310 311
  VTKM_CONT VariantArrayHandleBase(const VariantArrayHandleBase&) = default;
  VTKM_CONT VariantArrayHandleBase(VariantArrayHandleBase&&) noexcept = default;

  VTKM_CONT
  ~VariantArrayHandleBase() {}

  VTKM_CONT
  VariantArrayHandleBase<TypeList>& operator=(const VariantArrayHandleBase<TypeList>&) = default;

  VTKM_CONT
  VariantArrayHandleBase<TypeList>& operator=(VariantArrayHandleBase<TypeList>&&) noexcept =
    default;

312 313 314 315 316
  VTKM_CONT operator vtkm::cont::UncertainArrayHandle<TypeList, VTKM_DEFAULT_STORAGE_LIST>() const
  {
    return vtkm::cont::UncertainArrayHandle<TypeList, VTKM_DEFAULT_STORAGE_LIST>(*this);
  }

317

318
#ifndef VTKM_NO_DEPRECATED_VIRTUAL
319 320 321 322 323 324 325 326
  /// Returns this array cast to a \c ArrayHandleVirtual of the given type.
  /// This will perform type conversions as necessary, and will log warnings
  /// if the conversion is lossy.
  ///
  /// This method internally uses CastAndCall. A custom storage tag list may
  /// be specified in the second template parameter, which will be passed to
  /// the CastAndCall.
  ///
327
  VTKM_DEPRECATED_SUPPRESS_BEGIN
328
  template <typename T, typename StorageList = VTKM_DEFAULT_STORAGE_LIST>
329 330
  VTKM_CONT VTKM_DEPRECATED(1.6, "ArrayHandleVirtual is no longer suported.")
    vtkm::cont::ArrayHandleVirtual<T> AsVirtual() const
331 332 333
  {
    return this->Superclass::AsVirtual<T, StorageList, TypeList>();
  }
334
  VTKM_DEPRECATED_SUPPRESS_END
335
#endif //VTKM_NO_DEPRECATED_VIRTUAL
336

337
  /// Changes the types to try casting to when resolving this variant array,
338
  /// which is specified with a list tag like those in TypeList.h. Since C++
339 340 341 342 343 344
  /// does not allow you to actually change the template arguments, this method
  /// returns a new variant array object. This method is particularly useful to
  /// narrow down (or expand) the types when using an array of particular
  /// constraints.
  ///
  template <typename NewTypeList>
345
  VTKM_CONT VariantArrayHandleBase<NewTypeList> ResetTypes(NewTypeList = NewTypeList()) const
346
  {
347
    VTKM_IS_LIST(NewTypeList);
348
    return VariantArrayHandleBase<NewTypeList>(*this);
349 350
  }

351 352 353
  //@{
  /// \brief Call a functor using the underlying array type.
  ///
354
  /// `CastAndCall` attempts to cast the held array to a specific value type,
355 356
  /// then call the given functor with the cast array. The types
  /// tried in the cast are those in the lists defined by the TypeList.
357
  /// By default `VariantArrayHandle` set this to `VTKM_DEFAULT_TYPE_LIST`.
358
  ///
359 360 361 362
  /// In addition to the value type, an `ArrayHandle` also requires a storage tag.
  /// By default, `CastAndCall` attempts to cast the array using the storage tags
  /// listed in `VTKM_DEFAULT_STORAGE_LIST`. You can optionally give a custom
  /// list of storage tags as the second argument.
363 364 365 366 367 368 369
  ///
  /// As previous stated, if a storage tag list is provided, it is given in the
  /// first argument. The functor to call with the cast array is given as the next
  /// argument (or the first argument if a storage tag list is not provided).
  /// The remaning arguments, if any, are passed to the functor.
  ///
  /// The functor will be called with the cast array as its first argument. Any
370
  /// remaining arguments are passed from the arguments to `CastAndCall`.
371 372 373 374
  ///
  template <typename FunctorOrStorageList, typename... Args>
  VTKM_CONT void CastAndCall(FunctorOrStorageList&& functorOrStorageList, Args&&... args) const
  {
375
    this->CastAndCallImpl(vtkm::internal::IsList<FunctorOrStorageList>(),
376 377 378 379 380 381 382 383 384 385
                          std::forward<FunctorOrStorageList>(functorOrStorageList),
                          std::forward<Args>(args)...);
  }

  template <typename Functor>
  VTKM_CONT void CastAndCall(Functor&& f) const
  {
    this->CastAndCallImpl(std::false_type(), std::forward<Functor>(f));
  }
  //@}
386 387 388 389 390 391 392

  /// \brief Create a new array of the same type as this array.
  ///
  /// This method creates a new array that is the same type as this one and
  /// returns a new variant array handle for it. This method is convenient when
  /// creating output arrays that should be the same type as some input array.
  ///
393
  VTKM_CONT VariantArrayHandleBase<TypeList> NewInstance() const
394
  {
395
    return VariantArrayHandleBase<TypeList>(this->Superclass::NewInstance());
396 397 398
  }

private:
399 400 401
  template <typename Functor, typename... Args>
  VTKM_CONT void CastAndCallImpl(std::false_type, Functor&& f, Args&&... args) const
  {
402 403
    this->Superclass::CastAndCall<TypeList, VTKM_DEFAULT_STORAGE_LIST>(std::forward<Functor>(f),
                                                                       std::forward<Args>(args)...);
404 405
  }

406 407 408 409 410 411
  template <typename StorageList, typename Functor, typename... Args>
  VTKM_CONT void CastAndCallImpl(std::true_type, StorageList, Functor&& f, Args&&... args) const
  {
    this->Superclass::CastAndCall<TypeList, StorageList>(std::forward<Functor>(f),
                                                         std::forward<Args>(args)...);
  }
412 413
};

414
using VariantArrayHandle = vtkm::cont::VariantArrayHandleBase<VTKM_DEFAULT_TYPE_LIST>;
415

416 417 418 419

//=============================================================================
// Free function casting helpers

420 421
/// Returns true if \c variant matches the type of ArrayHandleType.
///
422 423 424 425 426 427
template <typename ArrayHandleType, typename Ts>
VTKM_CONT inline bool IsType(const vtkm::cont::VariantArrayHandleBase<Ts>& variant)
{
  return variant.template IsType<ArrayHandleType>();
}

428 429 430 431
/// Returns \c variant cast to the given \c ArrayHandle type. Throws \c
/// ErrorBadType if the cast does not work. Use \c IsType
/// to check if the cast can happen.
///
432 433 434 435 436 437
template <typename ArrayHandleType, typename Ts>
VTKM_CONT inline ArrayHandleType Cast(const vtkm::cont::VariantArrayHandleBase<Ts>& variant)
{
  return variant.template Cast<ArrayHandleType>();
}

438 439 440 441
namespace internal
{

template <typename TypeList>
442
struct DynamicTransformTraits<vtkm::cont::VariantArrayHandleBase<TypeList>>
443 444 445 446 447 448 449 450 451 452
{
  using DynamicTag = vtkm::cont::internal::DynamicTransformTagCastAndCall;
};

} // namespace internal
} // namespace cont
} // namespace vtkm

//=============================================================================
// Specializations of serialization related classes
453
/// @cond SERIALIZATION
454
namespace mangled_diy_namespace
455 456 457
{

template <typename TypeList>
458
struct Serialization<vtkm::cont::VariantArrayHandleBase<TypeList>>
459 460
{
private:
461
  using Type = vtkm::cont::VariantArrayHandleBase<TypeList>;
462
  using ImplObject = vtkm::cont::UncertainArrayHandle<TypeList, VTKM_DEFAULT_STORAGE_LIST>;
463 464 465 466

public:
  static VTKM_CONT void save(BinaryBuffer& bb, const Type& obj)
  {
467
    vtkmdiy::save(bb, ImplObject(obj));
468 469 470 471
  }

  static VTKM_CONT void load(BinaryBuffer& bb, Type& obj)
  {
472 473 474
    ImplObject implObj;
    vtkmdiy::load(bb, implObj);
    obj = implObj;
475 476 477 478
  }
};

} // diy
479
/// @endcond SERIALIZATION
480 481


482
#endif //vtk_m_virts_VariantArrayHandle_h