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