OSVR-Core
ComVariant.h
Go to the documentation of this file.
1 
11 // Copyright 2015 Sensics, Inc.
12 //
13 // Licensed under the Apache License, Version 2.0 (the "License");
14 // you may not use this file except in compliance with the License.
15 // You may obtain a copy of the License at
16 //
17 // http://www.apache.org/licenses/LICENSE-2.0
18 //
19 // Unless required by applicable law or agreed to in writing, software
20 // distributed under the License is distributed on an "AS IS" BASIS,
21 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 // See the License for the specific language governing permissions and
23 // limitations under the License.
24 
25 #ifndef INCLUDED_ComVariant_h_GUID_A0A043D9_A146_4693_D351_06F6A04ABADA
26 #define INCLUDED_ComVariant_h_GUID_A0A043D9_A146_4693_D351_06F6A04ABADA
27 
28 #ifdef _WIN32
29 
30 // Internal Includes
31 // - none
32 
33 // Library/third-party includes
34 #include <oaidl.h>
35 
36 // Standard includes
37 #include <cassert>
38 #include <memory>
39 #include <stdexcept>
40 #include <type_traits>
41 #include <utility>
42 
43 namespace comutils {
44 namespace variant {
45  namespace detail {
46 
48  template <typename T>
49  using is_variant_type =
50  std::integral_constant<bool,
51  std::is_same<T, VARIANT>::value ||
52  std::is_same<T, VARIANTARG>::value>;
53 
56  template <typename T, typename Result = void *>
57  using enable_for_variants_t =
58  typename std::enable_if<is_variant_type<T>::value, Result>::type;
59 
60  template <typename OutputType> struct VariantTypeTraits;
63 
64 #if 0 // I don't actually see any members in the union to hold these...
65 template <> struct VariantTypeTraits<const wchar_t *> {
66  static const auto vt = VT_LPWSTR;
67 };
68 template <> struct VariantTypeTraits<const char *> {
69  static const auto vt = VT_LPSTR;
70 };
71 #endif
72 
73  template <> struct VariantTypeTraits<BSTR> {
74  static const auto vt = VT_BSTR;
75  template <typename T> static BSTR get(T &v) { return v.bstrVal; }
76  };
77 
78  // std::wstring is the "C++-side" wrapper for a BSTR here.
79  template <>
80  struct VariantTypeTraits<std::wstring> : VariantTypeTraits<BSTR> {
81  template <typename T> static std::wstring get(T &v) {
82  auto bs = v.bstrVal;
83  return std::wstring(static_cast<const wchar_t *>(bs),
84  SysStringLen(bs));
85  }
86  };
87  template <typename Dest> struct VariantArrayTypeTraits {};
88  template <> struct VariantArrayTypeTraits<BSTR> {
89  static const auto vt = VT_BSTR;
90  };
91 
92  template <>
93  struct VariantArrayTypeTraits<std::wstring>
94  : VariantArrayTypeTraits<BSTR> {
95  static std::wstring get(SAFEARRAY &arr, std::size_t i) {
96  BSTR bs = nullptr;
97  auto idx = static_cast<long>(i);
98  std::wstring ret;
99  auto hr =
100  SafeArrayGetElement(&arr, &idx, static_cast<void *>(&bs));
101  if (SUCCEEDED(hr) && bs) {
102  ret = std::wstring(static_cast<const wchar_t *>(bs),
103  SysStringLen(bs));
104  SysFreeString(bs);
105  }
106  return ret;
107  }
108  };
109 
112  namespace DestructionPolicies {
114  struct SilentAndAssert {
115  static void handle(HRESULT hr) {
116  switch (hr) {
117  case S_OK:
118  break;
119  case DISP_E_ARRAYISLOCKED:
120  /*
121  OutputDebugStringA("VariantClear failed on variant "
122  "destruction: Variant "
123  "contains an array that is locked");
124  */
125  break;
126  case DISP_E_BADVARTYPE:
127  /*
128  OutputDebugStringA("VariantClear failed on variant "
129  "destruction: Variant is "
130  "not a valid type");
131  */
132  break;
133  case E_INVALIDARG:
134  assert(hr != E_INVALIDARG &&
135  // shouldn't happen.
136  "VariantClear failed on variant "
137  "destruction: invalid "
138  "argument.");
139  break;
140  }
141  }
142  };
143 
145 #ifdef _INC_WINDOWS
146  struct DebugStringAndAssert {
147  static void handle(HRESULT hr) {
148  switch (hr) {
149  case S_OK:
150  break;
151  case DISP_E_ARRAYISLOCKED:
152  OutputDebugStringA("VariantClear failed on variant "
153  "destruction: Variant "
154  "contains an array that is locked");
155  break;
156  case DISP_E_BADVARTYPE:
157  OutputDebugStringA("VariantClear failed on variant "
158  "destruction: Variant is "
159  "not a valid type");
160  break;
161  case E_INVALIDARG:
162  assert(hr != E_INVALIDARG &&
163  // shouldn't happen.
164  "VariantClear failed on variant "
165  "destruction: invalid "
166  "argument.");
167  break;
168  }
169  }
170  };
171 #endif
172 
174  struct ThrowAndAssert {
175  static void handle(HRESULT hr) {
176  switch (hr) {
177  case S_OK:
178  break;
179  case DISP_E_ARRAYISLOCKED:
180  throw std::runtime_error(
181  "VariantClear failed on variant "
182  "destruction: Variant "
183  "contains an array that is locked");
184  break;
185  case DISP_E_BADVARTYPE:
186  throw std::runtime_error(
187  "VariantClear failed on variant "
188  "destruction: Variant is "
189  "not a valid type");
190  break;
191  case E_INVALIDARG:
192  assert(hr != E_INVALIDARG &&
193  // shouldn't happen.
194  "VariantClear failed on variant "
195  "destruction: invalid "
196  "argument.");
197  break;
198  }
199  }
200  };
201 
202  using Default = SilentAndAssert;
203  } // namespace DestructionPolicies
204 
209  template <typename T = VARIANT,
210  typename DestructionPolicy = DestructionPolicies::Default>
211  struct VariantHolder {
212  public:
213  static_assert(
214  is_variant_type<T>::value,
215  "Only valid type parameters are VARIANT or VARIANTARG");
216  using type = VariantHolder<T>;
217  using unique_type = std::unique_ptr<type>;
218  static unique_type make_unique() {
219  unique_type ret(new type);
220  return ret;
221  }
222  VariantHolder() { VariantInit(getPtr()); }
223  ~VariantHolder() {
224  auto hr = VariantClear(getPtr());
225  DestructionPolicy::handle(hr);
226  }
228  VariantHolder(VariantHolder const &) = delete;
230  VariantHolder &operator=(VariantHolder const &) = delete;
231  T &get() { return data_; }
232  T const &get() const { return data_; }
233  T *getPtr() { return &data_; }
234  T const *getPtr() const { return &data_; }
235 
236  private:
237  T data_;
238  };
239 
242  template <typename Dest> struct VariantSafeArrayRange {
243  public:
244  explicit VariantSafeArrayRange(SAFEARRAY &arr) : arr_(arr) {
245  if (SafeArrayGetDim(&arr_) != numDims_) {
246  // assumes dimensions = 1;
247  throw std::runtime_error(
248  "Can't use this class on this array: "
249  "we assume a 1-dimensional array, "
250  "which this is not!");
251  }
252  if (!SUCCEEDED(SafeArrayGetUBound(&arr_, dim_, &uBound_))) {
253  throw std::runtime_error(
254  "Couldn't get 0th dimension upper bound.");
255  }
256 
257  if (!SUCCEEDED(SafeArrayGetLBound(&arr_, dim_, &lBound_))) {
258  throw std::runtime_error(
259  "Couldn't get 0th dimension lower bound.");
260  }
261  }
262  using type = VariantSafeArrayRange<Dest>;
263 
264  Dest get(std::size_t i) const {
265  return detail::VariantArrayTypeTraits<Dest>::get(arr_, i);
266  }
267  struct SafeArrayRangeIterator {
268  // default constructor: invalid/end iterator
269  SafeArrayRangeIterator() {}
270  // Construct with index.
271  SafeArrayRangeIterator(type const &range, long idx)
272  : elt_(idx), range_(&range) {
273  checkIndex();
274  }
275 
276  explicit operator bool() const { return range_ != nullptr; }
277 
278  bool operator==(SafeArrayRangeIterator const &other) const {
279  return (elt_ == other.elt_) && (range_ == other.range_);
280  }
281  bool operator!=(SafeArrayRangeIterator const &other) const {
282  return elt_ != other.elt_ || range_ != other.range_;
283  }
284 
285  Dest operator*() const { return range_->get(elt_); }
286 
287  SafeArrayRangeIterator &operator++() {
288  increment();
289  return *this;
290  }
291  SafeArrayRangeIterator &operator++(int) {
292  SafeArrayRangeIterator copy(*this);
293  increment();
294  return copy;
295  }
296 
297  private:
298  void increment() {
299  if (*this) {
300  ++elt_;
301  checkIndex();
302  }
303  }
304  void checkIndex() {
305  if (range_) {
306  if (!range_->inBounds(elt_)) {
307  range_ = nullptr;
308  }
309  }
310  if (!range_) {
311  elt_ = INVALID_ELEMENT;
312  }
313  }
314  static const long INVALID_ELEMENT = -1;
315  long elt_ = INVALID_ELEMENT;
316  type const *range_ = nullptr;
317  };
318  bool inBounds(long idx) const {
319  return idx >= lBound_ && idx <= uBound_;
320  }
321 
322  using iterator = SafeArrayRangeIterator;
323  using const_iterator = SafeArrayRangeIterator;
324  SafeArrayRangeIterator begin() const {
325  return SafeArrayRangeIterator(*this, lBound_);
326  }
327  SafeArrayRangeIterator end() const {
328  return SafeArrayRangeIterator();
329  }
330 
331  private:
332  SAFEARRAY &arr_;
333  const UINT numDims_ = 1;
334  const UINT dim_ = 1;
335  long uBound_ = 0;
336  long lBound_ = 0;
337  };
338  } // namespace detail
339 
343  template <typename T = VARIANT,
344  typename DestructionPolicy = detail::DestructionPolicies::Default>
345  class VariantWrapper {
346 
347  public:
348  static_assert(detail::is_variant_type<T>::value,
349  "Only valid type parameters are VARIANT or VARIANTARG");
350  using variant_type = T;
351  using destruction_policy = DestructionPolicy;
352  using type = VariantWrapper<T, DestructionPolicy>;
353  using holder_type = detail::VariantHolder<T, DestructionPolicy>;
354  using holder_unique_ptr = typename holder_type::unique_type;
355 
357  VariantWrapper() : data_(holder_type::make_unique()) {}
358 
360  explicit VariantWrapper(std::nullptr_t) {}
361 
364  VariantWrapper(VariantWrapper const &other) : VariantWrapper() {
365  if (!other) {
366  throw std::logic_error(
367  "On variant copy-construction: tried to "
368  "copy from a variant in an invalid state "
369  "(moved-from)!");
370  }
371  (*this) = other;
372  }
373 
376  VariantWrapper(VariantWrapper &&other)
377  : data_(std::move(other.data_)) {}
378 
381  type &operator=(type &&other) {
382  dealloc();
383  data_ = std::move(other.data_);
384  return *this;
385  }
386 
389  type &operator=(VariantWrapper const &other) {
390  if (!other) {
391  throw std::logic_error(
392  "On variant copy-assignment: tried to copy "
393  "from a variant in an invalid state "
394  "(moved-from)!");
395  }
396  ensureInit();
397  auto hr = VariantCopy(getPtr(), other.getPtr());
398  switch (hr) {
399  case S_OK:
400  break;
401  case DISP_E_ARRAYISLOCKED:
402  throw std::runtime_error(
403  "VariantCopy failed on variant "
404  "copy-construction/assignment: Variant "
405  "contains an array that is locked");
406  break;
407  case DISP_E_BADVARTYPE:
408  throw std::runtime_error(
409  "VariantCopy failed on variant "
410  "copy-construction/assignment: Variant is "
411  "not a valid type");
412  break;
413  case E_INVALIDARG:
414  throw std::invalid_argument(
415  "VariantCopy failed on variant "
416  "copy-construction/assignment: invalid "
417  "argument.");
418  break;
419  case E_OUTOFMEMORY:
420  throw std::runtime_error("VariantCopy failed on variant "
421  "copy-construction/assignment: "
422  "insufficient memory.");
423  break;
424  }
425  return *this;
426  }
427 
430  explicit operator bool() const { return bool(data_); }
431 
433  void ensureInit() {
434  if (!data_) {
435  data_ = holder_type::make_unique();
436  }
437  }
438 
441  void dealloc() { data_.reset(); }
442 
445  variant_type &get() { return data_->get(); }
446  variant_type const &get() const { return data_->get(); }
447  variant_type *getPtr() { return data_->getPtr(); }
448  variant_type const *getPtr() const { return data_->getPtr(); }
450 
451  private:
452  holder_unique_ptr data_;
453  };
454 
456  template <typename T> inline T *AttachVariant(VariantWrapper<T> &v) {
457  return v.getPtr();
458  }
459 
460  using Variant = VariantWrapper<VARIANT>;
461  using VariantArg = VariantWrapper<VARIANTARG>;
462 
465  template <typename Dest, typename T>
466  inline detail::enable_for_variants_t<T, bool> contains(T const &v) {
467  return (v.vt == detail::VariantTypeTraits<Dest>::vt);
468  }
471  template <typename Dest, typename T>
472  inline bool contains(VariantWrapper<T> const &v) {
473  return v && contains<Dest>(v.get());
474  }
475 
478  template <typename Dest, typename T>
479  inline detail::enable_for_variants_t<T, bool> containsArray(T const &v) {
480  return (v.vt == (detail::VariantTypeTraits<Dest>::vt | VT_ARRAY));
481  }
484  template <typename Dest, typename T>
485  inline bool containsArray(VariantWrapper<T> const &v) {
486  return v && containsArray<Dest>(v.get());
487  }
488 
490  template <typename T>
491  inline detail::enable_for_variants_t<T, bool> is_empty(T const &v) {
492  return (v.vt == VT_EMPTY);
493  }
494 
497  template <typename T> inline bool is_empty(VariantWrapper<T> const &v) {
498  return (!v) || is_empty(v.get());
499  }
500 
503  template <typename Dest, typename T>
504  inline detail::enable_for_variants_t<T, Dest> get(T const &v) {
505  if (!contains<Dest>(v)) {
506  throw std::invalid_argument(
507  "Variant does not contain the type of data "
508  "you are trying to access!");
509  }
510  return detail::VariantTypeTraits<Dest>::get(v);
511  }
512 
515  template <typename Dest, typename T>
516  inline Dest get(VariantWrapper<T> const &v) {
517  if (!contains<Dest>(v)) {
518  throw std::invalid_argument(
519  "Variant does not contain the type of data "
520  "you are trying to access!");
521  }
522  return get<Dest>(v.get());
523  }
526  template <typename Dest, typename T>
527  inline detail::enable_for_variants_t<T, Dest> getArray(T const &v,
528  std::size_t i) {
529  if (!containsArray<Dest>(v)) {
530  throw std::invalid_argument(
531  "Variant does not contain an array of the type of data "
532  "you are trying to access!");
533  }
534  SAFEARRAY *arr = V_ARRAY(&v);
535  return detail::VariantArrayTypeTraits<Dest>::get(*arr, i);
536  }
539  template <typename Dest, typename T>
540  inline Dest getArray(VariantWrapper<T> const &v, std::size_t i) {
541  if (!containsArray<Dest>(v)) {
542  throw std::invalid_argument(
543  "Variant does not contain an array of the type of data "
544  "you are trying to access!");
545  }
546  return getArray<Dest>(v.get());
547  }
548 
551  template <typename Dest, typename T>
552  inline detail::enable_for_variants_t<T, detail::VariantSafeArrayRange<Dest>>
553  getArray(T const &v) {
554  if (!containsArray<Dest>(v)) {
555  throw std::invalid_argument(
556  "Variant does not contain an array of the type of data "
557  "you are trying to access!");
558  }
559  SAFEARRAY *arr = V_ARRAY(&v);
560  return detail::VariantSafeArrayRange<Dest>(*arr);
561  }
564  template <typename Dest, typename T>
565  inline detail::VariantSafeArrayRange<Dest>
566  getArray(VariantWrapper<T> const &v) {
567  if (!containsArray<Dest>(v)) {
568  throw std::invalid_argument(
569  "Variant does not contain an array of the type of data "
570  "you are trying to access!");
571  }
572  return getArray<Dest>(v.get());
573  }
574 } // namespace variant
575 
576 using namespace variant;
577 
578 } // namespace comutils
579 
580 #endif // _WIN32
581 
582 #endif // INCLUDED_ComVariant_h_GUID_A0A043D9_A146_4693_D351_06F6A04ABADA
apply_list< quote< or_ >, transform< Haystack, detail::is_< Needle >>> contains
Determines if type Needle is in the list Haystack - is an alias for a type that inherits std::true_ty...
Definition: Contains.h:49
Definition: TypeSafeIdHash.h:44
const internal::permut_matrix_product_retval< PermutationDerived, Derived, OnTheRight > operator*(const MatrixBase< Derived > &matrix, const PermutationBase< PermutationDerived > &permutation)
Definition: PermutationMatrix.h:539
Definition: ComInit.cpp:34
Definition: newuoa.h:1888
A small structure to hold a non zero as a triplet (i,j,value).
Definition: SparseUtil.h:148