VariantArrayHandle.h 18.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
//============================================================================
//  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.
//
//  Copyright 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
//  Copyright 2014 UT-Battelle, LLC.
//  Copyright 2014 Los Alamos National Security.
//
//  Under the terms of Contract DE-NA0003525 with NTESS,
//  the U.S. Government retains certain rights in this software.
//
//  Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
//  Laboratory (LANL), the U.S. Government retains certain rights in
//  this software.
//============================================================================
20 21
#ifndef vtk_m_cont_VariantArrayHandle_h
#define vtk_m_cont_VariantArrayHandle_h
22 23 24 25 26 27 28

#include <vtkm/cont/vtkm_cont_export.h>

#include <vtkm/TypeListTag.h>
#include <vtkm/VecTraits.h>

#include <vtkm/cont/ArrayHandleVirtual.h>
29

30 31
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/cont/Logging.h>
32
#include <vtkm/cont/StorageListTag.h>
33 34
#include <vtkm/cont/internal/DynamicTransform.h>

35
#include <vtkm/cont/internal/VariantArrayHandleContainer.h>
36 37 38 39 40 41 42

namespace vtkm
{
namespace cont
{
/// \brief Holds an array handle without having to specify template parameters.
///
43
/// \c VariantArrayHandle holds an \c ArrayHandle or \c ArrayHandleVirtual
44 45 46 47 48 49 50
/// 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
51
/// in VTK-m, \c VariantArrayHandle contains a method named \c CastAndCall that
52 53 54 55 56
/// will determine the correct type from some known list of types. It returns
/// an ArrayHandleVirtual which type erases the storage type by using polymorphism.
/// This mechanism is used internally by VTK-m's worklet invocation
/// mechanism to determine the type when running algorithms.
///
57
/// By default, \c VariantArrayHandle will assume that the value type in the
58 59 60 61 62 63
/// array matches one of the types specified by \c VTKM_DEFAULT_TYPE_LIST_TAG
/// This list can be changed by using the \c ResetTypes. It is
/// 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
64
/// combinations grows exponentially when using multiple \c VariantArrayHandle
65 66
/// objects.
///
67 68
/// The actual implementation of \c VariantArrayHandle is in a templated class
/// named \c VariantArrayHandleBase, which is templated on the list of
69 70 71
/// component types.
///
template <typename TypeList>
72
class VTKM_ALWAYS_EXPORT VariantArrayHandleBase
73 74 75
{
public:
  VTKM_CONT
76
  VariantArrayHandleBase() = default;
77 78

  template <typename T, typename Storage>
79 80
  VTKM_CONT VariantArrayHandleBase(const vtkm::cont::ArrayHandle<T, Storage>& array)
    : ArrayContainer(std::make_shared<internal::VariantArrayHandleContainer<T>>(
81
        vtkm::cont::ArrayHandleVirtual<T>{ array }))
82 83 84 85
  {
  }

  template <typename T>
86
  explicit VTKM_CONT VariantArrayHandleBase(
87
    const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagVirtual>& array)
88
    : ArrayContainer(std::make_shared<internal::VariantArrayHandleContainer<T>>(array))
89 90 91 92
  {
  }

  template <typename OtherTypeList>
93
  VTKM_CONT explicit VariantArrayHandleBase(const VariantArrayHandleBase<OtherTypeList>& src)
94 95 96 97
    : ArrayContainer(internal::variant::GetContainer::Extract(src))
  {
  }

98 99
  VTKM_CONT VariantArrayHandleBase(const VariantArrayHandleBase& src) = default;
  VTKM_CONT VariantArrayHandleBase(VariantArrayHandleBase&& src) noexcept = default;
100 101

  VTKM_CONT
102
  ~VariantArrayHandleBase() {}
103 104

  VTKM_CONT
105
  VariantArrayHandleBase<TypeList>& operator=(const VariantArrayHandleBase<TypeList>& src) =
106 107 108
    default;

  VTKM_CONT
109
  VariantArrayHandleBase<TypeList>& operator=(VariantArrayHandleBase<TypeList>&& src) noexcept =
110 111 112 113 114 115
    default;


  /// Returns true if this array matches the array handle type passed in.
  ///
  template <typename ArrayHandleType>
116
  VTKM_CONT bool IsType() const
117 118 119 120 121 122 123
  {
    return internal::variant::IsType<ArrayHandleType>(this->ArrayContainer.get());
  }

  /// Returns true if this array matches the ValueType type passed in.
  ///
  template <typename T>
124
  VTKM_CONT bool IsValueType() const
125
  {
126
    return internal::variant::IsValueType<T>(this->ArrayContainer.get());
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
  }

  /// 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
  {
    return internal::variant::Cast<ArrayHandleType>(this->ArrayContainer.get());
  }

  /// Returns this array cast to a \c ArrayHandleVirtual of the given type. Throws \c
  /// ErrorBadType if the cast does not work.
  ///
  template <typename T>
  VTKM_CONT vtkm::cont::ArrayHandleVirtual<T> AsVirtual() const
  {
    return internal::variant::Cast<vtkm::cont::ArrayHandleVirtual<T>>(this->ArrayContainer.get());
  }

  /// Given a references to an ArrayHandle object, casts this array to the
  /// ArrayHandle's type and sets the given ArrayHandle to this array. Throws
  /// \c ErrorBadType if the cast does not work. Use \c
  /// ArrayHandleType to check if the cast can happen.
  ///
  /// 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>();
  }

  /// Changes the types to try casting to when resolving this variant array,
  /// which is specified with a list tag like those in TypeListTag.h. Since C++
  /// 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>
171
  VTKM_CONT VariantArrayHandleBase<NewTypeList> ResetTypes(NewTypeList = NewTypeList()) const
172 173
  {
    VTKM_IS_LIST_TAG(NewTypeList);
174
    return VariantArrayHandleBase<NewTypeList>(*this);
175 176
  }

177 178 179 180
  //@{
  /// \brief Call a functor using the underlying array type.
  ///
  /// \c CastAndCall Attempts to cast the held array to a specific value type,
181 182
  /// then call the given functor with the cast array. The types
  /// tried in the cast are those in the lists defined by the TypeList.
183
  /// By default \c VariantArrayHandle set this to \c VTKM_DEFAULT_TYPE_LIST_TAG.
184
  ///
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
  /// In addition to the value type, an \c ArrayHandle also requires a storage tag.
  /// By default, \c CastAndCall attempts to cast the array using the storage tags
  /// listed in \c VTKM_DEFAULT_STORAGE_LIST_TAG. You can optionally give a custom
  /// list of storage tags as the second argument. If the storage of the underlying
  /// array does not match any of the storage tags given, then the array will
  /// be cast to an \c ArrayHandleVirtual, which can hold any array given the
  /// appropriate value type. To always use \c ArrayHandleVirtual, pass
  /// \c vtkm::ListTagEmpty as thefirst argument.
  ///
  /// 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
  /// remaining arguments are passed from the arguments to \c CastAndCall.
  ///
  template <typename FunctorOrStorageList, typename... Args>
  VTKM_CONT void CastAndCall(FunctorOrStorageList&& functorOrStorageList, Args&&... args) const
  {
    this->CastAndCallImpl(vtkm::internal::ListTagCheck<FunctorOrStorageList>(),
                          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));
  }
  //@}
216 217 218 219 220 221 222 223

  /// \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
224
  VariantArrayHandleBase<TypeList> NewInstance() const
225
  {
226
    VariantArrayHandleBase<TypeList> instance;
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
    instance.ArrayContainer = this->ArrayContainer->NewInstance();
    return instance;
  }

  /// Releases any resources being used in the execution environment (that are
  /// not being shared by the control environment).
  ///
  void ReleaseResourcesExecution() { return this->ArrayContainer->ReleaseResourcesExecution(); }


  /// Releases all resources in both the control and execution environments.
  ///
  void ReleaseResources() { return this->ArrayContainer->ReleaseResources(); }

  /// \brief Get the number of components in each array value.
  ///
  /// This method will query the array type for the number of components in
  /// each value of the array. The number of components is determined by
  /// the \c VecTraits::NUM_COMPONENTS trait class.
  ///
  VTKM_CONT
  vtkm::IdComponent GetNumberOfComponents() const
  {
    return this->ArrayContainer->GetNumberOfComponents();
  }

  /// \brief Get the number of values in the array.
  ///
  VTKM_CONT
  vtkm::Id GetNumberOfValues() const { return this->ArrayContainer->GetNumberOfValues(); }

  VTKM_CONT
  void PrintSummary(std::ostream& out) const { this->ArrayContainer->PrintSummary(out); }

private:
  friend struct internal::variant::GetContainer;
263
  std::shared_ptr<vtkm::cont::internal::VariantArrayHandleContainerBase> ArrayContainer;
264 265 266 267 268 269 270 271 272 273 274 275

  template <typename Functor, typename... Args>
  VTKM_CONT void CastAndCallImpl(std::false_type, Functor&& f, Args&&... args) const
  {
    this->CastAndCallImpl(std::true_type(),
                          VTKM_DEFAULT_STORAGE_LIST_TAG(),
                          std::forward<Functor>(f),
                          std::forward<Args>(args)...);
  }

  template <typename StorageTagList, typename Functor, typename... Args>
  VTKM_CONT void CastAndCallImpl(std::true_type, StorageTagList, Functor&& f, Args&&...) const;
276 277
};

278
using VariantArrayHandle = vtkm::cont::VariantArrayHandleBase<VTKM_DEFAULT_TYPE_LIST_TAG>;
279

280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297

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

template <typename ArrayHandleType, typename Ts>
VTKM_CONT inline bool IsType(const vtkm::cont::VariantArrayHandleBase<Ts>& variant)
{
  return variant.template IsType<ArrayHandleType>();
}

template <typename ArrayHandleType, typename Ts>
VTKM_CONT inline ArrayHandleType Cast(const vtkm::cont::VariantArrayHandleBase<Ts>& variant)
{
  return variant.template Cast<ArrayHandleType>();
}



298 299 300
namespace detail
{

301
struct VariantArrayHandleTry
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
{
  template <typename T, typename Storage, typename Functor, typename... Args>
  void operator()(brigand::list<T, Storage>,
                  Functor&& f,
                  bool& called,
                  const vtkm::cont::internal::VariantArrayHandleContainerBase& container,
                  Args&&... args) const
  {
    using DerivedArrayType = vtkm::cont::ArrayHandle<T, Storage>;
    if (!called && vtkm::cont::internal::variant::IsType<DerivedArrayType>(&container))
    {
      called = true;
      const auto* derivedContainer =
        static_cast<const vtkm::cont::internal::VariantArrayHandleContainer<T>*>(&container);
      DerivedArrayType derivedArray = derivedContainer->Array.template Cast<DerivedArrayType>();
      VTKM_LOG_CAST_SUCC(container, derivedArray);

      // If you get a compile error here, it means that you have called CastAndCall for a
      // vtkm::cont::VariantArrayHandle and the arguments of the functor do not match those
      // being passed. This is often because it is calling the functor with an ArrayHandle
      // type that was not expected. Either add overloads to the functor to accept all
      // possible array types or constrain the types tried for the CastAndCall. Note that
      // the functor will be called with an array of type vtkm::cont::ArrayHandle<T, S>.
      // Directly using a subclass of ArrayHandle (e.g. vtkm::cont::ArrayHandleConstant<T>)
      // might not work.
      f(derivedArray, std::forward<Args>(args)...);
    }
  }
};

struct VariantArrayHandleTryFallback
333 334 335 336 337
{
  template <typename T, typename Functor, typename... Args>
  void operator()(T,
                  Functor&& f,
                  bool& called,
338
                  const vtkm::cont::internal::VariantArrayHandleContainerBase& container,
339 340
                  Args&&... args) const
  {
341
    if (!called && vtkm::cont::internal::variant::IsValueType<T>(&container))
342 343 344
    {
      called = true;
      const auto* derived =
345
        static_cast<const vtkm::cont::internal::VariantArrayHandleContainer<T>*>(&container);
346
      VTKM_LOG_CAST_SUCC(container, derived);
347 348 349 350 351 352 353 354 355

      // If you get a compile error here, it means that you have called CastAndCall for a
      // vtkm::cont::VariantArrayHandle and the arguments of the functor do not match those
      // being passed. This is often because it is calling the functor with an ArrayHandle
      // type that was not expected. Either add overloads to the functor to accept all
      // possible array types or constrain the types tried for the CastAndCall. Note that
      // the functor will be called with an array of type vtkm::cont::ArrayHandle<T, S>.
      // Directly using a subclass of ArrayHandle (e.g. vtkm::cont::ArrayHandleConstant<T>)
      // might not work.
356 357 358 359 360
      f(derived->Array, std::forward<Args>(args)...);
    }
  }
};

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
template <typename T>
struct IsUndefinedStorage
{
};
template <typename T, typename U>
struct IsUndefinedStorage<brigand::list<T, U>> : vtkm::cont::internal::IsInValidArrayHandle<T, U>
{
};

template <typename TypeList, typename StorageList>
struct ListTagDynamicTypes : vtkm::detail::ListRoot
{
  using crossProduct = typename vtkm::ListCrossProduct<TypeList, StorageList>;
  // using list = typename crossProduct::list;
  using list = ::brigand::remove_if<typename crossProduct::list, IsUndefinedStorage<brigand::_1>>;
};


379
VTKM_CONT_EXPORT void ThrowCastAndCallException(
380
  const vtkm::cont::internal::VariantArrayHandleContainerBase&,
381
  const std::type_info&);
382

383 384 385 386 387
} // namespace detail



template <typename TypeList>
388 389 390 391 392
template <typename StorageTagList, typename Functor, typename... Args>
VTKM_CONT void VariantArrayHandleBase<TypeList>::CastAndCallImpl(std::true_type,
                                                                 StorageTagList,
                                                                 Functor&& f,
                                                                 Args&&... args) const
393
{
394 395
  using crossProduct = detail::ListTagDynamicTypes<TypeList, StorageTagList>;

396 397
  bool called = false;
  const auto& ref = *this->ArrayContainer;
398
  vtkm::ListForEach(detail::VariantArrayHandleTry{},
399
                    crossProduct{},
400 401 402 403 404
                    std::forward<Functor>(f),
                    called,
                    ref,
                    std::forward<Args>(args)...);
  if (!called)
405 406 407 408 409 410 411 412 413 414
  {
    // try to fall back to using ArrayHandleVirtual
    vtkm::ListForEach(detail::VariantArrayHandleTryFallback{},
                      TypeList{},
                      std::forward<Functor>(f),
                      called,
                      ref,
                      std::forward<Args>(args)...);
  }
  if (!called)
415 416 417 418 419 420 421 422 423 424 425
  {
    // throw an exception
    VTKM_LOG_CAST_FAIL(*this, TypeList);
    detail::ThrowCastAndCallException(ref, typeid(TypeList));
  }
}

namespace internal
{

template <typename TypeList>
426
struct DynamicTransformTraits<vtkm::cont::VariantArrayHandleBase<TypeList>>
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
{
  using DynamicTag = vtkm::cont::internal::DynamicTransformTagCastAndCall;
};

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

//=============================================================================
// Specializations of serialization related classes
namespace diy
{

namespace internal
{

443
struct VariantArrayHandleSerializeFunctor
444 445 446 447 448 449 450 451 452
{
  template <typename ArrayHandleType>
  void operator()(const ArrayHandleType& ah, BinaryBuffer& bb) const
  {
    diy::save(bb, vtkm::cont::TypeString<ArrayHandleType>::Get());
    diy::save(bb, ah);
  }
};

453
struct VariantArrayHandleDeserializeFunctor
454 455 456
{
  template <typename T, typename TypeList>
  void operator()(T,
457
                  vtkm::cont::VariantArrayHandleBase<TypeList>& dh,
458 459 460 461 462 463 464 465 466 467
                  const std::string& typeString,
                  bool& success,
                  BinaryBuffer& bb) const
  {
    using ArrayHandleType = vtkm::cont::ArrayHandleVirtual<T>;

    if (!success && (typeString == vtkm::cont::TypeString<ArrayHandleType>::Get()))
    {
      ArrayHandleType ah;
      diy::load(bb, ah);
468
      dh = vtkm::cont::VariantArrayHandleBase<TypeList>(ah);
469 470 471 472 473 474 475 476
      success = true;
    }
  }
};

} // internal

template <typename TypeList>
477
struct Serialization<vtkm::cont::VariantArrayHandleBase<TypeList>>
478 479
{
private:
480
  using Type = vtkm::cont::VariantArrayHandleBase<TypeList>;
481 482 483 484

public:
  static VTKM_CONT void save(BinaryBuffer& bb, const Type& obj)
  {
485
    obj.CastAndCall(vtkm::ListTagEmpty(), internal::VariantArrayHandleSerializeFunctor{}, bb);
486 487 488 489 490 491 492 493 494
  }

  static VTKM_CONT void load(BinaryBuffer& bb, Type& obj)
  {
    std::string typeString;
    diy::load(bb, typeString);

    bool success = false;
    vtkm::ListForEach(
495
      internal::VariantArrayHandleDeserializeFunctor{}, TypeList{}, obj, typeString, success, bb);
496 497 498 499

    if (!success)
    {
      throw vtkm::cont::ErrorBadType(
500
        "Error deserializing VariantArrayHandle. Message TypeString: " + typeString);
501 502 503 504 505 506 507
    }
  }
};

} // diy


508
#endif //vtk_m_virts_VariantArrayHandle_h