Lumiera  0.pre.03
»edit your freedom«
opaque-holder.hpp
Go to the documentation of this file.
1 /*
2  OPAQUE-HOLDER.hpp - buffer holding an object inline while hiding the concrete type
3 
4  Copyright (C)
5  2009, 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 
68 #ifndef LIB_OPAQUE_HOLDER_H
69 #define LIB_OPAQUE_HOLDER_H
70 
71 
72 #include "lib/error.hpp"
73 #include "lib/nocopy.hpp"
74 #include "lib/access-casted.hpp"
75 #include "lib/meta/util.hpp"
76 #include "lib/util.hpp"
77 
78 #include <boost/lexical_cast.hpp>
79 
80 #include <type_traits>
81 #include <utility>
82 
83 
84 namespace lib {
85 
86  namespace error = lumiera::error;
87  using LERR_(BOTTOM_VALUE);
88  using LERR_(WRONG_TYPE);
89 
90  using util::isSameObject;
91  using util::isSameAdr;
92  using util::unConst;
93 
94 
95  namespace { // implementation helpers...
96 
98  using lib::meta::disable_if;
99  using std::is_constructible;
100 
101  template<typename X>
102  enable_if< is_constructible<bool,X>,
103  bool >
104  validitySelfCheck (X const& boolConvertible)
105  {
106  return bool(boolConvertible);
107  }
108 
109  template<typename X>
110  disable_if< is_constructible<bool,X>,
111  bool >
112  validitySelfCheck (X const&)
113  {
114  return true; // just pass if this type doesn't provide a validity check...
115  }
116 
117  }
118 
119 
120 
121  /* ==== Policy classes controlling re-Access ==== */
122 
129  template<class BA>
131  {
132  typedef BA Base;
133 
134  template<class SUB>
135  static Base*
136  convert2base (SUB& obj)
137  {
138  SUB* oPtr = &obj;
139  BA* asBase = util::AccessCasted<BA*>::access (oPtr);
140  if (asBase)
141  return asBase;
142 
143  throw error::Logic ("Unable to convert concrete object to Base interface"
144  , LERR_(WRONG_TYPE)
145  );
146  }
147  };
148 
161  {
162  typedef void Base;
163 
164  template<class SUB>
165  static void*
166  convert2base (SUB& obj)
167  {
168  return static_cast<void*> (&obj);
169  }
170  };
171 
172 
173 
174 
175 
176 
177 
196  template
197  < size_t siz
198  , class AccessPolicy = InPlaceAnyHolder_unrelatedTypes
200  >
202  {
203  typedef typename AccessPolicy::Base * BaseP;
204 
206  struct Buffer
207  {
208  alignas(size_t) std::byte content_[siz];
209 
210  void* ptr() { return &content_; }
211 
212  virtual ~Buffer() {}
213  virtual bool isValid() const =0;
214  virtual bool empty() const =0;
215  virtual BaseP getBase() const =0;
216 
217  virtual void clone (void* targetStorage) const =0;
218  };
219 
221  struct EmptyBuff : Buffer
222  {
223  virtual bool isValid() const { return false; }
224  virtual bool empty() const { return true; }
225 
226  BaseP
227  getBase() const
228  {
229  throw error::Invalid("accessing empty holder"
230  , LERR_(BOTTOM_VALUE));
231  }
232 
233  virtual void
234  clone (void* targetStorage) const
235  {
236  new(targetStorage) EmptyBuff();
237  }
238  };
239 
240 
243  template<typename SUB>
244  struct Buff : Buffer
245  {
246  static_assert (siz >= sizeof(SUB), "InPlaceAnyHolder: insufficient Buffer size");
247 
248  SUB&
249  get() const
250  {
251  return * std::launder (reinterpret_cast<SUB*> (unConst(this)->ptr()));
252  }
253 
254  ~Buff()
255  {
256  get().~SUB();
257  }
258 
259  explicit
260  Buff (SUB const& obj)
261  {
262  new(Buffer::ptr()) SUB (obj);
263  }
264 
265  Buff (Buff const& oBuff)
266  {
267  new(Buffer::ptr()) SUB (oBuff.get());
268  }
269 
270  Buff&
271  operator= (Buff const& ref)
272  {
273  if (&ref != this)
274  get() = ref.get();
275  return *this;
276  }
277 
278  /* == virtual access functions == */
279 
280  virtual void
281  clone (void* targetStorage) const
282  {
283  new(targetStorage) Buff(get());
284  }
285 
286  virtual BaseP
287  getBase() const
288  {
289  return AccessPolicy::convert2base (get());
290  }
291 
292  virtual bool
293  empty() const
294  {
295  return false;
296  }
297 
298  virtual bool
299  isValid() const
300  {
301  return validitySelfCheck (this->get());
302  }
303  };
304 
305 
306 
307  enum{ BUFFSIZE = sizeof(Buffer) };
308 
312  char storage_[BUFFSIZE];
313 
314 
315 
316 
317 
318 
319  protected: /* === internal interface for managing the storage === */
320 
321  Buffer&
322  buff()
323  {
324  return * std::launder (reinterpret_cast<Buffer*> (&storage_));
325  }
326  const Buffer&
327  buff() const
328  {
329  return * std::launder (reinterpret_cast<const Buffer *> (&storage_));
330  }
331 
332 
333  void
334  killBuffer()
335  {
336  buff().~Buffer();
337  }
338 
339  void
340  make_emptyBuff()
341  {
342  new(&storage_) EmptyBuff();
343  }
344 
345  template<class SUB>
346  void
347  place_inBuff (SUB const& obj)
348  {
349  new(&storage_) Buff<SUB> (obj);
350  }
351 
352  void
353  clone_inBuff (InPlaceAnyHolder const& ref)
354  {
355  ref.buff().clone (storage_);
356  }
357 
358  BaseP
359  asBase () const
360  {
361  BaseP asBase = buff().getBase();
362  ASSERT (asBase);
363  return asBase;
364  }
365 
366 
367 
368 
369  public:
370 
372  {
373  killBuffer();
374  }
375 
376  void
377  clear ()
378  {
379  killBuffer();
380  make_emptyBuff();
381  }
382 
383 
385  {
386  make_emptyBuff();
387  }
388 
389  template<class SUB>
390  InPlaceAnyHolder(SUB const& obj)
391  {
392  place_inBuff (obj);
393  }
394 
396  {
397  clone_inBuff (ref);
398  }
399 
401  operator= (InPlaceAnyHolder const& ref)
402  {
403  if (not isSameObject (*this, ref))
404  {
405  killBuffer();
406  try
407  {
408  clone_inBuff (ref);
409  }
410  catch (...)
411  {
412  make_emptyBuff();
413  throw;
414  }
415  }
416  return *this;
417  }
418 
419  template<class SUB>
421  operator= (SUB const& newContent)
422  {
423  if (empty()
424  or not isSameAdr (buff().getBase(), &newContent) // caution: BaseP may be void* and SUB might be a pointer
425  )
426  {
427  killBuffer();
428  try
429  {
430  place_inBuff (newContent);
431  }
432  catch (...)
433  {
434  make_emptyBuff();
435  throw;
436  }
437  }
438  return *this;
439  }
440 
441 
442 
451  template<class SUB>
452  SUB& get() const
453  {
454  typedef const Buffer* Iface;
455  typedef const Buff<SUB> * Actual;
456  Iface interface = &buff();
457  Actual actual = dynamic_cast<Actual> (interface);
458  if (actual)
459  return actual->get();
460 
461  if (this->empty())
462  throw error::Invalid("accessing empty holder"
463  ,LERR_(BOTTOM_VALUE));
464  else
465  throw error::Logic ("Attempt to access OpaqueHolder's contents "
466  "specifying incompatible target type"
467  , LERR_(WRONG_TYPE)
468  );
469  }
470 
471 
472 
473  bool
474  empty() const
475  {
476  return buff().empty();
477  }
478 
479 
480  bool
481  isValid() const
482  {
483  return buff().isValid();
484  }
485 
486  explicit
487  operator bool() const
488  {
489  return isValid();
490  }
491  };
492 
493 
494 
495 
496 
497 
526  template
527  < class BA
528  , size_t siz = sizeof(BA)
529  >
532  {
534 
535  public:
536  OpaqueHolder() : InPlaceHolder() {}
537 
538  template<class SUB>
539  OpaqueHolder(SUB const& obj) : InPlaceHolder(obj) {}
540 
541  template<class SUB>
542  OpaqueHolder&
543  operator= (SUB const& newContent)
544  {
545  static_cast<InPlaceHolder&>(*this) = newContent;
546  return *this;
547  }
548 
549  // note: using standard copy operations
550 
551 
552 
553  /* === smart-ptr style access === */
554 
555  BA&
556  operator* () const
557  {
558  ASSERT (!InPlaceHolder::empty());
559  return *InPlaceHolder::buff().getBase();
560  }
561 
562  BA*
563  operator-> () const
564  {
565  ASSERT (!InPlaceHolder::empty());
566  return InPlaceHolder::buff().getBase();
567  }
568 
569  };
570 
571 
572 
573 
574 
575 
576  template<class BA, class DEFAULT>
577  class PlantingHandle;
578 
601  template
602  < class BA
603  , size_t siz = sizeof(BA)
604  , class DEFAULT = BA
605  >
608  {
609 
610  alignas(BA) mutable
611  std::byte buf_[siz];
612 
613 
614  BA&
615  getObj() const
616  {
617  return * std::launder (reinterpret_cast<BA*> (&buf_));
618  }
619 
620  void
621  placeDefault()
622  {
623  static_assert (siz >= sizeof(DEFAULT), "InPlaceBuffer too small");
624 
625  new(&buf_) DEFAULT();
626  }
627 
628  void
629  destroy()
630  {
631  getObj().~BA();
632  }
633 
634 
635  public:
636  ~InPlaceBuffer ()
637  {
638  destroy();
639  }
640 
641  InPlaceBuffer ()
642  {
643  placeDefault();
644  }
645 
647  template<class SUB>
648  InPlaceBuffer (SUB&& instance)
649  {
650  static_assert (siz >= sizeof(SUB), "InPlaceBuffer too small");
651 
652  new(&buf_) SUB (std::forward<SUB> (instance));
653  }
654 
655  template<typename TY>
656  struct TypeTag{ };
661  template<typename SUB>
662  static auto embedType() { return TypeTag<SUB>{}; }
663 
665  template<class TY, typename...ARGS>
666  InPlaceBuffer (TypeTag<TY>, ARGS&& ...args)
667  {
668  static_assert (siz >= sizeof(TY), "InPlaceBuffer too small");
669 
670  new(&buf_) TY (std::forward<ARGS> (args)...);
671  }
672 
673 
674 
677 
678 
680  template<class TY, typename...ARGS>
681  TY&
682  create (ARGS&& ...args)
683  {
684  static_assert (siz >= sizeof(TY), "InPlaceBuffer too small");
685 
686  destroy();
687  try {
688  return *new(&buf_) TY {std::forward<ARGS> (args)...};
689  }
690  catch (...)
691  {
692  placeDefault();
693  throw;
694  }
695  }
696 
698  template<class SUB>
699  SUB&
700  emplace (SUB&& implementation)
701  {
702  static_assert (siz >= sizeof(SUB), "InPlaceBuffer too small");
703 
704  destroy();
705  try {
706  return *new(&buf_) SUB {std::forward<SUB> (implementation)};
707  }
708  catch (...)
709  {
710  placeDefault();
711  throw;
712  }
713  }
714 
715  DEFAULT&
716  reset()
717  {
718  destroy();
719  placeDefault();
720  return static_cast<DEFAULT&> (getObj());
721  }
722 
723 
724 
725 
726  /* === smart-ptr style access === */
727 
728  BA&
729  operator* () const
730  {
731  return getObj();
732  }
733 
734  BA*
735  operator-> () const
736  {
737  return &getObj();
738  }
739 
740 
741  template<class SUB>
742  SUB*
743  access ()
744  {
745  BA * asBase = &getObj();
746  SUB* content = util::AccessCasted<SUB*>::access (asBase);
747  return content;
748  } // NOTE: might be null.
749  };
750 
751 
752 
770  template<class BA, class DEFAULT = BA>
771  class PlantingHandle
772  {
773  void* buffer_;
774  size_t maxSiz_;
775 
776  static_assert (std::has_virtual_destructor<BA>(),
777  "target interface BA must provide virtual dtor, "
778  "since InPlaceBuffer needs to take ownership.");
779 
780  template<class SUB>
781  void __ensure_can_create();
782 
783 
784  public:
785  template<size_t maxSiz>
787  : buffer_(&targetBuffer)
788  , maxSiz_(maxSiz)
789  { }
790 
791  // default copy acceptable...
792 
793 
794  template<class SUB>
795  bool
796  canCreate() const
797  {
798  static_assert(std::is_base_of<BA,SUB>(), "concrete object implanted into the opaque "
799  "buffer must implement the defined interface");
800  return sizeof(SUB) <= maxSiz_;
801  }
802 
804  template<class SUB>
805  SUB&
806  emplace (SUB&& implementation)
807  {
808  __ensure_can_create<SUB>();
809 
811  Holder& holder = *static_cast<Holder*> (buffer_);
812 
813  return holder.template emplace (std::forward<SUB> (implementation));
814  }
815 
817  template<class SUB, typename...ARGS>
818  SUB&
819  create (ARGS&& ...args)
820  {
821  __ensure_can_create<SUB>();
822 
824  Holder& holder = *static_cast<Holder*> (buffer_);
825 
826  return holder.template create<SUB> (std::forward<ARGS> (args)...);
827  }
828 
829 
830  BA*
831  get() const
832  {
833  ENSURE (buffer_);
834  BA& bufferContent = **static_cast<InPlaceBuffer<BA>*> (buffer_);
835  return &bufferContent;
836  }
837  };
838 
839 
840 
844  template<class BA, class B0>
845  template<class SUB>
846  inline void
848  {
849  if (not this->canCreate<SUB>())
850  throw error::Fatal("Unable to implant implementation object of size "
851  "exceeding the pre-established storage buffer capacity. "
852  +boost::lexical_cast<std::string>(sizeof(SUB)) + " > "
853  +boost::lexical_cast<std::string>(maxSiz_)
854  ,error::LUMIERA_ERROR_CAPACITY);
855  }
856 
857 
858 
859 } // namespace lib
860 #endif
Helper template to access a given value, possibly converted or casted in a safe way.
TY & create(ARGS &&...args)
Abbreviation for placement new.
Simple and lightweight helpers for metaprogramming and type detection.
Any copy and copy construction prohibited.
Definition: nocopy.hpp:37
virtual ~Buffer()
this is an ABC with VTable
SUB & create(ARGS &&...args)
Abbreviation for placement new of a subclass SUB into the opaque buffer.
InPlaceBuffer(SUB &&instance)
immediately move-emplace an embedded subclass type
SUB & get() const
< core operation: target is contained within the inline buffer
Implementation namespace for support and library code.
InPlaceBuffer(TypeTag< TY >, ARGS &&...args)
immediately emplace an embedded subclass type
typename enable_if_c< Cond::value, T >::type enable_if
SFINAE helper to control the visibility of specialisations and overloads.
Definition: meta/util.hpp:83
Inline buffer to hold and own an object while concealing the concrete type.
Alternative policy for accessing the contents without a common interface; use this policy if the inte...
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
SUB & emplace(SUB &&implementation)
move-construct an instance of a subclass into the opaque buffer
Inline buffer to hold and own an object while concealing the concrete type.
SUB & emplace(SUB &&implementation)
move-construct an instance of subclass into the opaque buffer
Mix-Ins to allow or prohibit various degrees of copying and cloning.
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
Definition: record.hpp:104
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Lumiera error handling (C++ interface).
Inner capsule managing the contained object (interface)
Standard policy for accessing the contents via a common base class interface.
BaseP asBase() const
<
Buffer to place and maintain an object instance privately within another object.
bool isSameAdr(A const &a, B const &b)
compare plain object address identity, disregarding type.
Definition: util.hpp:411
special case: no stored object
static auto embedType()
helper to mark the subclass type to create.
concrete subclass to manage a specific kind of contained object.
Helper for accessing a value, employing either a conversion or downcast, depending on the relation of...
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee&#39;s memory identities. ...
Definition: util.hpp:421