dune-common  2.7.0
variant.hh
Go to the documentation of this file.
1 // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi: set et ts=4 sw=2 sts=2:
3 #ifndef DUNE_COMMON_STD_VARIANT_HH
4 #define DUNE_COMMON_STD_VARIANT_HH
5 #ifdef DUNE_HAVE_CXX_VARIANT
6 #include <variant>
7 namespace Dune {
8 namespace Std {
9  using std::variant;
10  using std::visit;
11  using std::variant_size;
12  using std::variant_size_v;
13  using std::get;
14  using std::get_if;
16  using std::monostate;
17 }
18 }
19 #else
20 #include <tuple>
21 #include <memory>
24 #include <dune/common/typelist.hh>
26 
27 namespace Dune {
28 namespace Std {
29 namespace Impl {
30 
31  // indicator value if something's not yet (or not any longer) valid
32  constexpr const auto invalidIndex = std::numeric_limits<std::size_t>::max();
33 
34  /* helper constructs to find position of a type T in a pack Ts... */
35  template <typename T, typename... Ts>
36  struct index_in_pack;
37 
38  template <typename T, typename... Ts>
39  struct index_in_pack<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
40 
41  template <typename T, typename U, typename... Ts>
42  struct index_in_pack<T, U, Ts...> : std::integral_constant<std::size_t, 1 + index_in_pack<T, Ts...>::value> {};
43 
44  /* end helper constructs to find position of a type T in a pack Ts... */
45 
46  template<typename T>
47  struct TypeStorage {
48 
49  using Buffer = std::aligned_storage_t<sizeof(T), alignof(T)>;
50 
51  // We only allow construction of empty TypeStorage
52  // objects and no asignment. Actual construction
53  // and assignment of stored values is done using
54  // special methods.
55  TypeStorage() = default;
56 
57  TypeStorage(const TypeStorage&) = delete;
58  TypeStorage(TypeStorage&&) = delete;
59  TypeStorage& operator=(const TypeStorage&) = delete;
60  TypeStorage& operator=(TypeStorage&&) = delete;
61 
62  void construct(const T& t) {
63  ::new (&buffer_) T(t);
64  }
65 
66  void construct(T&& t) {
67  ::new (&buffer_) T(std::move(t));
68  }
69 
70  void assign(const T& t) {
71  this->get() = t;
72  }
73 
74  void assign(T&& t) {
75  this->get() = std::move(t);
76  }
77 
78  void destruct() {
79  reinterpret_cast<T*>(&buffer_)->~T();
80  }
81 
82  auto& get() {
83  return *(reinterpret_cast<T*>(&buffer_));
84  }
85 
86  const auto& get() const {
87  return *(reinterpret_cast<const T*>(&buffer_));
88  }
89 
90  private:
91  Buffer buffer_;
92  };
93 
94 
95 
96  // A variadic union type providing access by index
97  // of member.
98  template<typename... T>
99  union VariadicUnion;
100 
101  // This is the recursion closure dummy.
102  // It's methods should never be called.
103  template<>
104  union VariadicUnion<> {
105  template<class Ti>
106  void construct(Ti&&) { assert(false); }
107 
108  template<class Ti>
109  void assign(Ti&&) { assert(false); }
110 
111  void destruct(std::size_t) { assert(false); };
112 
113  };
114 
115  template<typename Head, typename... Tail>
116  union VariadicUnion<Head, Tail...>
117  {
118  // We only allow construction of empty VariadicUnion
119  // objects and no asignment. Actual construction
120  // and assignment of stored values is done using
121  // special methods.
122  constexpr VariadicUnion() = default;
123 
124  VariadicUnion(const VariadicUnion& other) = delete;
125  VariadicUnion(VariadicUnion&& other) = delete;
126  VariadicUnion& operator=(const VariadicUnion& other) = delete;
127  VariadicUnion& operator=(VariadicUnion&& other) = delete;
128 
129  // Construct stored object
130  void construct(const Head& obj) {
131  new (&head_) TypeStorage<Head>;
132  head_.construct(obj);
133  }
134 
135  void construct(Head&& obj) {
136  new (&head_) TypeStorage<Head>;
137  head_.construct(std::move(obj));
138  }
139 
140  template<class Ti,
141  std::enable_if_t<not std::is_same<std::decay_t<Ti>, Head>::value, int> = 0>
142  void construct(Ti&& obj) {
143  new (&tail_) VariadicUnion<Tail...>;
144  tail_.construct(std::forward<Ti>(obj));
145  }
146 
147  // Assign to stored object. This should
148  // only be called if it's clear, that
149  // the VariadicUnion already stores
150  // on object of the passed type.
151  void assign(const Head& obj){
152  head_.assign(obj);
153  }
154 
155  void assign(Head&& obj){
156  head_.assign(std::move(obj));
157  }
158 
159  template<class Ti,
160  std::enable_if_t<not std::is_same<std::decay_t<Ti>, Head>::value, int> = 0>
161  void assign(Ti&& obj){
162  tail_.assign(std::forward<Ti>(obj));
163  }
164 
165  // Destruct stored object. This should only
166  // be called with the appropriate index of
167  // the stored object.
168  void destruct(size_t indexToReset) {
169  if (indexToReset == 0) {
170  head_.destruct();
171  return;
172  }
173  else
174  tail_.destruct(indexToReset-1);
175  }
176 
177  // Access to stored object
178  auto& getByIndex(std::integral_constant<size_t, 0>) {
179  return head_.get();
180  }
181 
182  const auto& getByIndex(std::integral_constant<size_t, 0>) const {
183  return head_.get();
184  }
185 
186  template<size_t N>
187  auto& getByIndex(std::integral_constant<size_t, N>) {
188  return tail_.getByIndex(std::integral_constant<size_t, N-1>());
189  }
190 
191  template<size_t N>
192  const auto& getByIndex(std::integral_constant<size_t, N>) const {
193  return tail_.getByIndex(std::integral_constant<size_t, N-1>());
194  }
195 
196  constexpr size_t size() const {
197  return sizeof...(Tail)+1;
198  }
199 
200  private:
201  TypeStorage<Head> head_;
202  VariadicUnion<Tail...> tail_;
203  };
204 
205  template<typename...T>
206  struct variant_{
207 
208  // Compute index of Ti in T...
209  template<class Ti>
210  constexpr static auto typeIndex()
211  {
212  return index_in_pack<std::decay_t<Ti>, T...>::value;
213  }
214 
215  // Create static index range for iterating over T...
216  constexpr static auto indexRange()
217  {
219  }
220 
221  constexpr void destructIfValid()
222  {
223  if (index_ != invalidIndex)
224  unions_.destruct(index_);
225  index_ = invalidIndex;
226  }
227 
228  // All methods will only use the default constructor but
229  // no other constructors or assignment operators of VariadicUnion.
230  // The construction and assignment of stored values is done
231  // using special methods.
232 
233  // Default constructor.
234  // Default construct T_0 if possible, otherwise set to invalid state
235  constexpr variant_() :
236  index_(invalidIndex)
237  {
238  using T0 = TypeListEntry_t<0, TypeList<T...>>;
239  Dune::Hybrid::ifElse(std::is_default_constructible<T0>(),
240  [&](auto&& id) {
241  unions_.construct(id(T0{}));
242  index_ = 0;
243  });
244  }
245 
246  // Construct from some Ti
247  template<typename Ti,
248  disableCopyMove<variant_, Ti> = 0>
249  constexpr variant_(Ti&& obj) :
250  index_(typeIndex<Ti>())
251  {
252  unions_.construct(std::forward<Ti>(obj));
253  }
254 
255  // Copy constructor
256  variant_(const variant_& other) {
257  index_ = other.index_;
258  if (index_==invalidIndex)
259  return;
260  Dune::Hybrid::forEach(indexRange(), [&](auto i) {
261  if(i==index_)
262  unions_.construct(other.template get<i>());
263  });
264  }
265 
266  // Move constructor
267  variant_(variant_&& other) {
268  index_ = other.index_;
269  if (index_==invalidIndex)
270  return;
271  Dune::Hybrid::forEach(indexRange(), [&](auto i) {
272  if(i==index_)
273  unions_.construct(std::move(other.template get<i>()));
274  });
275  other.destructIfValid();
276  }
277 
278  // Copy assignment operator
279  variant_& operator=(const variant_& other) {
280  if(index_ == other.index_) {
281  if (index_ != invalidIndex)
282  Dune::Hybrid::forEach(indexRange(), [&](auto i) {
283  if(i==index_)
284  unions_.assign(other.template get<i>());
285  });
286  }
287  else {
288  destructIfValid();
289  index_ = other.index_;
290  if (index_ != invalidIndex)
291  Dune::Hybrid::forEach(indexRange(), [&](auto i) {
292  if(i==index_)
293  unions_.construct(other.template get<i>());
294  });
295  }
296  return *this;
297  }
298 
299  // Move assignment operator
300  variant_& operator=(variant_&& other) {
301  if(index_ == other.index_) {
302  if (index_ != invalidIndex)
303  Dune::Hybrid::forEach(indexRange(), [&](auto i) {
304  if(i==index_)
305  unions_.assign(std::move(other.template get<i>()));
306  });
307  }
308  else {
309  destructIfValid();
310  index_ = other.index_;
311  if (index_ != invalidIndex)
312  Dune::Hybrid::forEach(indexRange(), [&](auto i) {
313  if(i==index_)
314  unions_.construct(std::move(other.template get<i>()));
315  });
316  }
317  other.destructIfValid();
318  return *this;
319  }
320 
321  // Assignment from some Ti
322  template<typename Ti,
323  disableCopyMove<variant_, Ti> = 0>
324  constexpr variant_& operator=(Ti&& obj) {
325  constexpr auto newIndex = typeIndex<Ti>();
326  if (index_ == newIndex)
327  unions_.assign(std::forward<Ti>(obj));
328  else
329  {
330  destructIfValid();
331  index_ = newIndex;
332  unions_.construct(std::forward<Ti>(obj));
333  }
334  return *this;
335  }
336 
337  template<typename Tp>
338  auto& get() {
339  constexpr auto idx = typeIndex<Tp>();
340  if (index_ != idx)
341  DUNE_THROW(Dune::Exception, "Bad variant access.");
342 
343  return get<idx>();
344  }
345 
346  template<typename Tp>
347  const auto& get() const {
348  constexpr auto idx = typeIndex<Tp>();
349  if (index_ != idx)
350  DUNE_THROW(Dune::Exception, "Bad variant access.");
351 
352  return get<idx>();
353  }
354 
355  template<typename Tp>
356  Tp* get_if() {
357  if (not holds_alternative<Tp>())
358  return (Tp*) nullptr;
359  else
360  return &(get<Tp>());
361  }
362 
363  template<typename Tp>
364  const Tp* get_if() const {
365  if (not holds_alternative<Tp>())
366  return (Tp*) nullptr;
367  else
368  return &(get<Tp>());
369  }
370 
371  template<std::size_t N>
372  auto* get_if() {
373  using Tp = std::decay_t<decltype(get<N>())>;
374  if (not holds_alternative<N>())
375  return (Tp*) nullptr;
376  else
377  return &(get<Tp>());
378  }
379 
380  template<std::size_t N>
381  const auto* get_if() const {
382  using Tp = std::decay_t<decltype(get<N>())>;
383  if (not holds_alternative<N>())
384  return (Tp*) nullptr;
385  else
386  return &(get<Tp>());
387  }
388 
389  template<std::size_t N>
390  auto& get() {
391  if (index_ != N || index_ == invalidIndex)
392  DUNE_THROW(Dune::Exception, "Bad variant access.");
393  return unions_.template getByIndex(std::integral_constant<std::size_t, N>());
394  }
395  template<std::size_t N>
396  const auto& get() const {
397  if (index_ != N || index_ == invalidIndex)
398  DUNE_THROW(Dune::Exception, "Bad variant access.");
399  return unions_.template getByIndex(std::integral_constant<std::size_t, N>());
400  }
401 
402  constexpr std::size_t index() const noexcept {
403  return index_;
404  }
405 
406  constexpr auto size() const {
407  return sizeof...(T);
408  }
409 
410  ~variant_() {
411  destructIfValid();
412  }
413 
420  template<typename F>
421  decltype(auto) visit(F&& func) {
422  auto dummyElseBranch = [&]() -> decltype(auto) { return func(this->get<0>());};
423  auto indices = std::make_index_sequence<size_>{};
424  return Hybrid::switchCases(indices, index(), [&](auto staticIndex) -> decltype(auto) {
425  return func(this->template get<decltype(staticIndex)::value>());
426  }, dummyElseBranch);
427  }
428 
429  template<typename F>
430  decltype(auto) visit(F&& func) const {
431  auto dummyElseBranch = [&]() -> decltype(auto) { return func(this->get<0>());};
432  auto indices = std::make_index_sequence<size_>{};
433  return Hybrid::switchCases(indices, index(), [&](auto staticIndex) -> decltype(auto) {
434  return func(this->template get<decltype(staticIndex)::value>());
435  }, dummyElseBranch);
436  }
437 
439  template<typename Tp>
440  constexpr bool holds_alternative() const {
441  // I have no idea how this could be really constexpr, but for STL-conformity,
442  // I'll leave the modifier there.
443  return (typeIndex<Tp>() == index_);
444  }
445 
447  template<std::size_t N>
448  constexpr bool holds_alternative() const {
449  // I have no idea how this could be really constexpr, but for STL-conformity,
450  // I'll leave the modifier there.
451  return (N == index_);
452  }
453 
454  private:
455  VariadicUnion<T...> unions_;
456  std::size_t index_;
457  constexpr static auto size_ = sizeof...(T);
458  };
459 
460 } // end namespace Impl
461 
463  template<typename ...T>
464  using variant = Impl::variant_<T...>;
465 
466  template<size_t N, typename... T>
467  auto& get(variant<T...>& var) {
468  return var.template get<N>();
469  }
470 
471  template<size_t N, typename... T>
472  const auto& get(const variant<T...>& var) {
473  return var.template get<N>();
474  }
475 
476  template<typename F, typename... T>
477  decltype(auto) visit(F&& visitor, variant<T...>& var) {
478  return var.visit(std::forward<F>(visitor));
479  }
480 
481  template<typename F, typename... T>
482  decltype(auto) visit(F&& visitor, const variant<T...>& var) {
483  return var.visit(std::forward<F>(visitor));
484  }
485 
486  template<typename Tp, typename ...T>
487  auto& get(variant<T...>& var) {
488  return var.template get<Tp>();
489  }
490 
491  template<typename Tp, typename ...T>
492  const auto& get(const variant<T...>& var) {
493  return var.template get<Tp>();
494  }
495 
496  template<typename Tp, typename ...T>
497  const auto* get_if(const variant<T...>* var) {
498  if (var == nullptr)
499  return (const Tp*) nullptr;
500  return var->template get_if<Tp>();
501  }
502 
503  template<typename Tp, typename ...T>
504  auto* get_if(variant<T...>* var) {
505  if (var == nullptr)
506  return (Tp*) nullptr;
507  return var->template get_if<Tp>();
508  }
509 
510  template<size_t N, typename ...T>
511  const auto* get_if(const variant<T...>* var) {
512  using Tp = std::decay_t<decltype(var->template get<N>())>;
513  if (var == nullptr)
514  return (const Tp*) nullptr;
515  return var->template get_if<N>();
516  }
517 
518  template<size_t N, typename ...T>
519  auto* get_if(variant<T...>* var) {
520  using Tp = std::decay_t<decltype(var->template get<N>())>;
521  if (var == nullptr)
522  return (Tp*) nullptr;
523  return var->template get_if<N>();
524  }
525 
526  template<typename Tp, typename ...T>
527  constexpr bool holds_alternative(const variant<T...>& var) {
528  return var.template holds_alternative<Tp>();
529  }
530 
531  template <typename T>
532  struct variant_size {};
533 
534  template <typename... T>
535  struct variant_size<variant<T...>>
536  : std::integral_constant<std::size_t, sizeof...(T)> { };
537 
538  // this cannot be inline (as it is in the STL) as this would need C++17
539  template <typename T>
540  constexpr std::size_t variant_size_v = variant_size<T>::value;
541 
547  struct monostate {};
548 
549  constexpr bool operator<(monostate, monostate) noexcept { return false; }
550  constexpr bool operator>(monostate, monostate) noexcept { return false; }
551  constexpr bool operator<=(monostate, monostate) noexcept { return true; }
552  constexpr bool operator>=(monostate, monostate) noexcept { return true; }
553  constexpr bool operator==(monostate, monostate) noexcept { return true; }
554  constexpr bool operator!=(monostate, monostate) noexcept { return false; }
555 
556 
557 } // end namespace Std
558 } // end namespace Dune
559 #endif
560 #endif
Dune::TypeListEntry_t
typename TypeListElement< i, T >::type TypeListEntry_t
Shortcut for TypeListElement<i, T>::type;.
Definition: typelist.hh:171
exceptions.hh
A few common exception classes.
Dune::Std::holds_alternative
constexpr bool holds_alternative(const variant< T... > &var)
Definition: variant.hh:527
Dune::Std::get_if
auto * get_if(variant< T... > *var)
Definition: variant.hh:504
Dune::Std::operator<=
constexpr bool operator<=(monostate, monostate) noexcept
Definition: variant.hh:551
Dune::AlignedNumberImpl::max
auto max(const AlignedNumber< T, align > &a, const AlignedNumber< T, align > &b)
Definition: debugalign.hh:412
Dune::Std::operator!=
constexpr bool operator!=(monostate, monostate) noexcept
Definition: variant.hh:554
Dune::Std::variant_size_v
constexpr std::size_t variant_size_v
Definition: variant.hh:540
Dune::assign
void assign(T &dst, const T &src, bool mask)
masked Simd assignment (scalar version)
Definition: simd.hh:445
typelist.hh
Dune::Std::operator>=
constexpr bool operator>=(monostate, monostate) noexcept
Definition: variant.hh:552
Dune::range
static StaticIntegralRange< T, to, from > range(std::integral_constant< T, from >, std::integral_constant< T, to >) noexcept
Definition: rangeutilities.hh:297
Dune::Hybrid::ifElse
decltype(auto) ifElse(const Condition &condition, IfFunc &&ifFunc, ElseFunc &&elseFunc)
A conditional expression.
Definition: hybridutilities.hh:355
Dune::Std::operator>
constexpr bool operator>(monostate, monostate) noexcept
Definition: variant.hh:550
Dune::Std::visit
decltype(auto) visit(F &&visitor, variant< T... > &var)
Definition: variant.hh:477
Dune::Hybrid::forEach
constexpr void forEach(Range &&range, F &&f)
Range based for loop.
Definition: hybridutilities.hh:267
Dune::Hybrid::switchCases
constexpr decltype(auto) switchCases(const Cases &cases, const Value &value, Branches &&branches, ElseBranch &&elseBranch)
Switch statement.
Definition: hybridutilities.hh:460
Dune::Std::get
auto & get(variant< T... > &var)
Definition: variant.hh:467
Dune::index_constant
std::integral_constant< std::size_t, i > index_constant
An index constant with value i.
Definition: indices.hh:28
Dune::Std::operator==
static constexpr bool operator==(const optional< T > &lhs, const optional< T > &rhs)
Definition: optional.hh:383
Dune::Std::get_if
const auto * get_if(const variant< T... > *var)
Definition: variant.hh:497
Dune::Std::visit
decltype(auto) visit(F &&visitor, const variant< T... > &var)
Definition: variant.hh:482
Dune::Std::variant
Impl::variant_< T... > variant
Incomplete re-implementation of C++17's std::variant.
Definition: variant.hh:464
Dune::Std::monostate
Trial default constructible class.
Definition: variant.hh:547
Dune::Std::operator<
static constexpr bool operator<(const optional< T > &lhs, const optional< T > &rhs)
Definition: optional.hh:390
Dune::Std::get
const auto & get(const variant< T... > &var)
Definition: variant.hh:472
Dune::Exception
Base class for Dune-Exceptions.
Definition: exceptions.hh:92
DUNE_THROW
#define DUNE_THROW(E, m)
Definition: exceptions.hh:216
rangeutilities.hh
Utilities for reduction like operations on ranges.
Dune::TypeList
std::tuple< MetaType< T >... > TypeList
A simple type list.
Definition: typelist.hh:87
Dune::Std::variant_size
Definition: variant.hh:532
Dune
Dune namespace.
Definition: alignedallocator.hh:13
hybridutilities.hh