91#ifndef LIB_SEVERAL_BUILDER_H
92#define LIB_SEVERAL_BUILDER_H
130 using std::is_nothrow_move_constructible_v;
131 using std::is_trivially_move_constructible_v;
132 using std::is_trivially_destructible_v;
133 using std::has_virtual_destructor_v;
134 using std::is_trivially_copyable_v;
135 using std::is_copy_constructible_v;
136 using std::is_object_v;
137 using std::is_volatile_v;
138 using std::is_const_v;
139 using std::is_same_v;
149 template<
typename TY>
150 inline constexpr size_t
153 size_t quant =
alignof(TY);
154 size_t siz = max (
sizeof(TY), quant);
155 size_t req = (siz/quant) * quant;
162 inline constexpr size_t
165 return positiveDiff (alignment,
alignof(
void*));
179 template<
class I,
template<
typename>
class ALO>
181 :
protected ALO<std::byte>
184 using AlloT = std::allocator_traits<Allo>;
188 using XAlloT = AlloT::template rebind_traits<std::remove_cv_t<X>>;
197 if constexpr (std::is_constructible_v<XAllo, Allo>)
214 template<
typename O,
template<
typename>
class XALO>
219 create (
size_t cnt,
size_t spread,
size_t alignment =
alignof(I))
223 size_t storageBytes = Bucket::storageOffset + cnt*spread;
224 storageBytes += alignRes (alignment);
226 std::byte* loc = AlloT::allocate (
baseAllocator(), storageBytes);
227 ENSURE (0 ==
size_t(loc) %
alignof(
void*));
229 size_t offset = (size_t(loc) + Bucket::storageOffset) % alignment;
231 offset = alignment - offset;
232 offset += Bucket::storageOffset;
233 ASSERT (storageBytes - offset >= cnt*spread);
237 auto bucketAllo = adaptAllocator<Bucket>();
239 try { BucketAlloT::construct (bucketAllo, bucket, storageBytes, offset, spread); }
249 template<
class E,
typename...ARGS>
255 auto elmAllo = adaptAllocator<E>();
256 E* loc =
reinterpret_cast<E*
> (& unConst(bucket->subscript (idx)));
257 ElmAlloT::construct (elmAllo, loc, forward<ARGS> (args)...);
268 if (bucket->isArmed())
274 if (not is_trivially_destructible_v<E>)
276 size_t cnt = bucket->cnt;
278 auto elmAllo = adaptAllocator<E>();
279 for (
size_t idx=0; idx<cnt; ++idx)
281 E* elm =
reinterpret_cast<E*
> (& unConst(bucket->subscript (idx)));
282 ElmAlloT::destroy (elmAllo, elm);
285 size_t storageBytes = bucket->getAllocSize();
286 std::byte* loc =
reinterpret_cast<std::byte*
> (bucket);
298 template<
class I,
class E,
template<
typename>
class ALO>
319 newBucket->installDestructor (data->getDtor());
320 size_t elms = min (cnt, data->cnt);
321 for (
size_t idx=0; idx<elms; ++idx)
326 { newBucket->destroy(); }
333 if constexpr (is_trivially_copyable_v<E>)
335 void* oldPos = & src->subscript(idx);
336 void* newPos = & tar->subscript(idx);
337 size_t amount = min (src->spread, tar->spread);
338 std::memmove (newPos, oldPos, amount);
341 if constexpr (is_nothrow_move_constructible_v<E>
342 or is_copy_constructible_v<E>)
344 E& oldElm =
reinterpret_cast<E&
> (src->subscript (idx));
345 Fac::template createAt<E> (tar, idx
346 ,std::move_if_noexcept (oldElm));
350 (void)src; (void)tar;
351 NOTREACHED(
"realloc immovable type (neither trivially nor typed movable)");
362 template<
class I,
class E>
413 ,
Policy{forward<ARGS> (alloInit)...}
424 template<
template<
typename>
class ALO =std::void_t
430 template<
typename TY =E>
433 ,
size_t elmSiz =reqSiz<TY>())
435 size_t extraElm = positiveDiff (cntElm, Coll::size());
436 ensureElementCapacity<TY> (elmSiz);
437 ensureStorageCapacity<TY> (elmSiz,extraElm);
438 elmSiz = max (elmSiz, Coll::spread());
449 if (not Coll::empty()
456 template<
typename VAL,
typename...VALS>
460 emplace<VAL> (forward<VAL> (val));
461 if constexpr (0 <
sizeof...(VALS))
462 return append (forward<VALS> (vals)...);
481 for (Val
const& x : ili)
482 emplaceNewElm<Val> (x);
496 template<
typename...ARGS>
500 for ( ; 0<cntNew; --cntNew)
501 emplaceNewElm<E> (forward<ARGS> (args)...);
506 template<
class TY,
typename...ARGS>
511 emplaceNewElm<Val> (forward<ARGS> (args)...);
527 size_t size()
const {
return Coll::size(); }
528 bool empty()
const {
return Coll::empty();}
529 size_t capacity()
const {
return Coll::storageBuffSiz() / Coll::spread(); }
538 if (idx >= Coll::size())
543 return Coll::operator[] (idx);
552 using Val = IT::value_type;
553 emplaceNewElm<Val> (*dataSrc);
560 using Val = IT::value_type;
561 emplaceNewElm<Val> (move (*dataSrc));
564 template<
class TY,
typename...ARGS>
568 static_assert (is_object_v<TY> and not (is_const_v<TY> or is_volatile_v<TY>));
570 probeMoveCapability<TY>();
571 ensureElementCapacity<TY>();
572 ensureStorageCapacity<TY>();
574 size_t elmSiz = reqSiz<TY>();
575 size_t newPos = Coll::size();
576 size_t newCnt = Coll::empty()? INITIAL_ELM_CNT : newPos+1;
578 ENSURE (Coll::data_);
580 Policy::template createAt<TY> (Coll::data_, newPos, forward<ARGS> (args)...);
581 Coll::data_->cnt = newPos+1;
591 Deleter deleterFunctor = selectDestructor<TY>();
592 if (Coll::data_->isArmed())
return;
593 Coll::data_->installDestructor (move (deleterFunctor));
601 if (Coll::spread() < requiredSiz and not (Coll::empty() or
canWildMove()))
603 "into Several-container for element size %d."}
604 % util::typeStr<TY>() % requiredSiz % Coll::spread()};
612 if (not (Coll::empty()
613 or Coll::hasReserve (requiredSiz, newElms)
614 or Policy::canExpand (Coll::data_, requiredSiz*(Coll::size() + newElms))
616 throw err::Invalid{
_Fmt{
"Several-container is unable to accommodate further element of type %s; "
617 "storage reserve (%d bytes ≙ %d elms) exhausted and unable to move "
618 "elements of mixed unknown detail type, which are not trivially movable." }
619 % util::typeStr<TY>() % Coll::storageBuffSiz() %
capacity()};
627 size_t demand{cnt*
spread};
628 size_t buffSiz{Coll::storageBuffSiz()};
629 if (demand == buffSiz)
631 if (demand > buffSiz)
633 if (
spread > Coll::spread())
634 cnt = max (cnt, buffSiz / Coll::spread());
635 size_t overhead =
sizeof(
Bucket) + alignRes(
alignof(E));
637 size_t expandAlloc = min (positiveDiff (min (safetyLim
638 ,Policy::ALLOC_LIMIT)
640 ,max (2*buffSiz, cnt*
spread));
642 size_t newCnt = expandAlloc /
spread;
643 expandAlloc = newCnt *
spread;
644 if (expandAlloc < demand)
646 "exceeds safety limit of %d bytes"} % safetyLim
647 ,
LERR_(SAFETY_LIMIT)};
649 Coll::data_ = Policy::realloc (Coll::data_, newCnt,
spread);
651 ENSURE (Coll::data_);
659 REQUIRE (not Coll::empty());
660 if (not (Policy::canExpand (Coll::data_, Coll::size())
662 throw err::Invalid{
"Unable to shrink storage for Several-collection, "
663 "since at least one element can not be moved."};
664 Coll::data_ = Policy::realloc (Coll::data_, Coll::size(), Coll::spread());
671 REQUIRE (Coll::data_);
672 REQUIRE (newSpread * Coll::size() <= Coll::storageBuffSiz());
673 size_t oldSpread = Coll::spread();
674 if (newSpread > oldSpread)
676 for (
size_t i=Coll::size()-1; 0<i; --i)
680 for (
size_t i=1; i<Coll::size(); ++i)
683 Coll::data_->spread = newSpread;
692 REQUIRE (Coll::data_);
693 byte* oldPos = Coll::data_->storage();
694 byte* newPos = oldPos;
695 oldPos += idx * oldSpread;
696 newPos += idx * newSpread;
697 std::memmove (newPos, oldPos,
util::min (oldSpread,newSpread));
714 case TRIVIAL:
return "trivial";
715 case ELEMENT:
return "fixed-element-type";
716 case VIRTUAL:
return "virtual-baseclass";
718 throw err::Logic{
"unknown DestructionMethod"};
736 template<
typename TY>
744 typename Policy::Fac& factory(*
this);
746 if (is_Subclass<TVal,IVal>() and has_virtual_destructor_v<IVal>)
749 return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<IVal> (bucket); };
751 if (is_trivially_destructible_v<TVal>)
754 return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<TVal> (bucket); };
756 if (is_same_v<TVal,EVal> and is_Subclass<EVal,IVal>())
759 return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<EVal> (bucket); };
761 throw err::Invalid{
_Fmt{
"Unsupported kind of destructor for element type %s."}
762 % util::typeStr<TY>()};
765 template<
typename TY>
770 throw err::Invalid{
_Fmt{
"Unable to handle (%s-)destructor for element type %s, "
771 "since this container has been primed to use %s-destructors."}
773 % util::typeStr<TY>()
782 template<
typename TY>
789 if (not (is_same_v<TVal,EVal> or is_trivially_copyable_v<TVal>))
797 return is_trivially_copyable_v<EVal> and not
lock_move;
826 template<
template<
typename>
class ALO,
typename...ARGS>
830 template<
template<
typename>
class ALO>
833 template<
class I,
class E>
839 template<
template<
typename>
class ALO,
typename X>
842 template<
class I,
class E>
870 template<
class I,
class E,
template<
class,
class>
class POL>
871 template<
template<
typename>
class ALO,
typename...ARGS>
876 throw err::Logic{
"lib::Several builder withAllocator() must be invoked "
877 "prior to adding any elements to the container"};
882 return BuilderWithAllo(forward<ARGS> (args)...);
896 template<
typename I,
typename E =I>
Builder to create and populate a lib::Several<I>.
void emplaceCopy(IT &dataSrc)
void shiftStorage(size_t idx, size_t oldSpread, size_t newSpread)
several::ArrayBucket< I > Bucket
auto withAllocator(ARGS &&...args)
cross-builder to use a custom allocator for the lib::Several container
SeveralBuilder && fillElm(size_t cntNew, ARGS &&...args)
emplace a number of elements of the defined element type E
size_t capReserve() const
SeveralBuilder && shrinkFit()
discard excess reserve capacity.
void adjustSpread(size_t newSpread)
move existing data to accommodate spread
SeveralBuilder(ARGS &&...alloInit)
start Several build using a custom allocator
static Literal render(DestructionMethod m)
void emplaceNewElm(ARGS &&...args)
void adjustStorage(size_t cnt, size_t spread)
possibly grow storage and re-arrange elements to accommodate desired capacity
I & operator[](size_t idx)
allow to peek into data emplaced thus far...
Policy & policyConnect()
expose policy to configure other ServeralBuilder
SeveralBuilder && appendAll(IT &&data)
append a copy of all values exposed through an iterator
SeveralBuilder && moveAll(SEQ &dataSrc)
consume all values exposed through an iterator by moving into the builder
void emplaceMove(IT &dataSrc)
SeveralBuilder && emplace(ARGS &&...args)
create a new content element within the managed storage
void probeMoveCapability()
mark that we're about to accept an otherwise unknown type, which can not be trivially moved.
void ensureElementCapacity(size_t requiredSiz=reqSiz< TY >())
ensure sufficient element capacity or the ability to adapt element spread
SeveralBuilder && append(VAL &&val, VALS &&...vals)
append copies of one or several arbitrary elements
Deleter selectDestructor()
Select a suitable method for invoking the element destructors and build a λ-object to be stored as de...
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 && 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 ensureDeleter()
ensure clean-up can be handled properly.
DestructionMethod destructor
SeveralBuilder && appendAll(std::initializer_list< X > ili)
void __ensureMark(DestructionMethod requiredKind)
Abstraction: Fixed array of elements.
Generic factory to manage objects within an ArrayBucket<I> storage, delegating to a custom allocator ...
E & createAt(Bucket *bucket, size_t idx, ARGS &&...args)
ElementFactory(ElementFactory< X, ALO > &relatedFac)
allow cross-initialisation when using same kind of base allocator
std::allocator_traits< Allo > AlloT
ElementFactory(Allo allo=Allo{})
void destroy(ArrayBucket< I > *bucket)
Bucket * create(size_t cnt, size_t spread, size_t alignment=alignof(I))
AlloT::template rebind_traits< std::remove_cv_t< X > > XAlloT
Derived specific exceptions within Lumiera's exception hierarchy.
Types marked with this mix-in may be moved but not copied.
A front-end for using printf-style formatting.
Lumiera error handling (C++ interface).
Building tree expanding and backtracking evaluations within hierarchical scopes.
hard wired safety limits.
#define LUMIERA_MAX_ORDINAL_NUMBER
AllocationPolicy< I, E, ALO > Policy
AllocationPolicy< I, E, std::allocator > HeapOwn
Default configuration to use heap memory for lib::Several.
Extension point: how to configure the SeveralBuilder to use an allocator ALO, initialised by ARGS.
const uint INITIAL_ELM_CNT
number of storage slots to open initially; starting with an over-allocation similar to std::vector
constexpr size_t alignRes(size_t alignment)
determine size of a reserve buffer to place with proper alignment
constexpr size_t reqSiz()
Helper to determine the »spread« required to hold elements of type TY in memory with proper alignment...
Implementation namespace for support and library code.
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
SeveralBuilder< I, E > makeSeveral()
Entrance Point: start building a lib::Several instance.
LumieraError< LERR_(STATE)> State
LumieraError< LERR_(LOGIC)> Logic
constexpr UN positiveDiff(N2 newVal, UN refVal)
OBJ * unConst(const OBJ *)
shortcut to save some typing when having to define const and non-const variants of member functions
Abstraction interface: array-like random access by subscript.
Policy Mix-In used to adapt to the ElementFactory and Allocator.
Bucket * realloc(Bucket *data, size_t cnt, size_t spread)
void moveElem(size_t idx, Bucket *src, Bucket *tar)
bool canExpand(Bucket *, size_t)
Extension point: able to adjust dynamically to the requested size?
static size_t constexpr ALLOC_LIMIT
by default assume that memory is practically unlimited...
Policy(ALO< X > refAllocator)
Metadata record placed immediately before the data storage.
std::function< void(ArrayBucket *)> Deleter
Helpers for type detection, type rewriting and metaprogramming.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...