Dune Core Modules (2.7.0)

variant.hh
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>
7namespace Dune {
8namespace 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;
15 using std::holds_alternative;
16 using std::monostate;
17}
18}
19#else
20#include <tuple>
21#include <memory>
22#include <dune/common/hybridutilities.hh>
24#include <dune/common/typelist.hh>
26
27namespace Dune {
28namespace Std {
29namespace 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 {
218 return Dune::range(Dune::index_constant<size_>());
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
Base class for Dune-Exceptions.
Definition: exceptions.hh:94
A few common exception classes.
std::integral_constant< std::size_t, i > index_constant
An index constant with value i.
Definition: indices.hh:28
#define DUNE_THROW(E, m)
Definition: exceptions.hh:216
constexpr void forEach(Range &&range, F &&f)
Range based for loop.
Definition: hybridutilities.hh:267
constexpr decltype(auto) switchCases(const Cases &cases, const Value &value, Branches &&branches, ElseBranch &&elseBranch)
Switch statement.
Definition: hybridutilities.hh:460
decltype(auto) ifElse(const Condition &condition, IfFunc &&ifFunc, ElseFunc &&elseFunc)
A conditional expression.
Definition: hybridutilities.hh:355
EnableIfInterOperable< T1, T2, bool >::type operator<(const RandomAccessIteratorFacade< T1, V1, R1, D > &lhs, const RandomAccessIteratorFacade< T2, V2, R2, D > &rhs)
Comparison operator.
Definition: iteratorfacades.hh:635
EnableIfInterOperable< T1, T2, bool >::type operator>(const RandomAccessIteratorFacade< T1, V1, R1, D > &lhs, const RandomAccessIteratorFacade< T2, V2, R2, D > &rhs)
Comparison operator.
Definition: iteratorfacades.hh:681
EnableIfInterOperable< T1, T2, bool >::type operator==(const ForwardIteratorFacade< T1, V1, R1, D > &lhs, const ForwardIteratorFacade< T2, V2, R2, D > &rhs)
Checks for equality.
Definition: iteratorfacades.hh:235
EnableIfInterOperable< T1, T2, bool >::type operator>=(const RandomAccessIteratorFacade< T1, V1, R1, D > &lhs, const RandomAccessIteratorFacade< T2, V2, R2, D > &rhs)
Comparison operator.
Definition: iteratorfacades.hh:703
EnableIfInterOperable< T1, T2, bool >::type operator!=(const ForwardIteratorFacade< T1, V1, R1, D > &lhs, const ForwardIteratorFacade< T2, V2, R2, D > &rhs)
Checks for inequality.
Definition: iteratorfacades.hh:257
auto max(ADLTag< 0 >, const V &v1, const V &v2)
implements binary Simd::max()
Definition: defaults.hh:79
std::tuple< MetaType< T >... > TypeList
A simple type list.
Definition: typelist.hh:87
Impl::variant_< T... > variant
Incomplete re-implementation of C++17's std::variant.
Definition: variant.hh:464
Dune namespace.
Definition: alignedallocator.hh:14
void assign(T &dst, const T &src, bool mask)
masked Simd assignment (scalar version)
Definition: simd.hh:445
typename TypeListElement< i, T >::type TypeListEntry_t
Shortcut for TypeListElement<i, T>::type;.
Definition: typelist.hh:171
Utilities for reduction like operations on ranges.
Trial default constructible class.
Definition: variant.hh:547
Creative Commons License   |  Legal Statements / Impressum  |  Hosted by TU Dresden  |  generated with Hugo v0.111.3 (Jul 15, 22:36, 2024)