Lumiera 0.pre.04~rc.1
»edit your freedom«
Loading...
Searching...
No Matches
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
84namespace lib {
85
86 namespace error = lumiera::error;
87 using LERR_(BOTTOM_VALUE);
88 using LERR_(WRONG_TYPE);
89
91 using util::isSameAdr;
92 using util::unConst;
93
94
95 namespace { // implementation helpers...
96
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 >
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 using Base = BA;
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 using Base = void;
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 using BaseP = AccessPolicy::Base *;
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
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
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
313
314
315
316
317
318
319 protected: /* === internal interface for managing the storage === */
320
321 Buffer&
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
335 {
336 buff().~Buffer();
337 }
338
339 void
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
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
378 {
379 killBuffer();
381 }
382
383
385 {
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
402 {
403 if (not isSameObject (*this, ref))
404 {
405 killBuffer();
406 try
407 {
408 clone_inBuff (ref);
409 }
410 catch (...)
411 {
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 {
435 throw;
436 }
437 }
438 return *this;
439 }
440
441
442
451 template<class SUB>
452 SUB& get() const
453 {
454 using Iface = const Buffer *;
455 using Actual = const Buff<SUB> *;
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:
537
538 template<class SUB>
539 OpaqueHolder(SUB const& obj) : InPlaceHolder(obj) {}
540
541 template<class SUB>
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 >
607 : util::NonCopyable
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
622 {
623 static_assert (siz >= sizeof(DEFAULT), "InPlaceBuffer too small");
624
625 new(&buf_) DEFAULT();
626 }
627
628 void
630 {
631 getObj().~BA();
632 }
633
634
635 public:
637 {
638 destroy();
639 }
640
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&
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*
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>
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
810 using Holder = InPlaceBuffer<BA, sizeof(SUB), DEFAULT>;
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
823 using Holder = InPlaceBuffer<BA, sizeof(SUB), DEFAULT>;
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 for accessing a value, employing either a conversion or downcast, depending on the relation of...
Inline buffer to hold and own an object while concealing the concrete type.
char storage_[BUFFSIZE]
embedded buffer actually holding the concrete Buff object, which in turn holds and manages the target...
void clone_inBuff(InPlaceAnyHolder const &ref)
void place_inBuff(SUB const &obj)
AccessPolicy::Base * BaseP
const Buffer & buff() const
InPlaceAnyHolder(SUB const &obj)
InPlaceAnyHolder(InPlaceAnyHolder const &ref)
InPlaceAnyHolder & operator=(InPlaceAnyHolder const &ref)
SUB & get() const
re-accessing the concrete contained object.
Buffer to place and maintain an object instance privately within another object.
PlantingHandle< BA, DEFAULT > Handle
a "planting handle" can be used to expose an opaque InPlaceBuffer through an API
static auto embedType()
helper to mark the subclass type to create.
InPlaceBuffer(TypeTag< TY >, ARGS &&...args)
immediately emplace an embedded subclass type
TY & create(ARGS &&...args)
Abbreviation for placement new.
SUB & emplace(SUB &&implementation)
move-construct an instance of subclass into the opaque buffer
InPlaceBuffer(SUB &&instance)
immediately move-emplace an embedded subclass type
Inline buffer to hold and own an object while concealing the concrete type.
InPlaceAnyHolder< siz, InPlaceAnyHolder_useCommonBase< BA > > InPlaceHolder
OpaqueHolder(SUB const &obj)
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
PlantingHandle(InPlaceBuffer< BA, maxSiz, DEFAULT > &targetBuffer)
SUB & emplace(SUB &&implementation)
move-construct an instance of a subclass into the opaque buffer
SUB & create(ARGS &&...args)
Abbreviation for placement new of a subclass SUB into the opaque buffer.
Derived specific exceptions within Lumiera's exception hierarchy.
Definition error.hpp:193
Lumiera error handling (C++ interface).
#define LERR_(_NAME_)
Definition error.hpp:45
Simple and lightweight helpers for metaprogramming and type detection.
enable_if< is_constructible< bool, X >, bool > validitySelfCheck(X const &boolConvertible)
enable_if_c< Cond::value, T >::type enable_if
SFINAE helper to control the visibility of specialisations and overloads.
Definition meta/util.hpp:87
enable_if_c< not Cond::value, T >::type disable_if
Definition meta/util.hpp:90
Implementation namespace for support and library code.
LumieraError< LERR_(FATAL), Logic > Fatal
Definition error.hpp:208
LumieraError< LERR_(LOGIC)> Logic
Definition error.hpp:207
LumieraError< LERR_(INVALID)> Invalid
Definition error.hpp:211
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee's memory identities.
Definition util.hpp:421
bool isSameAdr(A const &a, B const &b)
compare plain object address identity, disregarding type.
Definition util.hpp:411
OBJ * unConst(const OBJ *)
shortcut to save some typing when having to define const and non-const variants of member functions
Definition util.hpp:358
Mix-Ins to allow or prohibit various degrees of copying and cloning.
concrete subclass to manage a specific kind of contained object.
Buff & operator=(Buff const &ref)
virtual bool isValid() const
virtual void clone(void *targetStorage) const
virtual bool empty() const
SUB & get() const
< core operation: target is contained within the inline buffer
virtual BaseP getBase() const
Inner capsule managing the contained object (interface)
virtual BaseP getBase() const =0
virtual bool isValid() const =0
virtual ~Buffer()
this is an ABC with VTable
virtual void clone(void *targetStorage) const =0
virtual bool empty() const =0
special case: no stored object
virtual void clone(void *targetStorage) const
Alternative policy for accessing the contents without a common interface; use this policy if the inte...
Standard policy for accessing the contents via a common base class interface.
static Base * convert2base(SUB &obj)
static if_can_use_dynamic_downcast< SRC &&, TAR >::type access(SRC &&elem)
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...