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) Lumiera.org
5  2015, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
23 
78 #ifndef LIB_VARIANT_H
79 #define LIB_VARIANT_H
80 
81 
82 #include "lib/error.hpp"
83 #include "lib/meta/typelist.hpp"
85 #include "lib/meta/generator.hpp"
87 #include "lib/format-obj.hpp"
88 #include "lib/util.hpp"
89 
90 #include <type_traits>
91 #include <cstddef>
92 #include <utility>
93 #include <string>
94 
95 
96 namespace lib {
97 
98  using std::string;
99 
100  using std::move;
101  using std::forward;
102  using util::unConst;
103 
104  namespace error = lumiera::error;
105 
106 
107  namespace variant { // implementation metaprogramming helpers
108 
109  using std::remove_reference;
110  using meta::NullType;
111  using meta::Types;
112  using meta::Node;
113 
114 
115  template<typename X, typename TYPES>
117  : CanBuildFrom<typename remove_reference<X>::type
118  ,typename TYPES::List
119  >
120  { };
121 
122  template<typename X, typename TYPES>
123  struct CanBuildFrom<X, Node<X, TYPES>>
124  : std::true_type
125  {
126  using Type = X;
127  };
128 
129  template<typename X, typename TYPES>
130  struct CanBuildFrom<const X, Node<X, TYPES>>
131  : std::true_type
132  {
133  using Type = X;
134  };
135 
136  template<typename TYPES, size_t len>
137  struct CanBuildFrom<const char [len], Node<string, TYPES>>
138  : std::true_type
139  {
140  using Type = string;
141  };
142 
143  template<typename X, typename T,typename TYPES>
144  struct CanBuildFrom<X, Node<T, TYPES>>
145  : CanBuildFrom<X,TYPES>
146  { };
147 
148  template<typename X>
150  : std::false_type
151  { };
152 
153 
154  template<typename T>
155  struct Identity { using Type = T; };
156 
164  template<class TYPES, template<class> class _P_>
166  {
167  static_assert(not sizeof(TYPES), "None of the possible Types fulfils the condition");
168  };
169 
170  template<class...TYPES, template<class> class _P_>
171  struct FirstMatchingType<Types<TYPES...>, _P_>
172  : FirstMatchingType<typename Types<TYPES...>::List, _P_>
173  { };
174 
175  template<class T, class TYPES, template<class> class _P_>
176  struct FirstMatchingType<Node<T,TYPES>, _P_>
177  : std::conditional_t<_P_<T>::value, Identity<T>, FirstMatchingType<TYPES, _P_>>
178  { };
179 
180 
181 
182  template<typename RET>
183  struct VFunc
184  {
186  template<class VAL>
188  {
189  virtual RET handle(VAL&) { /* do nothing */ return RET(); };
190  };
191 
192 
194  template<typename TYPES>
195  using VisitorInterface
197 
198  };
199 
200 
201  }//(End) implementation helpers
202 
203 
204 
205 
223  template<typename TYPES>
224  class Variant
225  {
226 
227  public:
230  };
231 
232  template<typename RET>
233  using VisitorFunc = typename variant::VFunc<RET>::template VisitorInterface<TYPES>;
234  template<typename RET>
235  using VisitorConstFunc = typename variant::VFunc<RET>::template VisitorInterface<meta::ConstAll<typename TYPES::List>>;
236 
241  class Visitor
242  : public VisitorFunc<void>
243  {
244  public:
245  virtual ~Visitor() { }
246  };
247 
248  class Predicate
249  : public VisitorConstFunc<bool>
250  {
251  public:
252  virtual ~Predicate() { }
253  };
254 
255  class Renderer
256  : public VisitorConstFunc<string>
257  {
258  public:
259  virtual ~Renderer() { }
260  };
261 
267  template<template<class> class _P_>
269 
270 
271  private:
273  struct Buffer
275  {
276  alignas(ALIGN)
277  std::byte content_[SIZ];
278 
279  void* ptr() { return &content_; }
280 
281 
282  virtual ~Buffer() {}
283 
284  virtual void dispatch (Visitor&) =0;
285  virtual bool dispatch (Predicate&) const =0;
286  virtual string dispatch (Renderer&) const =0;
287  virtual operator string() const =0;
288  };
289 
290 
292  template<typename TY>
293  struct Buff
294  : meta::CopySupport<TY>::template Policy<Buffer,Buff<TY>>
295  {
296  static_assert (SIZ >= sizeof(TY), "Variant record: insufficient embedded Buffer size");
297 
298  TY&
299  access() const
300  {
301  return * std::launder (reinterpret_cast<TY*> (unConst(this)->ptr()));
302  }
303 
304  ~Buff()
305  {
306  access().~TY();
307  }
308 
309  Buff (TY const& obj)
310  {
311  new(Buffer::ptr()) TY(obj);
312  }
313 
314  Buff (TY && robj)
315  {
316  new(Buffer::ptr()) TY(move(robj));
317  }
318 
319  Buff (Buff const& oBuff)
320  {
321  new(Buffer::ptr()) TY(oBuff.access());
322  }
323 
324  Buff (Buff && rBuff)
325  {
326  new(Buffer::ptr()) TY(move (rBuff.access()));
327  }
328 
329  void
330  operator= (Buff const& buff)
331  {
332  *this = buff.access();
333  }
334 
335  void
336  operator= (Buff&& rref)
337  {
338  *this = move (rref.access());
339  }
340 
341  void
342  operator= (TY const& ob)
343  {
344  if (&ob != Buffer::ptr())
345  this->access() = ob;
346  }
347 
348  void
349  operator= (TY && rob)
350  {
351  if (&rob != Buffer::ptr())
352  this->access() = move(rob);
353  }
354 
355 
356 
357  static string indicateTypeMismatch (Buffer&);
358 
359 
360  static Buff&
361  downcast (Buffer& b)
362  {
363  Buff* buff = dynamic_cast<Buff*> (&b);
364 
365  if (!buff)
366  throw error::Logic(indicateTypeMismatch(b)
367  ,LERR_(WRONG_TYPE));
368  else
369  return *buff;
370  }
371 
372  void
373  dispatch (Visitor& visitor)
374  {
375  using Dispatcher = variant::VFunc<void>::template ValueAcceptInterface<TY>;
376 
377  Dispatcher& typeDispatcher = visitor;
378  typeDispatcher.handle (this->access());
379  }
380 
381  bool
382  dispatch (Predicate& visitor) const
383  {
384  using Dispatcher = variant::VFunc<bool>::template ValueAcceptInterface<const TY>;
385 
386  Dispatcher& typeDispatcher = visitor;
387  return typeDispatcher.handle (this->access());
388  }
389 
390  string
391  dispatch (Renderer& visitor) const
392  {
393  using Dispatcher = variant::VFunc<string>::template ValueAcceptInterface<const TY>;
394 
395  Dispatcher& typeDispatcher = visitor;
396  return typeDispatcher.handle (this->access());
397  }
398 
400  operator string() const;
401  };
402 
403  enum{ BUFFSIZE = sizeof(Buffer) };
404 
408  char storage_[BUFFSIZE];
409 
410 
411 
412 
413 
414  protected: /* === internal interface for managing the storage === */
415 
416  Buffer&
417  buffer()
418  {
419  return * std::launder (reinterpret_cast<Buffer*> (&storage_));
420  }
421  Buffer const&
422  buffer() const
423  {
424  return * std::launder (reinterpret_cast<const Buffer*> (&storage_));
425  }
426 
427  template<typename X>
428  Buff<X>&
429  buff()
430  {
431  return Buff<X>::downcast(this->buffer());
432  }
433 
435  template<typename X>
436  X*
438  {
439  Buff<X>* buff = dynamic_cast<Buff<X>*> (& this->buffer());
440  if (buff)
441  return & buff->access();
442  else
443  return nullptr;
444  }
445 
446 
447  public:
448  ~Variant()
449  {
450  buffer().~Buffer();
451  }
452 
453  Variant()
454  {
455  using DefaultType = typename TYPES::List::Head;
456 
457  new(storage_) Buff<DefaultType> (DefaultType());
458  }
459 
460  template<typename X>
461  Variant(X&& x)
462  {
463  static_assert (variant::CanBuildFrom<X, TYPES>(), "No type in Typelist can be built from the given argument");
464 
465  using StorageType = typename variant::CanBuildFrom<X, TYPES>::Type;
466 
467  new(storage_) Buff<StorageType> (forward<X>(x));
468  }
469 
470  Variant (Variant& ref)
471  {
472  ref.buffer().copyInto (&storage_);
473  }
474 
475  Variant (Variant const& ref)
476  {
477  ref.buffer().copyInto (&storage_);
478  }
479 
480  Variant (Variant&& rref)
481  {
482  rref.buffer().moveInto (&storage_);
483  }
484 
485  template<typename X>
486  Variant&
487  operator= (X x)
488  {
489  using RawType = typename std::remove_reference<X>::type;
490  static_assert (meta::isInList<RawType, typename TYPES::List>(),
491  "Type error: the given variant could never hold the required type");
492  static_assert (std::is_copy_assignable<RawType>::value, "target type does not support assignment");
493 
494  buff<RawType>() = forward<X>(x);
495  return *this;
496  }
497 
498  Variant&
499  operator= (Variant& ovar)
500  {
501  ovar.buffer().copyInto (this->buffer());
502  return *this;
503  }
504 
505  Variant&
506  operator= (Variant const& ovar)
507  {
508  ovar.buffer().copyInto (this->buffer());
509  return *this;
510  }
511 
512  Variant&
513  operator= (Variant&& rvar)
514  {
515  rvar.buffer().moveInto (this->buffer());
516  return *this;
517  }
518 
519  //note: NOT defining a swap operation, because swapping inline storage is pointless!
520 
521 
523  operator string() const;
524 
525 
526  /* === Access === */
527 
528  template<typename X>
529  X&
530  get()
531  {
532  static_assert (meta::isInList<X, typename TYPES::List>(),
533  "Type error: the given variant could never hold the required type");
534 
535  return buff<X>().access();
536  }
537 
538  template<typename X>
539  X const&
540  get() const
541  {
542  return unConst(this)->template get<X>();
543  }
544 
545  void
546  accept (Visitor& visitor)
547  {
548  buffer().dispatch (visitor);
549  }
550 
551  bool
552  accept (Predicate& visitor) const
553  {
554  return buffer().dispatch (visitor);
555  }
556 
557  string
558  accept (Renderer& visitor) const
559  {
560  return buffer().dispatch (visitor);
561  }
562  };
563 
564 
565 
566  /* == diagnostic helper == */
567 
568 
569  template<typename TYPES>
571  {
572  return "Variant|" + string(buffer());
573  }
574 
575  template<typename TYPES>
576  template<typename TY>
577  Variant<TYPES>::Buff<TY>::operator string() const
578  {
579  return util::typedString (this->access());
580  }
581 
590  template<typename TYPES>
591  template<typename TY>
592  inline string
594  {
595  try {
596  return "Variant type mismatch: expected value of type «"
597  + lib::meta::typeStr<TY>()+"», "
598  + "however the given variant record is "
599  + string{target};
600  }
601  catch(...) { return lib::meta::FAILURE_INDICATOR; }
602  }
603 
604 
605 }// namespace lib
606 #endif /*LIB_VARIANT_H*/
virtual ~Renderer()
this is an interface
Definition: variant.hpp:259
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:224
Apply a template to a collection of types.
Definition: generator.hpp:80
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:165
virtual ~Buffer()
this is an ABC with VTable
Definition: variant.hpp:282
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:299
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
virtual ~Predicate()
this is an interface
Definition: variant.hpp:252
virtual ~Visitor()
this is an interface
Definition: variant.hpp:245
concrete inner capsule specialised for a given type
Definition: variant.hpp:293
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
X * maybeGet()
Definition: variant.hpp:437
how to treat one single type in visitation
Definition: variant.hpp:187
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:241
Inner capsule managing the contained object (interface)
Definition: variant.hpp:273
Helper for building »virtual copy« operations.