DynamicArrayHandle.h 11.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
//============================================================================
//  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 Sandia Corporation.
//  Copyright 2014 UT-Battelle, LLC.
11
//  Copyright 2014 Los Alamos National Security.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
//
//  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
//  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.
//============================================================================
#ifndef vtk_m_cont_DynamicArrayHandle_h
#define vtk_m_cont_DynamicArrayHandle_h

#include <vtkm/TypeListTag.h>

#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ErrorControlBadValue.h>
27
#include <vtkm/cont/StorageListTag.h>
28

29
#include <vtkm/cont/internal/DynamicTransform.h>
30 31 32
#include <vtkm/cont/internal/SimplePolymorphicContainer.h>

#include <boost/smart_ptr/shared_ptr.hpp>
33
#include <boost/utility/enable_if.hpp>
34 35 36 37 38 39 40

namespace vtkm {
namespace cont {

namespace internal {

/// Behaves like (and is interchangable with) a DynamicArrayHandle. The
41
/// difference is that the list of types and list of storage to try when
42 43
/// calling CastAndCall is set to the class template arguments.
///
44
template<typename TypeList, typename StorageList>
45 46 47 48
class DynamicArrayHandleCast;

} // namespace internal

49 50 51 52 53 54 55 56 57 58 59
/// \brief Holds an array handle without having to specify template parameters.
///
/// \c DynamicArrayHandle holds an \c ArrayHandle object using runtime
/// polymorphism to manage different value types and storage rather than
/// compile-time templates. This adds a programming convienience 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
/// in VTK-m, \c DynamicArrayHandle contains a method named \c CastAndCall that
60 61
/// will determine the correct type from some known list of types and storage
/// objects. This mechanism is used internally by VTK-m's worklet invocation
62 63 64 65
/// mechanism to determine the type when running algorithms.
///
/// By default, \c DynamicArrayHandle will assume that the value type in the
/// array matches one of the types specified by \c VTKM_DEFAULT_TYPE_LIST_TAG
66 67 68
/// and the storage matches one of the tags specified by \c
/// VTKM_DEFAULT_STORAGE_LIST_TAG. These lists can be changed by using the \c
/// ResetTypeList and \c ResetStorageList methods, respectively. It is
69 70 71 72 73 74 75
/// 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
/// combinations grows exponentally when using multiple \c DynamicArrayHandle
/// objects.
///
76 77 78 79 80 81
class DynamicArrayHandle
{
public:
  VTKM_CONT_EXPORT
  DynamicArrayHandle() {  }

82
  template<typename Type, typename Storage>
83
  VTKM_CONT_EXPORT
84 85 86
  DynamicArrayHandle(const vtkm::cont::ArrayHandle<Type,Storage> &array)
    : ArrayStorage(new vtkm::cont::internal::SimplePolymorphicContainer<
                     vtkm::cont::ArrayHandle<Type,Storage> >(array))
87 88
  {  }

89
  template<typename TypeList, typename StorageList>
90 91
  VTKM_CONT_EXPORT
  DynamicArrayHandle(
92 93
      const internal::DynamicArrayHandleCast<TypeList,StorageList> &dynamicArray)
    : ArrayStorage(dynamicArray.ArrayStorage) {  }
94 95

  /// Returns true if this array is of the provided type and uses the provided
96
  /// storage.
97
  ///
98
  template<typename Type, typename Storage>
99
  VTKM_CONT_EXPORT
100 101
  bool IsTypeAndStorage(Type = Type(), Storage = Storage()) const {
    return (this->TryCastStorage<Type,Storage>() != NULL);
102 103 104
  }

  /// Returns this array cast to an ArrayHandle object of the given type and
105 106
  /// storage. Throws ErrorControlBadValue if the cast does not work. Use
  /// IsTypeAndStorage to check if the cast can happen.
107
  ///
108
  template<typename Type, typename Storage>
109
  VTKM_CONT_EXPORT
110 111
  vtkm::cont::ArrayHandle<Type, Storage>
  CastToArrayHandle(Type = Type(), Storage = Storage()) const {
112
    vtkm::cont::internal::SimplePolymorphicContainer<
113 114
      vtkm::cont::ArrayHandle<Type,Storage> > *container =
        this->TryCastStorage<Type,Storage>();
115
    if (container == NULL)
116
    {
117
      throw vtkm::cont::ErrorControlBadValue("Bad cast of dynamic array.");
118
    }
119 120 121 122 123 124 125 126 127 128 129 130
    return container->Item;
  }

  /// Changes the types to try casting to when resolving this dynamic 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 dynamic array object. This method is particularly useful to
  /// narrow down (or expand) the types when using an array of particular
  /// constraints.
  ///
  template<typename NewTypeList>
  VTKM_CONT_EXPORT
131
  internal::DynamicArrayHandleCast<NewTypeList,VTKM_DEFAULT_STORAGE_LIST_TAG>
132
  ResetTypeList(NewTypeList = NewTypeList()) const {
133
    VTKM_IS_LIST_TAG(NewTypeList);
134
    return internal::DynamicArrayHandleCast<
135
        NewTypeList,VTKM_DEFAULT_STORAGE_LIST_TAG>(*this);
136 137
  }

138
  /// Changes the array storage types to try casting to when resolving this
139
  /// dynamic array, which is specified with a list tag like those in
140
  /// StorageListTag.h. Since C++ does not allow you to actually change the
141 142 143 144
  /// template arguments, this method returns a new dynamic array object. This
  /// method is particularly useful to narrow down (or expand) the types when
  /// using an array of particular constraints.
  ///
145
  template<typename NewStorageList>
146
  VTKM_CONT_EXPORT
147 148
  internal::DynamicArrayHandleCast<VTKM_DEFAULT_TYPE_LIST_TAG,NewStorageList>
  ResetStorageList(NewStorageList = NewStorageList()) const {
149
    VTKM_IS_LIST_TAG(NewStorageList);
150
    return internal::DynamicArrayHandleCast<
151
        VTKM_DEFAULT_TYPE_LIST_TAG,NewStorageList>(*this);
152 153
  }

154 155
  /// Attempts to cast the held array to a specific value type and storage,
  /// then call the given functor with the cast array. The types and storage
156
  /// tried in the cast are those in the lists defined by
157
  /// VTKM_DEFAULT_TYPE_LIST_TAG and VTK_DEFAULT_STORAGE_LIST_TAG,
158
  /// respectively, unless they have been changed with a previous call to
159
  /// ResetTypeList or ResetStorageList.
160 161 162 163 164 165
  ///
  template<typename Functor>
  VTKM_CONT_EXPORT
  void CastAndCall(const Functor &f) const
  {
    this->CastAndCall(
166
          f, VTKM_DEFAULT_TYPE_LIST_TAG(), VTKM_DEFAULT_STORAGE_LIST_TAG());
167 168 169
  }

  /// A version of CastAndCall that tries specified lists of types and
170
  /// storage types.
171
  ///
172
  template<typename Functor, typename TypeList, typename StorageList>
173
  VTKM_CONT_EXPORT
174
  void CastAndCall(const Functor &f, TypeList, StorageList) const;
175 176 177

private:
  boost::shared_ptr<vtkm::cont::internal::SimplePolymorphicContainerBase>
178
    ArrayStorage;
179

180
  template<typename Type, typename Storage>
181 182
  VTKM_CONT_EXPORT
  vtkm::cont::internal::SimplePolymorphicContainer<
183 184
    vtkm::cont::ArrayHandle<Type,Storage> > *
  TryCastStorage() const {
185 186 187
    return
        dynamic_cast<
          vtkm::cont::internal::SimplePolymorphicContainer<
188 189
            vtkm::cont::ArrayHandle<Type,Storage> > *>(
          this->ArrayStorage.get());
190 191 192 193 194 195
  }
};

namespace detail {

template<typename Functor, typename Type>
196
struct DynamicArrayHandleTryStorage {
197 198 199 200 201
  const DynamicArrayHandle Array;
  const Functor &Function;
  bool FoundCast;

  VTKM_CONT_EXPORT
202
  DynamicArrayHandleTryStorage(const DynamicArrayHandle &array,
203 204 205
                                 const Functor &f)
    : Array(array), Function(f), FoundCast(false) {  }

206
  template<typename Storage>
207
  VTKM_CONT_EXPORT
208
  typename boost::enable_if<
209
    typename vtkm::cont::internal::IsValidArrayHandle<Type,Storage>::type
210
    >::type
211
  operator()(Storage) {
212
    if (!this->FoundCast &&
213
        this->Array.IsTypeAndStorage(Type(), Storage()))
214
    {
215
      this->Function(this->Array.CastToArrayHandle(Type(), Storage()));
216 217 218
      this->FoundCast = true;
    }
  }
219

220
  template<typename Storage>
221 222
  VTKM_CONT_EXPORT
  typename boost::disable_if<
223
    typename vtkm::cont::internal::IsValidArrayHandle<Type,Storage>::type
224
    >::type
225
  operator()(Storage) {
226 227
    // This type of array handle cannot exist, so do nothing.
  }
228 229
};

230
template<typename Functor, typename StorageList>
231 232 233 234 235 236 237 238 239 240 241 242 243
struct DynamicArrayHandleTryType {
  const DynamicArrayHandle Array;
  const Functor &Function;
  bool FoundCast;

  VTKM_CONT_EXPORT
  DynamicArrayHandleTryType(const DynamicArrayHandle &array, const Functor &f)
    : Array(array), Function(f), FoundCast(false) {  }

  template<typename Type>
  VTKM_CONT_EXPORT
  void operator()(Type) {
    if (this->FoundCast) { return; }
244 245 246 247 248
    typedef DynamicArrayHandleTryStorage<Functor, Type> TryStorageType;
    TryStorageType tryStorage =
        TryStorageType(this->Array, this->Function);
    vtkm::ListForEach(tryStorage, StorageList());
    if (tryStorage.FoundCast)
249 250 251 252 253 254 255 256
    {
      this->FoundCast = true;
    }
  }
};

} // namespace detail

257
template<typename Functor, typename TypeList, typename StorageList>
258 259 260
VTKM_CONT_EXPORT
void DynamicArrayHandle::CastAndCall(const Functor &f,
                                     TypeList,
261
                                     StorageList) const
262
{
263 264
  VTKM_IS_LIST_TAG(TypeList);
  VTKM_IS_LIST_TAG(StorageList);
265
  typedef detail::DynamicArrayHandleTryType<Functor, StorageList> TryTypeType;
266 267 268 269 270
  TryTypeType tryType = TryTypeType(*this, f);
  vtkm::ListForEach(tryType, TypeList());
  if (!tryType.FoundCast)
  {
    throw vtkm::cont::ErrorControlBadValue(
271
          "Could not find appropriate cast for array in CastAndCall.");
272 273 274 275 276
  }
}

namespace internal {

277
template<typename TypeList, typename StorageList>
278 279
class DynamicArrayHandleCast : public vtkm::cont::DynamicArrayHandle
{
280 281 282
  VTKM_IS_LIST_TAG(TypeList);
  VTKM_IS_LIST_TAG(StorageList);

283
public:
284 285 286
  VTKM_CONT_EXPORT
  DynamicArrayHandleCast() : DynamicArrayHandle() {  }

287 288 289 290
  VTKM_CONT_EXPORT
  DynamicArrayHandleCast(const vtkm::cont::DynamicArrayHandle &array)
    : DynamicArrayHandle(array) {  }

291
  template<typename SrcTypeList, typename SrcStorageList>
292 293
  VTKM_CONT_EXPORT
  DynamicArrayHandleCast(
294
      const DynamicArrayHandleCast<SrcTypeList,SrcStorageList> &array)
295 296 297 298
    : DynamicArrayHandle(array) {  }

  template<typename NewTypeList>
  VTKM_CONT_EXPORT
299
  DynamicArrayHandleCast<NewTypeList,StorageList>
300
  ResetTypeList(NewTypeList = NewTypeList()) const {
301
    VTKM_IS_LIST_TAG(NewTypeList);
302
    return DynamicArrayHandleCast<NewTypeList,StorageList>(*this);
303 304
  }

305
  template<typename NewStorageList>
306
  VTKM_CONT_EXPORT
307 308
  internal::DynamicArrayHandleCast<TypeList,NewStorageList>
  ResetStorageList(NewStorageList = NewStorageList()) const {
309
    VTKM_IS_LIST_TAG(NewStorageList);
310
    return internal::DynamicArrayHandleCast<TypeList,NewStorageList>(*this);
311 312 313 314 315 316
  }

  template<typename Functor>
  VTKM_CONT_EXPORT
  void CastAndCall(const Functor &f) const
  {
317
    this->CastAndCall(f, TypeList(), StorageList());
318 319 320 321 322 323 324 325 326 327
  }

  template<typename Functor, typename TL, typename CL>
  VTKM_CONT_EXPORT
  void CastAndCall(const Functor &f, TL, CL) const
  {
    this->DynamicArrayHandle::CastAndCall(f, TL(), CL());
  }
};

328 329 330 331 332
template<>
struct DynamicTransformTraits<vtkm::cont::DynamicArrayHandle> {
  typedef vtkm::cont::internal::DynamicTransformTagCastAndCall DynamicTag;
};

333
template<typename TypeList, typename StorageList>
334
struct DynamicTransformTraits<
335
    vtkm::cont::internal::DynamicArrayHandleCast<TypeList,StorageList> >
336 337 338 339
{
  typedef vtkm::cont::internal::DynamicTransformTagCastAndCall DynamicTag;
};

340 341 342 343 344 345
} // namespace internal

}
} // namespace vtkm::cont

#endif //vtk_m_cont_DynamicArrayHandle_h