Lumiera  0.pre.03
»edit your freedom«
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"
76 #include "lib/meta/generator.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 
87 namespace 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::NullType;
102  using meta::Types;
103  using meta::Node;
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>
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>
186  using VisitorInterface
188 
189  };
190 
191 
192  }//(End) implementation helpers
193 
194 
195 
196 
214  template<typename TYPES>
215  class Variant
216  {
217 
218  public:
221  };
222 
223  template<typename RET>
224  using VisitorFunc = typename variant::VFunc<RET>::template VisitorInterface<TYPES>;
225  template<typename RET>
226  using VisitorConstFunc = typename variant::VFunc<RET>::template VisitorInterface<meta::ConstAll<typename TYPES::List>>;
227 
232  class Visitor
233  : public VisitorFunc<void>
234  {
235  public:
236  virtual ~Visitor() { }
237  };
238 
239  class Predicate
240  : public VisitorConstFunc<bool>
241  {
242  public:
243  virtual ~Predicate() { }
244  };
245 
246  class Renderer
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 
295  ~Buff()
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
321  operator= (Buff const& buff)
322  {
323  *this = buff.access();
324  }
325 
326  void
327  operator= (Buff&& rref)
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&
352  downcast (Buffer& b)
353  {
354  Buff* buff = dynamic_cast<Buff*> (&b);
355 
356  if (!buff)
357  throw error::Logic(indicateTypeMismatch(b)
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 
399  char storage_[BUFFSIZE];
400 
401 
402 
403 
404 
405  protected: /* === internal interface for managing the storage === */
406 
407  Buffer&
408  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>&
420  buff()
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:
439  ~Variant()
440  {
441  buffer().~Buffer();
442  }
443 
444  Variant()
445  {
446  using DefaultType = typename 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 = typename variant::CanBuildFrom<X, TYPES>::Type;
457 
458  new(storage_) Buff<StorageType> (forward<X>(x));
459  }
460 
461  Variant (Variant& ref)
462  {
463  ref.buffer().copyInto (&storage_);
464  }
465 
466  Variant (Variant const& ref)
467  {
468  ref.buffer().copyInto (&storage_);
469  }
470 
471  Variant (Variant&& rref)
472  {
473  rref.buffer().moveInto (&storage_);
474  }
475 
476  template<typename X>
477  Variant&
478  operator= (X x)
479  {
480  using RawType = typename std::remove_reference<X>::type;
481  static_assert (meta::isInList<RawType, typename TYPES::List>(),
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&
490  operator= (Variant& ovar)
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&
504  operator= (Variant&& rvar)
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&
521  get()
522  {
523  static_assert (meta::isInList<X, typename TYPES::List>(),
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 ~Renderer()
this is an interface
Definition: variant.hpp:250
A template metaprogramming technique for manipulating collections of types.
Policy to pick a suitable implementation of "virtual copy operations".
Typesafe union record.
Definition: variant.hpp:215
Apply a template to a collection of types.
Definition: generator.hpp:71
Helpers for working with lib::meta::Types (i.e.
Metaprogramming: simple helpers for working with lists-of-types.
Helper to pick the first type from a type sequence, which fulfils the predicate (meta function) given...
Definition: variant.hpp:156
virtual ~Buffer()
this is an ABC with VTable
Definition: variant.hpp:273
Metafunction " max( alignof(T) ) for T in TYPES ".
Implementation namespace for support and library code.
Metafunction " max( sizeof(T) ) for T in TYPES ".
TY & access() const
< core operation: target is contained within the inline buffer
Definition: variant.hpp:290
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
virtual ~Predicate()
this is an interface
Definition: variant.hpp:243
virtual ~Visitor()
this is an interface
Definition: variant.hpp:236
concrete inner capsule specialised for a given type
Definition: variant.hpp:284
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
X * maybeGet()
Definition: variant.hpp:428
how to treat one single type in visitation
Definition: variant.hpp:178
Lumiera error handling (C++ interface).
Simple functions to represent objects, for debugging and diagnostics.
to be implemented by the client for visitation
Definition: variant.hpp:232
Inner capsule managing the contained object (interface)
Definition: variant.hpp:264
Helper for building »virtual copy« operations.