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 <utility>
92 #include <string>
93 
94 
95 namespace lib {
96 
97  using std::string;
98 
99  using std::move;
100  using std::forward;
101  using util::unConst;
102 
103  namespace error = lumiera::error;
104 
105 
106  namespace variant { // implementation metaprogramming helpers
107 
108  using std::remove_reference;
109  using meta::NullType;
110  using meta::Types;
111  using meta::Node;
112 
113 
114  template<typename X, typename TYPES>
116  : CanBuildFrom<typename remove_reference<X>::type
117  ,typename TYPES::List
118  >
119  { };
120 
121  template<typename X, typename TYPES>
122  struct CanBuildFrom<X, Node<X, TYPES>>
123  : std::true_type
124  {
125  using Type = X;
126  };
127 
128  template<typename X, typename TYPES>
129  struct CanBuildFrom<const X, Node<X, TYPES>>
130  : std::true_type
131  {
132  using Type = X;
133  };
134 
135  template<typename TYPES, size_t len>
136  struct CanBuildFrom<const char [len], Node<string, TYPES>>
137  : std::true_type
138  {
139  using Type = string;
140  };
141 
142  template<typename X, typename T,typename TYPES>
143  struct CanBuildFrom<X, Node<T, TYPES>>
144  : CanBuildFrom<X,TYPES>
145  { };
146 
147  template<typename X>
149  : std::false_type
150  { };
151 
152 
153  template<typename T>
154  struct Identity { using Type = T; };
155 
163  template<class TYPES, template<class> class _P_>
165  {
166  static_assert(not sizeof(TYPES), "None of the possible Types fulfils the condition");
167  };
168 
169  template<class...TYPES, template<class> class _P_>
170  struct FirstMatchingType<Types<TYPES...>, _P_>
171  : FirstMatchingType<typename Types<TYPES...>::List, _P_>
172  { };
173 
174  template<class T, class TYPES, template<class> class _P_>
175  struct FirstMatchingType<Node<T,TYPES>, _P_>
176  : std::conditional_t<_P_<T>::value, Identity<T>, FirstMatchingType<TYPES, _P_>>
177  { };
178 
179 
180 
181  template<typename RET>
182  struct VFunc
183  {
185  template<class VAL>
187  {
188  virtual RET handle(VAL&) { /* do nothing */ return RET(); };
189  };
190 
191 
193  template<typename TYPES>
194  using VisitorInterface
196 
197  };
198 
199 
200  }//(End) implementation helpers
201 
202 
203 
204 
222  template<typename TYPES>
223  class Variant
224  {
225  // WARNING: never add any member field before the storage_ array /////////////////////////TICKET #1204
226 
227  public:
229 
230  template<typename RET>
231  using VisitorFunc = typename variant::VFunc<RET>::template VisitorInterface<TYPES>;
232  template<typename RET>
233  using VisitorConstFunc = typename variant::VFunc<RET>::template VisitorInterface<meta::ConstAll<typename TYPES::List>>;
234 
239  class Visitor
240  : public VisitorFunc<void>
241  {
242  public:
243  virtual ~Visitor() { }
244  };
245 
246  class Predicate
247  : public VisitorConstFunc<bool>
248  {
249  public:
250  virtual ~Predicate() { }
251  };
252 
258  template<template<class> class _P_>
260 
261 
262  private:
264  struct Buffer
266  {
267  char content_[SIZ];
268 
269  void* ptr() { return &content_; }
270 
271 
272  virtual ~Buffer() {}
273 
274  virtual void dispatch (Visitor&) =0;
275  virtual bool dispatch (Predicate&) const =0;
276  virtual operator string() const =0;
277  };
278 
279 
281  template<typename TY>
282  struct Buff
283  : meta::CopySupport<TY>::template Policy<Buffer,Buff<TY>>
284  {
285  static_assert (SIZ >= sizeof(TY), "Variant record: insufficient embedded Buffer size");
286 
287  TY&
288  access() const
289  {
290  return *reinterpret_cast<TY*> (unConst(this)->ptr());
291  }
292 
293  ~Buff()
294  {
295  access().~TY();
296  }
297 
298  Buff (TY const& obj)
299  {
300  new(Buffer::ptr()) TY(obj);
301  }
302 
303  Buff (TY && robj)
304  {
305  new(Buffer::ptr()) TY(move(robj));
306  }
307 
308  Buff (Buff const& oBuff)
309  {
310  new(Buffer::ptr()) TY(oBuff.access());
311  }
312 
313  Buff (Buff && rBuff)
314  {
315  new(Buffer::ptr()) TY(move (rBuff.access()));
316  }
317 
318  void
319  operator= (Buff const& buff)
320  {
321  *this = buff.access();
322  }
323 
324  void
325  operator= (Buff&& rref)
326  {
327  *this = move (rref.access());
328  }
329 
330  void
331  operator= (TY const& ob)
332  {
333  if (&ob != Buffer::ptr())
334  this->access() = ob;
335  }
336 
337  void
338  operator= (TY && rob)
339  {
340  if (&rob != Buffer::ptr())
341  this->access() = move(rob);
342  }
343 
344 
345 
346  static string indicateTypeMismatch (Buffer&);
347 
348 
349  static Buff&
350  downcast (Buffer& b)
351  {
352  Buff* buff = dynamic_cast<Buff*> (&b);
353 
354  if (!buff)
355  throw error::Logic(indicateTypeMismatch(b)
356  ,error::LERR_(WRONG_TYPE));
357  else
358  return *buff;
359  }
360 
361  void
362  dispatch (Visitor& visitor)
363  {
364  using Dispatcher = variant::VFunc<void>::template ValueAcceptInterface<TY>;
365 
366  Dispatcher& typeDispatcher = visitor;
367  typeDispatcher.handle (this->access());
368  }
369 
370  bool
371  dispatch (Predicate& visitor) const
372  {
373  using Dispatcher = variant::VFunc<bool>::template ValueAcceptInterface<const TY>;
374 
375  Dispatcher& typeDispatcher = visitor;
376  return typeDispatcher.handle (this->access());
377  }
378 
380  operator string() const;
381  };
382 
383  enum{ BUFFSIZE = sizeof(Buffer) };
384 
388  char storage_[BUFFSIZE];
389 
390 
391 
392 
393 
394  protected: /* === internal interface for managing the storage === */
395 
396  Buffer&
397  buffer()
398  {
399  return *reinterpret_cast<Buffer*> (&storage_);
400  }
401  Buffer const&
402  buffer() const
403  {
404  return *reinterpret_cast<const Buffer*> (&storage_);
405  }
406 
407  template<typename X>
408  Buff<X>&
409  buff()
410  {
411  return Buff<X>::downcast(this->buffer());
412  }
413 
415  template<typename X>
416  X*
418  {
419  Buff<X>* buff = dynamic_cast<Buff<X>*> (& this->buffer());
420  if (buff)
421  return & buff->access();
422  else
423  return nullptr;
424  }
425 
426 
427  public:
428  ~Variant()
429  {
430  buffer().~Buffer();
431  }
432 
433  Variant()
434  {
435  using DefaultType = typename TYPES::List::Head;
436 
437  new(storage_) Buff<DefaultType> (DefaultType());
438  }
439 
440  template<typename X>
441  Variant(X&& x)
442  {
443  static_assert (variant::CanBuildFrom<X, TYPES>(), "No type in Typelist can be built from the given argument");
444 
445  using StorageType = typename variant::CanBuildFrom<X, TYPES>::Type;
446 
447  new(storage_) Buff<StorageType> (forward<X>(x));
448  }
449 
450  Variant (Variant& ref)
451  {
452  ref.buffer().copyInto (&storage_);
453  }
454 
455  Variant (Variant const& ref)
456  {
457  ref.buffer().copyInto (&storage_);
458  }
459 
460  Variant (Variant&& rref)
461  {
462  rref.buffer().moveInto (&storage_);
463  }
464 
465  template<typename X>
466  Variant&
467  operator= (X x)
468  {
469  using RawType = typename std::remove_reference<X>::type;
470  static_assert (meta::isInList<RawType, typename TYPES::List>(),
471  "Type error: the given variant could never hold the required type");
472  static_assert (std::is_copy_assignable<RawType>::value, "target type does not support assignment");
473 
474  buff<RawType>() = forward<X>(x);
475  return *this;
476  }
477 
478  Variant&
479  operator= (Variant& ovar)
480  {
481  ovar.buffer().copyInto (this->buffer());
482  return *this;
483  }
484 
485  Variant&
486  operator= (Variant const& ovar)
487  {
488  ovar.buffer().copyInto (this->buffer());
489  return *this;
490  }
491 
492  Variant&
493  operator= (Variant&& rvar)
494  {
495  rvar.buffer().moveInto (this->buffer());
496  return *this;
497  }
498 
499  //note: NOT defining a swap operation, because swapping inline storage is pointless!
500 
501 
503  operator string() const;
504 
505 
506  /* === Access === */
507 
508  template<typename X>
509  X&
510  get()
511  {
512  static_assert (meta::isInList<X, typename TYPES::List>(),
513  "Type error: the given variant could never hold the required type");
514 
515  return buff<X>().access();
516  }
517 
518  template<typename X>
519  X const&
520  get() const
521  {
522  return unConst(this)->template get<X>();
523  }
524 
525  void
526  accept (Visitor& visitor)
527  {
528  buffer().dispatch (visitor);
529  }
530 
531  bool
532  accept (Predicate& visitor) const
533  {
534  return buffer().dispatch (visitor);
535  }
536  };
537 
538 
539 
540  /* == diagnostic helper == */
541 
542 
543  template<typename TYPES>
545  {
546  return "Variant|" + string(buffer());
547  }
548 
549  template<typename TYPES>
550  template<typename TY>
551  Variant<TYPES>::Buff<TY>::operator string() const
552  {
553  return util::typedString (this->access());
554  }
555 
564  template<typename TYPES>
565  template<typename TY>
566  inline string
568  {
569  try {
570  return "Variant type mismatch: expected value of type «"
571  + lib::meta::typeStr<TY>()+"», "
572  + "however the given variant record is "
573  + string{target};
574  }
575  catch(...) { return lib::meta::FAILURE_INDICATOR; }
576  }
577 
578 
579 }// namespace lib
580 #endif /*LIB_VARIANT_H*/
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:223
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:164
virtual ~Buffer()
this is an ABC with VTable
Definition: variant.hpp:272
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:288
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:196
virtual ~Predicate()
this is an interface
Definition: variant.hpp:250
virtual ~Visitor()
this is an interface
Definition: variant.hpp:243
concrete inner capsule specialised for a given type
Definition: variant.hpp:282
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
X * maybeGet()
Definition: variant.hpp:417
how to treat one single type in visitation
Definition: variant.hpp:186
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:239
Inner capsule managing the contained object (interface)
Definition: variant.hpp:264
Helper for building "virtual copy" operations.