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) Lumiera.org
5  2009, 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 
77 #ifndef LIB_OPAQUE_HOLDER_H
78 #define LIB_OPAQUE_HOLDER_H
79 
80 
81 #include "lib/error.hpp"
82 #include "lib/nocopy.hpp"
83 #include "lib/access-casted.hpp"
84 #include "lib/meta/util.hpp"
85 #include "lib/util.hpp"
86 
87 #include <boost/lexical_cast.hpp>
88 
89 #include <cstddef>
90 #include <utility>
91 #include <type_traits>
92 
93 
94 namespace lib {
95 
96  namespace error = lumiera::error;
97  using LERR_(BOTTOM_VALUE);
98  using LERR_(WRONG_TYPE);
99 
100  using util::isSameObject;
101  using util::unConst;
102 
103 
104  namespace { // implementation helpers...
105 
106  using lib::meta::enable_if;
107  using lib::meta::disable_if;
108  using std::is_constructible;
109 
110  template<typename X>
111  enable_if< is_constructible<bool,X>,
112  bool >
113  validitySelfCheck (X const& boolConvertible)
114  {
115  return bool(boolConvertible);
116  }
117 
118  template<typename X>
119  disable_if< is_constructible<bool,X>,
120  bool >
121  validitySelfCheck (X const&)
122  {
123  return true; // just pass if this type doesn't provide a validity check...
124  }
125 
126  }
127 
128 
129 
130  /* ==== Policy classes controlling re-Access ==== */
131 
138  template<class BA>
140  {
141  typedef BA Base;
142 
143  template<class SUB>
144  static Base*
145  convert2base (SUB& obj)
146  {
147  SUB* oPtr = &obj;
148  BA* asBase = util::AccessCasted<BA*>::access (oPtr);
149  if (asBase)
150  return asBase;
151 
152  throw error::Logic ("Unable to convert concrete object to Base interface"
153  , LERR_(WRONG_TYPE)
154  );
155  }
156  };
157 
170  {
171  typedef void Base;
172 
173  template<class SUB>
174  static void*
175  convert2base (SUB& obj)
176  {
177  return static_cast<void*> (&obj);
178  }
179  };
180 
181 
182 
183 
184 
185 
186 
205  template
206  < size_t siz
207  , class AccessPolicy = InPlaceAnyHolder_unrelatedTypes
209  >
211  {
212  typedef typename AccessPolicy::Base * BaseP;
213 
215  struct Buffer
216  {
217  alignas(size_t) std::byte content_[siz];
218 
219  void* ptr() { return &content_; }
220 
221  virtual ~Buffer() {}
222  virtual bool isValid() const =0;
223  virtual bool empty() const =0;
224  virtual BaseP getBase() const =0;
225 
226  virtual void clone (void* targetStorage) const =0;
227  };
228 
230  struct EmptyBuff : Buffer
231  {
232  virtual bool isValid() const { return false; }
233  virtual bool empty() const { return true; }
234 
235  BaseP
236  getBase() const
237  {
238  throw error::Invalid("accessing empty holder"
239  , LERR_(BOTTOM_VALUE));
240  }
241 
242  virtual void
243  clone (void* targetStorage) const
244  {
245  new(targetStorage) EmptyBuff();
246  }
247  };
248 
249 
252  template<typename SUB>
253  struct Buff : Buffer
254  {
255  static_assert (siz >= sizeof(SUB), "InPlaceAnyHolder: insufficient Buffer size");
256 
257  SUB&
258  get() const
259  {
260  return * std::launder (reinterpret_cast<SUB*> (unConst(this)->ptr()));
261  }
262 
263  ~Buff()
264  {
265  get().~SUB();
266  }
267 
268  explicit
269  Buff (SUB const& obj)
270  {
271  new(Buffer::ptr()) SUB (obj);
272  }
273 
274  Buff (Buff const& oBuff)
275  {
276  new(Buffer::ptr()) SUB (oBuff.get());
277  }
278 
279  Buff&
280  operator= (Buff const& ref)
281  {
282  if (&ref != this)
283  get() = ref.get();
284  return *this;
285  }
286 
287  /* == virtual access functions == */
288 
289  virtual void
290  clone (void* targetStorage) const
291  {
292  new(targetStorage) Buff(get());
293  }
294 
295  virtual BaseP
296  getBase() const
297  {
298  return AccessPolicy::convert2base (get());
299  }
300 
301  virtual bool
302  empty() const
303  {
304  return false;
305  }
306 
307  virtual bool
308  isValid() const
309  {
310  return validitySelfCheck (this->get());
311  }
312  };
313 
314 
315 
316  enum{ BUFFSIZE = sizeof(Buffer) };
317 
321  char storage_[BUFFSIZE];
322 
323 
324 
325 
326 
327 
328  protected: /* === internal interface for managing the storage === */
329 
330  Buffer&
331  buff()
332  {
333  return * std::launder (reinterpret_cast<Buffer*> (&storage_));
334  }
335  const Buffer&
336  buff() const
337  {
338  return * std::launder (reinterpret_cast<const Buffer *> (&storage_));
339  }
340 
341 
342  void
343  killBuffer()
344  {
345  buff().~Buffer();
346  }
347 
348  void
349  make_emptyBuff()
350  {
351  new(&storage_) EmptyBuff();
352  }
353 
354  template<class SUB>
355  void
356  place_inBuff (SUB const& obj)
357  {
358  new(&storage_) Buff<SUB> (obj);
359  }
360 
361  void
362  clone_inBuff (InPlaceAnyHolder const& ref)
363  {
364  ref.buff().clone (storage_);
365  }
366 
367  BaseP
368  asBase () const
369  {
370  BaseP asBase = buff().getBase();
371  ASSERT (asBase);
372  return asBase;
373  }
374 
375 
376 
377 
378  public:
379 
381  {
382  killBuffer();
383  }
384 
385  void
386  clear ()
387  {
388  killBuffer();
389  make_emptyBuff();
390  }
391 
392 
394  {
395  make_emptyBuff();
396  }
397 
398  template<class SUB>
399  InPlaceAnyHolder(SUB const& obj)
400  {
401  place_inBuff (obj);
402  }
403 
405  {
406  clone_inBuff (ref);
407  }
408 
410  operator= (InPlaceAnyHolder const& ref)
411  {
412  if (not isSameObject (*this, ref))
413  {
414  killBuffer();
415  try
416  {
417  clone_inBuff (ref);
418  }
419  catch (...)
420  {
421  make_emptyBuff();
422  throw;
423  }
424  }
425  return *this;
426  }
427 
428  template<class SUB>
430  operator= (SUB const& newContent)
431  {
432  if (empty()
433  or not isSameObject (*buff().getBase(), newContent)
434  )
435  {
436  killBuffer();
437  try
438  {
439  place_inBuff (newContent);
440  }
441  catch (...)
442  {
443  make_emptyBuff();
444  throw;
445  }
446  }
447  return *this;
448  }
449 
450 
451 
460  template<class SUB>
461  SUB& get() const
462  {
463  typedef const Buffer* Iface;
464  typedef const Buff<SUB> * Actual;
465  Iface interface = &buff();
466  Actual actual = dynamic_cast<Actual> (interface);
467  if (actual)
468  return actual->get();
469 
470  if (this->empty())
471  throw error::Invalid("accessing empty holder"
472  ,LERR_(BOTTOM_VALUE));
473  else
474  throw error::Logic ("Attempt to access OpaqueHolder's contents "
475  "specifying incompatible target type"
476  , LERR_(WRONG_TYPE)
477  );
478  }
479 
480 
481 
482  bool
483  empty() const
484  {
485  return buff().empty();
486  }
487 
488 
489  bool
490  isValid() const
491  {
492  return buff().isValid();
493  }
494 
495  explicit
496  operator bool() const
497  {
498  return isValid();
499  }
500  };
501 
502 
503 
504 
505 
506 
535  template
536  < class BA
537  , size_t siz = sizeof(BA)
538  >
541  {
543 
544  public:
545  OpaqueHolder() : InPlaceHolder() {}
546 
547  template<class SUB>
548  OpaqueHolder(SUB const& obj) : InPlaceHolder(obj) {}
549 
550  template<class SUB>
551  OpaqueHolder&
552  operator= (SUB const& newContent)
553  {
554  static_cast<InPlaceHolder&>(*this) = newContent;
555  return *this;
556  }
557 
558  // note: using standard copy operations
559 
560 
561 
562  /* === smart-ptr style access === */
563 
564  BA&
565  operator* () const
566  {
567  ASSERT (!InPlaceHolder::empty());
568  return *InPlaceHolder::buff().getBase();
569  }
570 
571  BA*
572  operator-> () const
573  {
574  ASSERT (!InPlaceHolder::empty());
575  return InPlaceHolder::buff().getBase();
576  }
577 
578  };
579 
580 
581 
582 
583 
584 
585  template<class BA, class DEFAULT>
586  class PlantingHandle;
587 
610  template
611  < class BA
612  , size_t siz = sizeof(BA)
613  , class DEFAULT = BA
614  >
617  {
618 
619  alignas(BA) mutable
620  std::byte buf_[siz];
621 
622 
623  BA&
624  getObj() const
625  {
626  return * std::launder (reinterpret_cast<BA*> (&buf_));
627  }
628 
629  void
630  placeDefault()
631  {
632  static_assert (siz >= sizeof(DEFAULT), "InPlaceBuffer too small");
633 
634  new(&buf_) DEFAULT();
635  }
636 
637  void
638  destroy()
639  {
640  getObj().~BA();
641  }
642 
643 
644  public:
645  ~InPlaceBuffer ()
646  {
647  destroy();
648  }
649 
650  InPlaceBuffer ()
651  {
652  placeDefault();
653  }
654 
656  template<class SUB>
657  InPlaceBuffer (SUB&& instance)
658  {
659  static_assert (siz >= sizeof(SUB), "InPlaceBuffer too small");
660 
661  new(&buf_) SUB (std::forward<SUB> (instance));
662  }
663 
664  template<typename TY>
665  struct TypeTag{ };
670  template<typename SUB>
671  static auto embedType() { return TypeTag<SUB>{}; }
672 
674  template<class TY, typename...ARGS>
675  InPlaceBuffer (TypeTag<TY>, ARGS&& ...args)
676  {
677  static_assert (siz >= sizeof(TY), "InPlaceBuffer too small");
678 
679  new(&buf_) TY (std::forward<ARGS> (args)...);
680  }
681 
682 
683 
686 
687 
689  template<class TY, typename...ARGS>
690  TY&
691  create (ARGS&& ...args)
692  {
693  static_assert (siz >= sizeof(TY), "InPlaceBuffer too small");
694 
695  destroy();
696  try {
697  return *new(&buf_) TY {std::forward<ARGS> (args)...};
698  }
699  catch (...)
700  {
701  placeDefault();
702  throw;
703  }
704  }
705 
707  template<class SUB>
708  SUB&
709  emplace (SUB&& implementation)
710  {
711  static_assert (siz >= sizeof(SUB), "InPlaceBuffer too small");
712 
713  destroy();
714  try {
715  return *new(&buf_) SUB {std::forward<SUB> (implementation)};
716  }
717  catch (...)
718  {
719  placeDefault();
720  throw;
721  }
722  }
723 
724  DEFAULT&
725  reset()
726  {
727  destroy();
728  placeDefault();
729  return static_cast<DEFAULT&> (getObj());
730  }
731 
732 
733 
734 
735  /* === smart-ptr style access === */
736 
737  BA&
738  operator* () const
739  {
740  return getObj();
741  }
742 
743  BA*
744  operator-> () const
745  {
746  return &getObj();
747  }
748 
749 
750  template<class SUB>
751  SUB*
752  access ()
753  {
754  BA * asBase = &getObj();
755  SUB* content = util::AccessCasted<SUB*>::access (asBase);
756  return content;
757  } // NOTE: might be null.
758  };
759 
760 
761 
779  template<class BA, class DEFAULT = BA>
780  class PlantingHandle
781  {
782  void* buffer_;
783  size_t maxSiz_;
784 
785  static_assert (std::has_virtual_destructor<BA>(),
786  "target interface BA must provide virtual dtor, "
787  "since InPlaceBuffer needs to take ownership.");
788 
789  template<class SUB>
790  void __ensure_can_create();
791 
792 
793  public:
794  template<size_t maxSiz>
796  : buffer_(&targetBuffer)
797  , maxSiz_(maxSiz)
798  { }
799 
800  // default copy acceptable...
801 
802 
803  template<class SUB>
804  bool
805  canCreate() const
806  {
807  static_assert(std::is_base_of<BA,SUB>(), "concrete object implanted into the opaque "
808  "buffer must implement the defined interface");
809  return sizeof(SUB) <= maxSiz_;
810  }
811 
813  template<class SUB>
814  SUB&
815  emplace (SUB&& implementation)
816  {
817  __ensure_can_create<SUB>();
818 
820  Holder& holder = *static_cast<Holder*> (buffer_);
821 
822  return holder.template emplace (std::forward<SUB> (implementation));
823  }
824 
826  template<class SUB, typename...ARGS>
827  SUB&
828  create (ARGS&& ...args)
829  {
830  __ensure_can_create<SUB>();
831 
833  Holder& holder = *static_cast<Holder*> (buffer_);
834 
835  return holder.template create<SUB> (std::forward<ARGS> (args)...);
836  }
837 
838 
839  BA*
840  get() const
841  {
842  ENSURE (buffer_);
843  BA& bufferContent = **static_cast<InPlaceBuffer<BA>*> (buffer_);
844  return &bufferContent;
845  }
846  };
847 
848 
849 
853  template<class BA, class B0>
854  template<class SUB>
855  inline void
857  {
858  if (not this->canCreate<SUB>())
859  throw error::Fatal("Unable to implant implementation object of size "
860  "exceeding the pre-established storage buffer capacity. "
861  +boost::lexical_cast<std::string>(sizeof(SUB)) + " > "
862  +boost::lexical_cast<std::string>(maxSiz_)
863  ,error::LUMIERA_ERROR_CAPACITY);
864  }
865 
866 
867 
868 } // namespace lib
869 #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:46
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:92
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:199
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:113
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.
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, bypassing any custom comparison operators.
Definition: util.hpp:372