Lumiera  0.pre.03
»edit your freedom«
several-builder.hpp
Go to the documentation of this file.
1 /*
2  SEVERAL-BUILDER.hpp - builder for a limited fixed collection of elements
3 
4  Copyright (C)
5  2024, 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 
91 #ifndef LIB_SEVERAL_BUILDER_H
92 #define LIB_SEVERAL_BUILDER_H
93 
94 
95 #include "lib/error.hpp"
96 #include "lib/several.hpp"
97 #include "include/limits.hpp"
98 #include "lib/iter-explorer.hpp"
99 #include "lib/format-string.hpp"
100 #include "lib/util.hpp"
101 
102 #include <type_traits>
103 #include <functional>
104 #include <cstring>
105 #include <utility>
106 #include <vector>
107 
108 
109 
110 namespace lib {
111  namespace err = lumiera::error;
112 
113  using std::vector;
114  using std::forward;
115  using std::move;
116  using std::byte;
117 
118  namespace {
122  const uint INITIAL_ELM_CNT = 10;
123 
124 
125  using util::max;
126  using util::min;
127  using util::_Fmt;
128  using util::positiveDiff;
129  using std::is_nothrow_move_constructible_v;
130  using std::is_trivially_move_constructible_v;
131  using std::is_trivially_destructible_v;
132  using std::has_virtual_destructor_v;
133  using std::is_trivially_copyable_v;
134  using std::is_copy_constructible_v;
135  using std::is_object_v;
136  using std::is_volatile_v;
137  using std::is_const_v;
138  using std::is_same_v;
140 
141  using several::ArrayBucket;
142 
143 
148  template<typename TY>
149  size_t inline constexpr
151  {
152  size_t quant = alignof(TY);
153  size_t siz = max (sizeof(TY), quant);
154  size_t req = (siz/quant) * quant;
155  if (req < siz)
156  req += quant;
157  return req;
158  }
159 
161  size_t inline constexpr
162  alignRes (size_t alignment)
163  {
164  return positiveDiff (alignment, alignof(void*));
165  }
166  }//(End)helpers
167 
168 
169  namespace allo {// Allocation management policies
170 
178  template<class I, template<typename> class ALO>
180  : protected ALO<std::byte>
181  {
182  using Allo = ALO<std::byte>;
183  using AlloT = std::allocator_traits<Allo>;
184  using Bucket = ArrayBucket<I>;
185 
186  Allo& baseAllocator() { return *this; }
187 
188  template<typename X>
189  auto
190  adaptAllocator()
191  {
192  using XAllo = typename AlloT::template rebind_alloc<X>;
193  if constexpr (std::is_constructible_v<XAllo, Allo>)
194  return XAllo{baseAllocator()};
195  else
196  return XAllo{};
197  }
198 
199  public:
200  ElementFactory (Allo allo = Allo{})
201  : Allo{std::move (allo)}
202  { }
203 
205  template<typename X>
207  : ElementFactory{relatedFac.baseAllocator()}
208  { }
209 
210  template<typename O, template<typename> class XALO>
211  friend class ElementFactory;
212 
213 
214  Bucket*
215  create (size_t cnt, size_t spread, size_t alignment =alignof(I))
216  {
217  REQUIRE (cnt);
218  REQUIRE (spread);
219  size_t storageBytes = Bucket::storageOffset + cnt*spread;
220  storageBytes += alignRes (alignment); // over-aligned data => reserve for alignment padding
221  // Step-1 : acquire the raw storage buffer
222  std::byte* loc = AlloT::allocate (baseAllocator(), storageBytes);
223  ENSURE (0 == size_t(loc) % alignof(void*));
224 
225  size_t offset = (size_t(loc) + Bucket::storageOffset) % alignment;
226  if (offset) // padding needed to next aligned location
227  offset = alignment - offset;
228  offset += Bucket::storageOffset;
229  ASSERT (storageBytes - offset >= cnt*spread);
230  Bucket* bucket = reinterpret_cast<Bucket*> (loc);
231 
232  using BucketAlloT = typename AlloT::template rebind_traits<Bucket>;
233  auto bucketAllo = adaptAllocator<Bucket>();
234  // Step-2 : construct the Bucket metadata | ▽ ArrayBucket ctor arg ▽
235  try { BucketAlloT::construct (bucketAllo, bucket, storageBytes, offset, spread); }
236  catch(...)
237  {
238  AlloT::deallocate (baseAllocator(), loc, storageBytes);
239  throw;
240  }
241  return bucket;
242  };
243 
244 
245  template<class E, typename...ARGS>
246  E&
247  createAt (Bucket* bucket, size_t idx, ARGS&& ...args)
248  {
249  REQUIRE (bucket);
250  using ElmAlloT = typename AlloT::template rebind_traits<E>;
251  auto elmAllo = adaptAllocator<E>();
252  E* loc = reinterpret_cast<E*> (& bucket->subscript (idx));
253  ElmAlloT::construct (elmAllo, loc, forward<ARGS> (args)...);
254  ENSURE (loc);
255  return *loc;
256  };
257 
258 
259  template<class E>
260  void
261  destroy (ArrayBucket<I>* bucket)
262  {
263  REQUIRE (bucket);
264  if (bucket->isArmed())
265  { // ensure the bucket's destructor is invoked
266  // and in turn itself invokes this function
267  bucket->destroy();
268  return;
269  }
270  if (not is_trivially_destructible_v<E>)
271  {
272  size_t cnt = bucket->cnt;
273  using ElmAlloT = typename AlloT::template rebind_traits<E>;
274  auto elmAllo = adaptAllocator<E>();
275  for (size_t idx=0; idx<cnt; ++idx)
276  {
277  E* elm = reinterpret_cast<E*> (& bucket->subscript (idx));
278  ElmAlloT::destroy (elmAllo, elm);
279  }
280  }
281  size_t storageBytes = bucket->getAllocSize();
282  std::byte* loc = reinterpret_cast<std::byte*> (bucket);
283  AlloT::deallocate (baseAllocator(), loc, storageBytes);
284  };
285  };
286 
287 
294  template<class I, class E, template<typename> class ALO>
295  struct AllocationPolicy
296  : ElementFactory<I, ALO>
297  {
298  using Fac = ElementFactory<I, ALO>;
299  using Bucket = ArrayBucket<I>;
300 
301  using Fac::Fac; // pass-through ctor
302 
304  size_t static constexpr ALLOC_LIMIT = size_t(-1) / sizeof(E);
305 
307  bool canExpand(Bucket*, size_t){ return false; }
308 
309  Bucket*
310  realloc (Bucket* data, size_t cnt, size_t spread)
311  {
312  Bucket* newBucket = Fac::create (cnt, spread, alignof(E));
313  if (data)
314  try {
315  newBucket->installDestructor (data->getDtor());
316  size_t elms = min (cnt, data->cnt);
317  for (size_t idx=0; idx<elms; ++idx)
318  moveElem(idx, data, newBucket);
319  data->destroy();
320  }
321  catch(...)
322  { newBucket->destroy(); }
323  return newBucket;
324  }
325 
326  void
327  moveElem (size_t idx, Bucket* src, Bucket* tar)
328  {
329  if constexpr (is_trivially_copyable_v<E>)
330  {
331  void* oldPos = & src->subscript(idx);
332  void* newPos = & tar->subscript(idx);
333  size_t amount = min (src->spread, tar->spread);
334  std::memmove (newPos, oldPos, amount);
335  }
336  else
337  if constexpr (is_nothrow_move_constructible_v<E>
338  or is_copy_constructible_v<E>)
339  {
340  E& oldElm = reinterpret_cast<E&> (src->subscript (idx));
341  Fac::template createAt<E> (tar, idx
342  ,std::move_if_noexcept (oldElm));
343  }
344  else
345  {
346  (void)src; (void)tar;
347  NOTREACHED("realloc immovable type (neither trivially nor typed movable)");
348  // this alternative code section is very important, because it allows
349  // to instantiate this code even for »noncopyable« types, assuming that
350  // sufficient storage is reserved beforehand, and thus copying is irrelevant.
351  // For context: the std::vector impl. from libStdC++ is lacking this option.
352  }
353  tar->cnt = idx+1; // mark fill continuously for proper clean-up after exception
354  }
355  };
356 
358  template<class I, class E>
360 
361  }//(End) namespace several
362 
363 
364 
365 
366 
367  /*************************************************/
387  template<class I
388  ,class E =I
389  ,template<class,class> class POL =allo::HeapOwn
390  >
392  : private Several<I>
394  , POL<I,E>
395  {
396  using Coll = Several<I>;
397  using Policy = POL<I,E>;
398 
400  using Deleter = typename Bucket::Deleter;
401 
402  public:
403  SeveralBuilder() = default;
404 
406  template<typename...ARGS, typename = meta::enable_if<std::is_constructible<Policy,ARGS&&...>>>
407  SeveralBuilder (ARGS&& ...alloInit)
408  : Several<I>{}
409  , Policy{forward<ARGS> (alloInit)...}
410  { }
411 
413  Policy& policyConnect() { return *this; }
414 
415 
416 
417  /* ===== Builder API ===== */
418 
420  template<template<typename> class ALO =std::void_t
421  ,typename...ARGS>
422  auto withAllocator (ARGS&& ...args);
423 
424 
426  template<typename TY =E>
428  reserve (size_t cntElm =1
429  ,size_t elmSiz =reqSiz<TY>())
430  {
431  size_t extraElm = positiveDiff (cntElm, Coll::size());
432  ensureElementCapacity<TY> (elmSiz);
433  ensureStorageCapacity<TY> (elmSiz,extraElm);
434  elmSiz = max (elmSiz, Coll::spread());
435  adjustStorage (cntElm, elmSiz);
436  return move(*this);
437  }
438 
444  {
445  if (not Coll::empty()
446  or size() < capacity())
447  fitStorage();
448  return move(*this);
449  }
450 
452  template<typename VAL, typename...VALS>
454  append (VAL&& val, VALS&& ...vals)
455  {
456  emplace<VAL> (forward<VAL> (val));
457  if constexpr (0 < sizeof...(VALS))
458  return append (forward<VALS> (vals)...);
459  else
460  return move(*this);
461  }
462 
464  template<class IT>
466  appendAll (IT&& data)
467  {
468  explore(data).foreach ([this](auto it){ emplaceCopy(it); });
469  return move(*this);
470  }
471 
472  template<class X>
474  appendAll (std::initializer_list<X> ili)
475  {
476  using Val = typename meta::Strip<X>::TypeReferred;
477  for (Val const& x : ili)
478  emplaceNewElm<Val> (x);
479  return move(*this);
480  }
481 
483  template<typename...ARGS>
485  fillElm (size_t cntNew, ARGS&& ...args)
486  {
487  for ( ; 0<cntNew; --cntNew)
488  emplaceNewElm<E> (forward<ARGS> (args)...);
489  return move(*this);
490  }
491 
493  template<class TY, typename...ARGS>
495  emplace (ARGS&& ...args)
496  {
497  using Val = typename meta::Strip<TY>::TypeReferred;
498  emplaceNewElm<Val> (forward<ARGS> (args)...);
499  return move(*this);
500  }
501 
502 
503  /***********************************************************/
508  Several<I>
510  {
511  return move (*this);
512  }
513 
514  size_t size() const { return Coll::size(); }
515  bool empty() const { return Coll::empty();}
516  size_t capacity() const { return Coll::storageBuffSiz() / Coll::spread(); }
517  size_t capReserve() const { return capacity() - size(); }
518 
522  I&
523  operator[] (size_t idx)
524  {
525  if (idx >= Coll::size())
526  throw err::Invalid{_Fmt{"Access index %d >= size(%d)."}
527  % idx % Coll::size()
528  ,LERR_(INDEX_BOUNDS)
529  };
530  return Coll::operator[] (idx);
531  }
532 
533 
534  private: /* ========= Implementation of element placement ================ */
535  template<class IT>
536  void
537  emplaceCopy (IT& dataSrc)
538  {
539  using Val = typename IT::value_type;
540  emplaceNewElm<Val> (*dataSrc);
541  }
542 
543  template<class TY, typename...ARGS>
544  void
545  emplaceNewElm (ARGS&& ...args)
546  {
547  static_assert (is_object_v<TY> and not (is_const_v<TY> or is_volatile_v<TY>));
548 
549  probeMoveCapability<TY>(); // mark when target type is not (trivially) movable
550  ensureElementCapacity<TY>(); // sufficient or able to adapt spread
551  ensureStorageCapacity<TY>(); // sufficient or able to grow buffer
552 
553  size_t elmSiz = reqSiz<TY>();
554  size_t newPos = Coll::size();
555  size_t newCnt = Coll::empty()? INITIAL_ELM_CNT : newPos+1;
556  adjustStorage (newCnt, max (elmSiz, Coll::spread()));
557  ENSURE (Coll::data_);
558  ensureDeleter<TY>();
559  Policy::template createAt<TY> (Coll::data_, newPos, forward<ARGS> (args)...);
560  Coll::data_->cnt = newPos+1;
561  }
562 
566  template<class TY>
567  void
569  {
570  Deleter deleterFunctor = selectDestructor<TY>();
571  if (Coll::data_->isArmed()) return;
572  Coll::data_->installDestructor (move (deleterFunctor));
573  }
574 
576  template<class TY>
577  void
578  ensureElementCapacity (size_t requiredSiz =reqSiz<TY>())
579  {
580  if (Coll::spread() < requiredSiz and not (Coll::empty() or canWildMove()))
581  throw err::Invalid{_Fmt{"Unable to place element of type %s (size=%d)"
582  "into Several-container for element size %d."}
583  % util::typeStr<TY>() % requiredSiz % Coll::spread()};
584  }
585 
587  template<class TY>
588  void
589  ensureStorageCapacity (size_t requiredSiz =reqSiz<TY>(), size_t newElms =1)
590  {
591  if (not (Coll::empty()
592  or Coll::hasReserve (requiredSiz, newElms)
593  or Policy::canExpand (Coll::data_, requiredSiz*(Coll::size() + newElms))
594  or canDynGrow()))
595  throw err::Invalid{_Fmt{"Several-container is unable to accommodate further element of type %s; "
596  "storage reserve (%d bytes ≙ %d elms) exhausted and unable to move "
597  "elements of mixed unknown detail type, which are not trivially movable." }
598  % util::typeStr<TY>() % Coll::storageBuffSiz() % capacity()};
599  }
600 
601 
603  void
604  adjustStorage (size_t cnt, size_t spread)
605  {
606  size_t demand{cnt*spread};
607  size_t buffSiz{Coll::storageBuffSiz()};
608  if (demand == buffSiz)
609  return;
610  if (demand > buffSiz)
611  {// grow into exponentially expanded new allocation
612  if (spread > Coll::spread())
613  cnt = max (cnt, buffSiz / Coll::spread()); // retain reserve
614  size_t overhead = sizeof(Bucket) + alignRes(alignof(E));
615  size_t safetyLim = LUMIERA_MAX_ORDINAL_NUMBER * Coll::spread();
616  size_t expandAlloc = min (positiveDiff (min (safetyLim
617  ,Policy::ALLOC_LIMIT)
618  ,overhead)
619  ,max (2*buffSiz, cnt*spread));
620  // round down to an straight number of elements
621  size_t newCnt = expandAlloc / spread;
622  expandAlloc = newCnt * spread;
623  if (expandAlloc < demand)
624  throw err::State{_Fmt{"Storage expansion for Several-collection "
625  "exceeds safety limit of %d bytes"} % safetyLim
626  ,LERR_(SAFETY_LIMIT)};
627  // allocate new storage block...
628  Coll::data_ = Policy::realloc (Coll::data_, newCnt,spread);
629  }
630  ENSURE (Coll::data_);
631  if (canWildMove() and spread != Coll::spread())
632  adjustSpread (spread);
633  }
634 
635  void
636  fitStorage()
637  {
638  REQUIRE (not Coll::empty());
639  if (not (Policy::canExpand (Coll::data_, Coll::size())
640  or canDynGrow()))
641  throw err::Invalid{"Unable to shrink storage for Several-collection, "
642  "since at least one element can not be moved."};
643  Coll::data_ = Policy::realloc (Coll::data_, Coll::size(), Coll::spread());
644  }
645 
647  void
648  adjustSpread (size_t newSpread)
649  {
650  REQUIRE (Coll::data_);
651  REQUIRE (newSpread * Coll::size() <= Coll::storageBuffSiz());
652  size_t oldSpread = Coll::spread();
653  if (newSpread > oldSpread)
654  // need to spread out
655  for (size_t i=Coll::size()-1; 0<i; --i)
656  shiftStorage (i, oldSpread, newSpread);
657  else
658  // attempt to condense spread
659  for (size_t i=1; i<Coll::size(); ++i)
660  shiftStorage (i, oldSpread, newSpread);
661  // data elements now spaced by new spread
662  Coll::data_->spread = newSpread;
663  }
664 
665  void
666  shiftStorage (size_t idx, size_t oldSpread, size_t newSpread)
667  {
668  REQUIRE (idx);
669  REQUIRE (oldSpread);
670  REQUIRE (newSpread);
671  REQUIRE (Coll::data_);
672  byte* oldPos = Coll::data_->storage();
673  byte* newPos = oldPos;
674  oldPos += idx * oldSpread;
675  newPos += idx * newSpread;
676  std::memmove (newPos, oldPos, util::min (oldSpread,newSpread));
677  }
678 
679 
680 
681  /* ==== Logic do decide about possible element handling ==== */
682 
683  enum DestructionMethod{ UNKNOWN
684  , TRIVIAL
685  , ELEMENT
686  , VIRTUAL
687  };
688  static Literal
689  render (DestructionMethod m)
690  {
691  switch (m)
692  {
693  case TRIVIAL: return "trivial";
694  case ELEMENT: return "fixed-element-type";
695  case VIRTUAL: return "virtual-baseclass";
696  default:
697  throw err::Logic{"unknown DestructionMethod"};
698  }
699  }
700 
701  DestructionMethod destructor{UNKNOWN};
702  bool lock_move{false};
703 
704 
715  template<typename TY>
716  Deleter
718  {
719  typename Policy::Fac& factory(*this);
720 
721  if (is_Subclass<TY,I>() and has_virtual_destructor_v<I>)
722  {
723  __ensureMark<TY> (VIRTUAL);
724  return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<I> (bucket); };
725  }
726  if (is_trivially_destructible_v<TY>)
727  {
728  __ensureMark<TY> (TRIVIAL);
729  return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<TY> (bucket); };
730  }
731  if (is_same_v<TY,E> and is_Subclass<E,I>())
732  {
733  __ensureMark<TY> (ELEMENT);
734  return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<E> (bucket); };
735  }
736  throw err::Invalid{_Fmt{"Unsupported kind of destructor for element type %s."}
737  % util::typeStr<TY>()};
738  }
739 
740  template<typename TY>
741  void
742  __ensureMark (DestructionMethod requiredKind)
743  {
744  if (destructor != UNKNOWN and destructor != requiredKind)
745  throw err::Invalid{_Fmt{"Unable to handle (%s-)destructor for element type %s, "
746  "since this container has been primed to use %s-destructors."}
747  % render(requiredKind)
748  % util::typeStr<TY>()
749  % render(destructor)};
750  destructor = requiredKind;
751  }
752 
753 
757  template<typename TY>
758  void
760  {
761  if (not (is_same_v<TY,E> or is_trivially_copyable_v<TY>))
762  lock_move = true;
763  }
764 
765  bool
766  canWildMove()
767  {
768  return is_trivially_copyable_v<E> and not lock_move;
769  }
770 
771  bool
772  canDynGrow()
773  {
774  return not lock_move;
775  }
776  };
777 
778 
779 
780 
781  /* ===== Helpers and convenience-functions for creating SeveralBuilder ===== */
782 
783 
784  namespace allo { // Setup for custom allocator policies
785 
797  template<template<typename> class ALO, typename...ARGS>
798  struct SetupSeveral;
799 
801  template<template<typename> class ALO>
802  struct SetupSeveral<ALO>
803  {
804  template<class I, class E>
805  using Policy = AllocationPolicy<I,E,ALO>;
806  };
807 
810  template<template<typename> class ALO, typename X>
811  struct SetupSeveral<ALO, ALO<X>>
812  {
813  template<class I, class E>
814  struct Policy
815  : AllocationPolicy<I,E,ALO>
816  {
817  Policy (ALO<X> refAllocator)
818  : AllocationPolicy<I,E,ALO>(move(refAllocator))
819  { }
820  };
821  };
822  //
823  }//(End)Allocator configuration
824 
825 
826 
841  template<class I, class E, template<class,class> class POL>
842  template<template<typename> class ALO, typename...ARGS>
843  inline auto
845  {
846  if (not empty())
847  throw err::Logic{"lib::Several builder withAllocator() must be invoked "
848  "prior to adding any elements to the container"};
849 
850  using Setup = allo::SetupSeveral<ALO,ARGS...>;
851  using BuilderWithAllo = SeveralBuilder<I,E, Setup::template Policy>;
852 
853  return BuilderWithAllo(forward<ARGS> (args)...);
854  }
855 
856 
857 
858 
859 
860  /*********************************************************/
867  template<typename I, typename E =I>
870  {
871  return SeveralBuilder<I,E>{};
872  }
873 
874  template<typename X>
876  makeSeveral (std::initializer_list<X> ili)
877  {
878  return SeveralBuilder<X>{}
879  .reserve (ili.size())
880  .appendAll (ili);
881  }
882 
883 
884 } // namespace lib
885 #endif /*LIB_SEVERAL_BUILDER_H*/
void ensureStorageCapacity(size_t requiredSiz=reqSiz< TY >(), size_t newElms=1)
ensure sufficient storage reserve for newElms or verify the ability to re-allocate ...
SeveralBuilder && appendAll(IT &&data)
append a copy of all values exposed through an iterator
SeveralBuilder && reserve(size_t cntElm=1, size_t elmSiz=reqSiz< TY >())
ensure up-front that a desired capacity is allocated
Several< I > build()
Terminal Builder: complete and lock the collection contents.
void ensureElementCapacity(size_t requiredSiz=reqSiz< TY >())
ensure sufficient element capacity or the ability to adapt element spread
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
Definition: Setup.py:1
SeveralBuilder && emplace(ARGS &&...args)
create a new content element within the managed storage
Metadata record placed immediately before the data storage.
Definition: several.hpp:105
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:76
Types marked with this mix-in may be moved but not copied.
Definition: nocopy.hpp:49
Front-end for printf-style string template interpolation.
Extension point: how to configure the SeveralBuilder to use an allocator ALO, initialised by ARGS...
hard wired safety limits.
bool canExpand(Bucket *, size_t)
Extension point: able to adjust dynamically to the requested size?
A front-end for using printf-style formatting.
Abstraction interface: array-like random access by subscript.
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:83
SeveralBuilder< I, E > makeSeveral()
Entrance Point: start building a lib::Several instance.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Abstraction: Fixed array of elements.
Definition: several.hpp:156
SeveralBuilder && fillElm(size_t cntNew, ARGS &&...args)
emplace a number of elements of the defined element type E
Deleter selectDestructor()
Select a suitable method for invoking the element destructors and build a λ-object to be stored as de...
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
void adjustSpread(size_t newSpread)
move existing data to accommodate spread
Builder to create and populate a lib::Several<I>.
size_t constexpr alignRes(size_t alignment)
determine size of a reserve buffer to place with proper alignment
Lumiera error handling (C++ interface).
void probeMoveCapability()
mark that we&#39;re about to accept an otherwise unknown type, which can not be trivially moved...
SeveralBuilder && shrinkFit()
discard excess reserve capacity.
size_t constexpr reqSiz()
Helper to determine the »spread« required to hold elements of type TY in memory with proper alignment...
Building tree expanding and backtracking evaluations within hierarchical scopes.
ElementFactory(ElementFactory< X, ALO > &relatedFac)
allow cross-initialisation when using same kind of base allocator
verify compliance to an interface by subtype check
Definition: trait.hpp:323
Policy Mix-In used to adapt to the ElementFactory and Allocator.
void adjustStorage(size_t cnt, size_t spread)
possibly grow storage and re-arrange elements to accommodate desired capacity
Generic factory to manage objects within an ArrayBucket storage, delegating to a custom allocator ALO...
SeveralBuilder && append(VAL &&val, VALS &&...vals)
append copies of one or several arbitrary elements
SeveralBuilder(ARGS &&...alloInit)
start Several build using a custom allocator
void ensureDeleter()
ensure clean-up can be handled properly.
const uint INITIAL_ELM_CNT
number of storage slots to open initially; starting with an over-allocation similar to std::vector ...