Lumiera 0.pre.04~rc.1
»edit your freedom«
Loading...
Searching...
No Matches
variant.hpp
Go to the documentation of this file.
1/*
2 VARIANT.hpp - lightweight typesafe union record
3
4 Copyright (C)
5 2015, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12*/
13
14
69#ifndef LIB_VARIANT_H
70#define LIB_VARIANT_H
71
72
73#include "lib/error.hpp"
74#include "lib/meta/typelist.hpp"
78#include "lib/format-obj.hpp"
79#include "lib/util.hpp"
80
81#include <type_traits>
82#include <cstddef>
83#include <utility>
84#include <string>
85
86
87namespace lib {
88
89 using std::string;
90
91 using std::move;
92 using std::forward;
93 using util::unConst;
94
95 namespace error = lumiera::error;
96
97
98 namespace variant { // implementation metaprogramming helpers
99
100 using std::remove_reference;
101 using meta::Types;
102 using meta::Node;
103 using meta::Nil;
104
105
106 template<typename X, typename TYPES>
108 : CanBuildFrom<typename remove_reference<X>::type
109 ,typename TYPES::List
110 >
111 { };
112
113 template<typename X, typename TYPES>
114 struct CanBuildFrom<X, Node<X, TYPES>>
115 : std::true_type
116 {
117 using Type = X;
118 };
119
120 template<typename X, typename TYPES>
121 struct CanBuildFrom<const X, Node<X, TYPES>>
122 : std::true_type
123 {
124 using Type = X;
125 };
126
127 template<typename TYPES, size_t len>
128 struct CanBuildFrom<const char [len], Node<string, TYPES>>
129 : std::true_type
130 {
131 using Type = string;
132 };
133
134 template<typename X, typename T,typename TYPES>
135 struct CanBuildFrom<X, Node<T, TYPES>>
136 : CanBuildFrom<X,TYPES>
137 { };
138
139 template<typename X>
140 struct CanBuildFrom<X, Nil>
141 : std::false_type
142 { };
143
144
145 template<typename T>
146 struct Identity { using Type = T; };
147
155 template<class TYPES, template<class> class _P_>
157 {
158 static_assert(not sizeof(TYPES), "None of the possible Types fulfils the condition");
159 };
160
161 template<class...TYPES, template<class> class _P_>
162 struct FirstMatchingType<Types<TYPES...>, _P_>
163 : FirstMatchingType<typename Types<TYPES...>::List, _P_>
164 { };
165
166 template<class T, class TYPES, template<class> class _P_>
167 struct FirstMatchingType<Node<T,TYPES>, _P_>
168 : std::conditional_t<_P_<T>::value, Identity<T>, FirstMatchingType<TYPES, _P_>>
169 { };
170
171
172
173 template<typename RET>
174 struct VFunc
175 {
177 template<class VAL>
179 {
180 virtual RET handle(VAL&) { /* do nothing */ return RET(); };
181 };
182
183
185 template<typename TYPES>
188
189 };
190
191
192 }//(End) implementation helpers
193
194
195
196
214 template<typename TYPES>
216 {
217
218 public:
221 };
222
223 template<typename RET>
224 using VisitorFunc = variant::VFunc<RET>::template VisitorInterface<TYPES>;
225 template<typename RET>
226 using VisitorConstFunc = variant::VFunc<RET>::template VisitorInterface<meta::ConstAll<typename TYPES::List>>;
227
233 : public VisitorFunc<void>
234 {
235 public:
236 virtual ~Visitor() { }
237 };
238
240 : public VisitorConstFunc<bool>
241 {
242 public:
243 virtual ~Predicate() { }
244 };
245
247 : public VisitorConstFunc<string>
248 {
249 public:
250 virtual ~Renderer() { }
251 };
252
258 template<template<class> class _P_>
260
261
262 private:
264 struct Buffer
266 {
267 alignas(ALIGN)
268 std::byte content_[SIZ];
269
270 void* ptr() { return &content_; }
271
272
273 virtual ~Buffer() {}
274
275 virtual void dispatch (Visitor&) =0;
276 virtual bool dispatch (Predicate&) const =0;
277 virtual string dispatch (Renderer&) const =0;
278 virtual operator string() const =0;
279 };
280
281
283 template<typename TY>
284 struct Buff
285 : meta::CopySupport<TY>::template Policy<Buffer,Buff<TY>>
286 {
287 static_assert (SIZ >= sizeof(TY), "Variant record: insufficient embedded Buffer size");
288
289 TY&
290 access() const
291 {
292 return * std::launder (reinterpret_cast<TY*> (unConst(this)->ptr()));
293 }
294
296 {
297 access().~TY();
298 }
299
300 Buff (TY const& obj)
301 {
302 new(Buffer::ptr()) TY(obj);
303 }
304
305 Buff (TY && robj)
306 {
307 new(Buffer::ptr()) TY(move(robj));
308 }
309
310 Buff (Buff const& oBuff)
311 {
312 new(Buffer::ptr()) TY(oBuff.access());
313 }
314
315 Buff (Buff && rBuff)
316 {
317 new(Buffer::ptr()) TY(move (rBuff.access()));
318 }
319
320 void
322 {
323 *this = buff.access();
324 }
325
326 void
328 {
329 *this = move (rref.access());
330 }
331
332 void
333 operator= (TY const& ob)
334 {
335 if (&ob != Buffer::ptr())
336 this->access() = ob;
337 }
338
339 void
340 operator= (TY && rob)
341 {
342 if (&rob != Buffer::ptr())
343 this->access() = move(rob);
344 }
345
346
347
348 static string indicateTypeMismatch (Buffer&);
349
350
351 static Buff&
353 {
354 Buff* buff = dynamic_cast<Buff*> (&b);
355
356 if (!buff)
358 ,LERR_(WRONG_TYPE));
359 else
360 return *buff;
361 }
362
363 void
364 dispatch (Visitor& visitor)
365 {
366 using Dispatcher = variant::VFunc<void>::template ValueAcceptInterface<TY>;
367
368 Dispatcher& typeDispatcher = visitor;
369 typeDispatcher.handle (this->access());
370 }
371
372 bool
373 dispatch (Predicate& visitor) const
374 {
375 using Dispatcher = variant::VFunc<bool>::template ValueAcceptInterface<const TY>;
376
377 Dispatcher& typeDispatcher = visitor;
378 return typeDispatcher.handle (this->access());
379 }
380
381 string
382 dispatch (Renderer& visitor) const
383 {
384 using Dispatcher = variant::VFunc<string>::template ValueAcceptInterface<const TY>;
385
386 Dispatcher& typeDispatcher = visitor;
387 return typeDispatcher.handle (this->access());
388 }
389
391 operator string() const;
392 };
393
394 enum{ BUFFSIZE = sizeof(Buffer) };
395
400
401
402
403
404
405 protected: /* === internal interface for managing the storage === */
406
407 Buffer&
409 {
410 return * std::launder (reinterpret_cast<Buffer*> (&storage_));
411 }
412 Buffer const&
413 buffer() const
414 {
415 return * std::launder (reinterpret_cast<const Buffer*> (&storage_));
416 }
417
418 template<typename X>
419 Buff<X>&
421 {
422 return Buff<X>::downcast(this->buffer());
423 }
424
426 template<typename X>
427 X*
429 {
430 Buff<X>* buff = dynamic_cast<Buff<X>*> (& this->buffer());
431 if (buff)
432 return & buff->access();
433 else
434 return nullptr;
435 }
436
437
438 public:
440 {
441 buffer().~Buffer();
442 }
443
445 {
446 using DefaultType = TYPES::List::Head;
447
448 new(storage_) Buff<DefaultType> (DefaultType());
449 }
450
451 template<typename X>
452 Variant(X&& x)
453 {
454 static_assert (variant::CanBuildFrom<X, TYPES>(), "No type in Typelist can be built from the given argument");
455
456 using StorageType = variant::CanBuildFrom<X, TYPES>::Type;
457
458 new(storage_) Buff<StorageType> (forward<X>(x));
459 }
460
462 {
463 ref.buffer().copyInto (&storage_);
464 }
465
466 Variant (Variant const& ref)
467 {
468 ref.buffer().copyInto (&storage_);
469 }
470
472 {
473 rref.buffer().moveInto (&storage_);
474 }
475
476 template<typename X>
477 Variant&
479 {
480 using RawType = std::remove_reference<X>::type;
482 "Type error: the given variant could never hold the required type");
483 static_assert (std::is_copy_assignable<RawType>::value, "target type does not support assignment");
484
485 buff<RawType>() = forward<X>(x);
486 return *this;
487 }
488
489 Variant&
491 {
492 ovar.buffer().copyInto (this->buffer());
493 return *this;
494 }
495
496 Variant&
497 operator= (Variant const& ovar)
498 {
499 ovar.buffer().copyInto (this->buffer());
500 return *this;
501 }
502
503 Variant&
505 {
506 rvar.buffer().moveInto (this->buffer());
507 return *this;
508 }
509
510 //note: NOT defining a swap operation, because swapping inline storage is pointless!
511
512
514 operator string() const;
515
516
517 /* === Access === */
518
519 template<typename X>
520 X&
522 {
524 "Type error: the given variant could never hold the required type");
525
526 return buff<X>().access();
527 }
528
529 template<typename X>
530 X const&
531 get() const
532 {
533 return unConst(this)->template get<X>();
534 }
535
536 void
537 accept (Visitor& visitor)
538 {
539 buffer().dispatch (visitor);
540 }
541
542 bool
543 accept (Predicate& visitor) const
544 {
545 return buffer().dispatch (visitor);
546 }
547
548 string
549 accept (Renderer& visitor) const
550 {
551 return buffer().dispatch (visitor);
552 }
553 };
554
555
556
557 /* == diagnostic helper == */
558
559
560 template<typename TYPES>
562 {
563 return "Variant|" + string(buffer());
564 }
565
566 template<typename TYPES>
567 template<typename TY>
568 Variant<TYPES>::Buff<TY>::operator string() const
569 {
570 return util::typedString (this->access());
571 }
572
581 template<typename TYPES>
582 template<typename TY>
583 inline string
585 {
586 try {
587 return "Variant type mismatch: expected value of type «"
588 + lib::meta::typeStr<TY>()+"», "
589 + "however the given variant record is "
590 + string{target};
591 }
592 catch(...) { return lib::meta::FAILURE_INDICATOR; }
593 }
594
595
596}// namespace lib
597#endif /*LIB_VARIANT_H*/
virtual ~Predicate()
this is an interface
Definition variant.hpp:243
virtual ~Renderer()
this is an interface
Definition variant.hpp:250
to be implemented by the client for visitation
Definition variant.hpp:234
virtual ~Visitor()
this is an interface
Definition variant.hpp:236
Typesafe union record.
Definition variant.hpp:216
char storage_[BUFFSIZE]
embedded buffer actually holding the concrete Buff object, which in turn holds and manages the target...
Definition variant.hpp:399
Variant(Variant &ref)
Definition variant.hpp:461
X * maybeGet()
Definition variant.hpp:428
Variant(Variant const &ref)
Definition variant.hpp:466
variant::VFunc< RET >::template VisitorInterface< TYPES > VisitorFunc
Definition variant.hpp:224
X const & get() const
Definition variant.hpp:531
bool accept(Predicate &visitor) const
Definition variant.hpp:543
Variant(Variant &&rref)
Definition variant.hpp:471
Buffer const & buffer() const
Definition variant.hpp:413
Buff< X > & buff()
Definition variant.hpp:420
Variant & operator=(X x)
Definition variant.hpp:478
void accept(Visitor &visitor)
Definition variant.hpp:537
string accept(Renderer &visitor) const
Definition variant.hpp:549
Variant(X &&x)
Definition variant.hpp:452
variant::VFunc< RET >::template VisitorInterface< meta::ConstAll< typename TYPES::List > > VisitorConstFunc
Definition variant.hpp:226
Buffer & buffer()
Definition variant.hpp:408
virtual void copyInto(void *targetStorage) const =0
virtual void moveInto(void *targetStorage)=0
Lumiera error handling (C++ interface).
#define LERR_(_NAME_)
Definition error.hpp:45
Simple functions to represent objects, for debugging and diagnostics.
Helpers for working with lib::meta::Types (i.e.
enable_if_c< Cond::value, T >::type enable_if
SFINAE helper to control the visibility of specialisations and overloads.
Definition meta/util.hpp:87
const string FAILURE_INDICATOR
Policy to pick a suitable implementation of "virtual copy operations".
»Empty« mark
Definition typelist.hpp:82
Type list with head and tail; T ≡ Nil marks list end.
Definition typelist.hpp:90
variadic sequence of types
Definition typelist.hpp:102
meta::InstantiateForEach< typename TYPES::List, ValueAcceptInterface > VisitorInterface
build a generic visitor interface for all types in list
Definition variant.hpp:187
Helper to pick the first type from a type sequence, which fulfils the predicate (meta function) given...
Definition variant.hpp:157
Implementation namespace for support and library code.
LumieraError< LERR_(LOGIC)> Logic
Definition error.hpp:207
std::string typedString(TY const &val) noexcept
indicate type and possibly a (custom) conversion to string
OBJ * unConst(const OBJ *)
shortcut to save some typing when having to define const and non-const variants of member functions
Definition util.hpp:358
concrete inner capsule specialised for a given type
Definition variant.hpp:286
bool dispatch(Predicate &visitor) const
Definition variant.hpp:373
void dispatch(Visitor &visitor)
Definition variant.hpp:364
Buff(Buff const &oBuff)
Definition variant.hpp:310
string dispatch(Renderer &visitor) const
Definition variant.hpp:382
Buff(Buff &&rBuff)
Definition variant.hpp:315
void operator=(Buff const &buff)
Definition variant.hpp:321
TY & access() const
< core operation: target is contained within the inline buffer
Definition variant.hpp:290
Buff(TY const &obj)
Definition variant.hpp:300
Buff(TY &&robj)
Definition variant.hpp:305
static string indicateTypeMismatch(Buffer &)
error message when accessing the variant content with wrong type assumptions.
Definition variant.hpp:584
static Buff & downcast(Buffer &b)
Definition variant.hpp:352
Inner capsule managing the contained object (interface)
Definition variant.hpp:266
virtual bool dispatch(Predicate &) const =0
virtual void dispatch(Visitor &)=0
virtual string dispatch(Renderer &) const =0
std::byte content_[SIZ]
Definition variant.hpp:268
virtual ~Buffer()
this is an ABC with VTable
Definition variant.hpp:273
how to treat one single type in visitation
Definition variant.hpp:179
Metaprogramming: simple helpers for working with lists-of-types.
A template metaprogramming technique for manipulating collections of types.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Helper for building »virtual copy« operations.