2024-05-12 19:05:50 +02:00
|
|
|
/*
|
|
|
|
|
SEVERAL-BUILDER.hpp - builder for a limited fixed collection of elements
|
|
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
2024, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU General Public License as
|
|
|
|
|
published by the Free Software Foundation; either version 2 of
|
|
|
|
|
the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/** @file several-builder.hpp
|
2024-06-11 00:57:24 +02:00
|
|
|
** Builder to create and populate instances of the lib::Several container.
|
|
|
|
|
** For mere usage, inclusion of several.hpp should be sufficient, since the
|
|
|
|
|
** container front-end is generic and intends to hide most details of allocation
|
|
|
|
|
** and element placement. It is an array-like container, but may hold subclass
|
|
|
|
|
** elements, while exposing only a reference to the interface type.
|
2024-05-12 19:05:50 +02:00
|
|
|
**
|
2024-06-11 00:57:24 +02:00
|
|
|
** # Implementation data layout
|
2024-05-12 19:05:50 +02:00
|
|
|
**
|
2024-06-11 00:57:24 +02:00
|
|
|
** The front-end container lib::Several<I> is actually just a smart-ptr referring
|
|
|
|
|
** to the actual data storage, which resides within an _array bucket._ Typically
|
|
|
|
|
** the latter is placed into memory managed by a custom allocator, most notably
|
|
|
|
|
** lib::AllocationCluster. However, by default, the ArrayBucket<I> will be placed
|
|
|
|
|
** into heap memory. All further meta information is also maintained alongside
|
|
|
|
|
** this data allocation, including a _deleter function_ to invoke all element
|
|
|
|
|
** destructors and de-allocate the bucket itself. Neither the type of the
|
|
|
|
|
** actual elements, nor the type of the allocator is revealed.
|
2024-05-12 19:05:50 +02:00
|
|
|
**
|
2024-06-11 00:57:24 +02:00
|
|
|
** Since the actual data elements can (optionally) be of a different type than
|
|
|
|
|
** the exposed interface type \a I, additional storage and spacing is required
|
|
|
|
|
** in the element array. The field ArrayBucket<I>::spread defines this spacing
|
|
|
|
|
** and thus the offset used for subscript access.
|
|
|
|
|
**
|
|
|
|
|
** @todo this is a first implementation solution from 6/2025 — and was deemed
|
|
|
|
|
** _roughly adequate_ at that time, yet should be revalidated once more
|
|
|
|
|
** observations pertaining real-world usage are available...
|
2024-05-12 19:05:50 +02:00
|
|
|
** @see several-builder-test.cpp
|
|
|
|
|
**
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LIB_SEVERAL_BUILDER_H
|
|
|
|
|
#define LIB_SEVERAL_BUILDER_H
|
|
|
|
|
|
|
|
|
|
|
2024-06-07 19:04:06 +02:00
|
|
|
#include "lib/error.hpp"
|
2024-05-12 19:05:50 +02:00
|
|
|
#include "lib/several.hpp"
|
2024-06-07 19:04:06 +02:00
|
|
|
#include "include/limits.hpp"
|
2024-05-28 18:52:01 +02:00
|
|
|
#include "lib/iter-explorer.hpp"
|
2024-06-07 19:04:06 +02:00
|
|
|
#include "lib/format-string.hpp"
|
2024-05-28 18:52:01 +02:00
|
|
|
#include "lib/util.hpp"
|
2024-05-12 19:05:50 +02:00
|
|
|
|
2024-06-04 23:24:11 +02:00
|
|
|
#include <type_traits>
|
2024-06-09 17:19:40 +02:00
|
|
|
#include <functional>
|
2024-05-29 01:01:16 +02:00
|
|
|
#include <cstring>
|
2024-05-28 18:07:08 +02:00
|
|
|
#include <utility>
|
2024-05-12 19:05:50 +02:00
|
|
|
#include <vector>
|
2024-05-28 18:07:08 +02:00
|
|
|
|
2024-05-12 19:05:50 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
2024-06-07 19:04:06 +02:00
|
|
|
namespace err = lumiera::error;
|
|
|
|
|
|
2024-05-28 18:52:01 +02:00
|
|
|
using std::vector;
|
|
|
|
|
using std::forward;
|
|
|
|
|
using std::move;
|
2024-05-29 01:01:16 +02:00
|
|
|
using std::byte;
|
2024-05-12 19:05:50 +02:00
|
|
|
|
2024-05-29 01:01:16 +02:00
|
|
|
namespace {// Allocation management policies
|
2024-05-28 17:20:34 +02:00
|
|
|
|
2024-06-08 23:59:47 +02:00
|
|
|
/** number of storage slots to open initially;
|
|
|
|
|
* starting with an over-allocation similar to `std::vector`
|
|
|
|
|
*/
|
|
|
|
|
const uint INITIAL_ELM_CNT = 10;
|
|
|
|
|
|
|
|
|
|
|
2024-06-07 19:04:06 +02:00
|
|
|
using util::max;
|
|
|
|
|
using util::min;
|
|
|
|
|
using util::_Fmt;
|
2024-06-11 02:48:23 +02:00
|
|
|
using std::is_trivially_move_constructible_v;
|
2024-06-09 23:45:24 +02:00
|
|
|
using std::is_trivially_destructible_v;
|
2024-06-11 02:48:23 +02:00
|
|
|
using std::has_virtual_destructor_v;
|
|
|
|
|
using std::is_trivially_copyable_v;
|
2024-06-11 23:22:00 +02:00
|
|
|
using std::is_object_v;
|
|
|
|
|
using std::is_volatile_v;
|
|
|
|
|
using std::is_const_v;
|
2024-06-11 02:48:23 +02:00
|
|
|
using std::is_same_v;
|
|
|
|
|
using lib::meta::is_Subclass;
|
|
|
|
|
|
2024-06-06 23:15:49 +02:00
|
|
|
|
2024-06-11 00:57:24 +02:00
|
|
|
/**
|
|
|
|
|
* Helper to determine the »spread« required to hold
|
|
|
|
|
* elements of type \a TY in memory _with proper alignment._
|
|
|
|
|
* @warning assumes that the start of the buffer is also suitably aligned,
|
|
|
|
|
* which _may not be the case_ for **over-aligned objects** with
|
|
|
|
|
* `alignof(TY) > alignof(void*)`
|
|
|
|
|
*/
|
|
|
|
|
template<typename TY>
|
|
|
|
|
size_t inline constexpr
|
|
|
|
|
reqSiz()
|
|
|
|
|
{
|
|
|
|
|
size_t quant = alignof(TY);
|
|
|
|
|
size_t siz = max (sizeof(TY), quant);
|
|
|
|
|
size_t req = (siz/quant) * quant;
|
|
|
|
|
if (req < siz)
|
|
|
|
|
req += quant;
|
|
|
|
|
return req;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-06 21:13:50 +02:00
|
|
|
template<class I, template<typename> class ALO>
|
2024-06-06 21:53:04 +02:00
|
|
|
class ElementFactory
|
2024-06-06 21:13:50 +02:00
|
|
|
: private ALO<std::byte>
|
2024-06-06 18:41:07 +02:00
|
|
|
{
|
2024-06-06 21:13:50 +02:00
|
|
|
using Allo = ALO<std::byte>;
|
2024-06-06 18:41:07 +02:00
|
|
|
using AlloT = std::allocator_traits<Allo>;
|
2024-06-06 21:53:04 +02:00
|
|
|
using Bucket = ArrayBucket<I>;
|
2024-06-06 18:41:07 +02:00
|
|
|
|
2024-06-06 21:13:50 +02:00
|
|
|
Allo& baseAllocator() { return *this; }
|
|
|
|
|
|
2024-06-06 18:41:07 +02:00
|
|
|
template<typename X>
|
2024-06-06 21:13:50 +02:00
|
|
|
auto
|
2024-06-06 18:41:07 +02:00
|
|
|
adaptAllocator()
|
|
|
|
|
{
|
|
|
|
|
using XAllo = typename AlloT::template rebind_alloc<X>;
|
|
|
|
|
if constexpr (std::is_constructible_v<XAllo, Allo>)
|
2024-06-06 21:13:50 +02:00
|
|
|
return XAllo{baseAllocator()};
|
2024-06-06 18:41:07 +02:00
|
|
|
else
|
|
|
|
|
return XAllo{};
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-06 21:53:04 +02:00
|
|
|
public:
|
2024-06-06 21:13:50 +02:00
|
|
|
ElementFactory (Allo allo = Allo{})
|
|
|
|
|
: Allo{std::move (allo)}
|
|
|
|
|
{ }
|
|
|
|
|
|
2024-06-06 21:53:04 +02:00
|
|
|
Bucket*
|
|
|
|
|
create (size_t cnt, size_t spread)
|
|
|
|
|
{
|
2024-06-07 22:50:24 +02:00
|
|
|
size_t storageBytes = Bucket::requiredStorage (cnt, spread);
|
2024-06-06 21:53:04 +02:00
|
|
|
std::byte* loc = AlloT::allocate (baseAllocator(), storageBytes);
|
2024-06-07 22:50:24 +02:00
|
|
|
Bucket* bucket = reinterpret_cast<Bucket*> (loc);
|
2024-06-06 21:53:04 +02:00
|
|
|
|
|
|
|
|
using BucketAlloT = typename AlloT::template rebind_traits<Bucket>;
|
|
|
|
|
auto bucketAllo = adaptAllocator<Bucket>();
|
2024-06-10 18:52:25 +02:00
|
|
|
try { BucketAlloT::construct (bucketAllo, bucket, cnt*spread, spread); }
|
2024-06-06 21:53:04 +02:00
|
|
|
catch(...)
|
|
|
|
|
{
|
|
|
|
|
AlloT::deallocate (baseAllocator(), loc, storageBytes);
|
|
|
|
|
throw;
|
|
|
|
|
}
|
2024-06-07 22:50:24 +02:00
|
|
|
return bucket;
|
2024-06-06 21:53:04 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<class E, typename...ARGS>
|
|
|
|
|
E&
|
|
|
|
|
createAt (Bucket* bucket, size_t idx, ARGS&& ...args)
|
|
|
|
|
{
|
|
|
|
|
REQUIRE (bucket);
|
|
|
|
|
using ElmAlloT = typename AlloT::template rebind_traits<E>;
|
|
|
|
|
auto elmAllo = adaptAllocator<E>();
|
2024-06-11 18:23:48 +02:00
|
|
|
E* loc = reinterpret_cast<E*> (& bucket->subscript (idx));
|
2024-06-06 21:53:04 +02:00
|
|
|
ElmAlloT::construct (elmAllo, loc, forward<ARGS> (args)...);
|
|
|
|
|
ENSURE (loc);
|
|
|
|
|
return *loc;
|
|
|
|
|
};
|
|
|
|
|
|
2024-06-06 21:13:50 +02:00
|
|
|
template<class E>
|
|
|
|
|
void
|
2024-06-07 22:50:24 +02:00
|
|
|
destroy (ArrayBucket<I>* bucket)
|
2024-06-06 18:41:07 +02:00
|
|
|
{
|
2024-06-06 21:13:50 +02:00
|
|
|
REQUIRE (bucket);
|
2024-06-09 23:45:24 +02:00
|
|
|
if (not is_trivially_destructible_v<E>)
|
|
|
|
|
{
|
|
|
|
|
size_t cnt = bucket->cnt;
|
|
|
|
|
using ElmAlloT = typename AlloT::template rebind_traits<E>;
|
|
|
|
|
auto elmAllo = adaptAllocator<E>();
|
|
|
|
|
for (size_t idx=0; idx<cnt; ++idx)
|
2024-06-11 18:23:48 +02:00
|
|
|
{
|
|
|
|
|
E* elm = reinterpret_cast<E*> (& bucket->subscript (idx));
|
|
|
|
|
ElmAlloT::destroy (elmAllo, elm);
|
|
|
|
|
}
|
2024-06-09 23:45:24 +02:00
|
|
|
}
|
2024-06-10 18:52:25 +02:00
|
|
|
size_t storageBytes = Bucket::requiredStorage (bucket->buffSiz);
|
2024-06-07 22:50:24 +02:00
|
|
|
std::byte* loc = reinterpret_cast<std::byte*> (bucket);
|
|
|
|
|
AlloT::deallocate (baseAllocator(), loc, storageBytes);
|
2024-06-06 18:41:07 +02:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2024-06-08 21:58:04 +02:00
|
|
|
template<class I, class E, template<typename> class ALO>
|
2024-06-06 23:15:49 +02:00
|
|
|
struct AllocationPolicy
|
|
|
|
|
: ElementFactory<I, ALO>
|
|
|
|
|
{
|
|
|
|
|
using Fac = ElementFactory<I, ALO>;
|
2024-06-07 19:04:06 +02:00
|
|
|
using Bucket = ArrayBucket<I>;
|
|
|
|
|
|
|
|
|
|
using Fac::Fac; // pass-through ctor
|
2024-06-06 23:15:49 +02:00
|
|
|
|
2024-06-08 01:51:32 +02:00
|
|
|
const bool isDisposable{false}; ///< memory must be explicitly deallocated
|
2024-06-08 17:35:14 +02:00
|
|
|
|
|
|
|
|
bool canExpand(size_t){ return false; }
|
2024-06-08 01:51:32 +02:00
|
|
|
|
2024-06-07 19:04:06 +02:00
|
|
|
Bucket*
|
2024-06-08 22:44:07 +02:00
|
|
|
realloc (Bucket* data, size_t cnt, size_t spread)
|
2024-06-06 23:15:49 +02:00
|
|
|
{
|
2024-06-08 22:44:07 +02:00
|
|
|
Bucket* newBucket = Fac::create (cnt, spread);
|
|
|
|
|
if (data)
|
2024-06-09 17:19:40 +02:00
|
|
|
try {
|
|
|
|
|
newBucket->deleter = data->deleter;
|
|
|
|
|
size_t elms = min (cnt, data->cnt);
|
|
|
|
|
for (size_t idx=0; idx<elms; ++idx)
|
|
|
|
|
moveElem(idx, data, newBucket);
|
|
|
|
|
data->destroy();
|
|
|
|
|
}
|
|
|
|
|
catch(...)
|
|
|
|
|
{ newBucket->destroy(); }
|
2024-06-08 22:44:07 +02:00
|
|
|
return newBucket;
|
2024-06-06 23:15:49 +02:00
|
|
|
}
|
2024-06-08 22:44:07 +02:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
moveElem (size_t idx, Bucket* src, Bucket* tar)
|
|
|
|
|
{
|
|
|
|
|
if constexpr (is_trivially_copyable_v<E>)
|
|
|
|
|
{
|
|
|
|
|
void* oldPos = & src->subscript(idx);
|
|
|
|
|
void* newPos = & tar->subscript(idx);
|
|
|
|
|
size_t amount = min (src->spread, tar->spread);
|
|
|
|
|
std::memmove (newPos, oldPos, amount);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-06-12 03:23:20 +02:00
|
|
|
E& oldElm = reinterpret_cast<E&> (src->subscript (idx));
|
2024-06-08 22:44:07 +02:00
|
|
|
Fac::template createAt<E> (tar, idx
|
2024-06-12 03:23:20 +02:00
|
|
|
,std::move_if_noexcept (oldElm));
|
2024-06-08 22:44:07 +02:00
|
|
|
}
|
2024-06-09 23:45:24 +02:00
|
|
|
tar->cnt = idx+1; // mark fill continuously for proper clean-up after exception
|
2024-06-08 22:44:07 +02:00
|
|
|
}
|
2024-06-06 23:15:49 +02:00
|
|
|
};
|
|
|
|
|
|
2024-06-08 21:58:04 +02:00
|
|
|
template<class I, class E>
|
|
|
|
|
using HeapOwn = AllocationPolicy<I, E, std::allocator>;
|
2024-06-06 23:15:49 +02:00
|
|
|
|
2024-06-11 02:48:23 +02:00
|
|
|
}//(End)implementation details
|
|
|
|
|
|
2024-05-28 17:20:34 +02:00
|
|
|
|
2024-05-12 19:05:50 +02:00
|
|
|
/**
|
|
|
|
|
* Wrap a vector holding objects of a subtype and
|
|
|
|
|
* provide array-like access using the interface type.
|
|
|
|
|
*/
|
2024-06-08 21:58:04 +02:00
|
|
|
template<class I ///< Interface or base type visible on resulting Several<I>
|
|
|
|
|
,class E =I ///< a subclass element element type (relevant when not trivially movable and destructible)
|
|
|
|
|
,class POL =HeapOwn<I,E> ///< Allocator policy
|
2024-06-04 23:24:11 +02:00
|
|
|
>
|
2024-05-28 04:03:51 +02:00
|
|
|
class SeveralBuilder
|
2024-05-28 17:20:34 +02:00
|
|
|
: Several<I>
|
2024-06-06 18:41:07 +02:00
|
|
|
, util::MoveOnly
|
2024-05-28 17:20:34 +02:00
|
|
|
, POL
|
2024-05-12 19:05:50 +02:00
|
|
|
{
|
2024-06-07 19:04:06 +02:00
|
|
|
using Coll = Several<I>;
|
2024-05-28 18:52:01 +02:00
|
|
|
|
2024-06-11 02:48:23 +02:00
|
|
|
using Bucket = ArrayBucket<I>;
|
|
|
|
|
using Deleter = typename Bucket::Deleter;
|
2024-06-08 01:51:32 +02:00
|
|
|
|
2024-05-12 19:05:50 +02:00
|
|
|
public:
|
2024-05-28 18:52:01 +02:00
|
|
|
SeveralBuilder() = default;
|
|
|
|
|
|
|
|
|
|
/** start Several build using a custom allocator */
|
|
|
|
|
template<typename...ARGS, typename = meta::enable_if<std::is_constructible<POL,ARGS...>>>
|
|
|
|
|
SeveralBuilder (ARGS&& ...alloInit)
|
|
|
|
|
: Several<I>{}
|
|
|
|
|
, POL{forward<ARGS> (alloInit)...}
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
2024-06-11 02:48:23 +02:00
|
|
|
/* ===== Builder API ===== */
|
|
|
|
|
|
2024-05-28 18:52:01 +02:00
|
|
|
SeveralBuilder&&
|
|
|
|
|
reserve (size_t cntElm)
|
|
|
|
|
{
|
2024-06-11 00:57:24 +02:00
|
|
|
adjustStorage (cntElm, reqSiz<E>());
|
2024-05-28 18:52:01 +02:00
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 23:22:00 +02:00
|
|
|
template<typename VAL, typename...VALS>
|
|
|
|
|
SeveralBuilder&&
|
|
|
|
|
append (VAL&& val, VALS&& ...vals)
|
|
|
|
|
{
|
|
|
|
|
emplace<VAL> (forward<VAL> (val));
|
|
|
|
|
if constexpr (0 < sizeof...(VALS))
|
|
|
|
|
return append (forward<VALS> (vals)...);
|
|
|
|
|
else
|
|
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 18:52:01 +02:00
|
|
|
template<class IT>
|
|
|
|
|
SeveralBuilder&&
|
|
|
|
|
appendAll (IT&& data)
|
|
|
|
|
{
|
2024-06-08 01:51:32 +02:00
|
|
|
explore(data).foreach ([this](auto it){ emplaceCopy(it); });
|
2024-05-28 18:52:01 +02:00
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 18:23:48 +02:00
|
|
|
template<class X>
|
|
|
|
|
SeveralBuilder&&
|
|
|
|
|
appendAll (std::initializer_list<X> ili)
|
|
|
|
|
{
|
|
|
|
|
using Val = typename meta::Strip<X>::TypeReferred;
|
|
|
|
|
for (Val const& x : ili)
|
|
|
|
|
emplaceNewElm<Val> (x);
|
|
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename...ARGS>
|
|
|
|
|
SeveralBuilder&&
|
|
|
|
|
fillElm (size_t cntNew, ARGS&& ...args)
|
|
|
|
|
{
|
|
|
|
|
for ( ; 0<cntNew; --cntNew)
|
2024-06-12 03:23:20 +02:00
|
|
|
emplaceNewElm<E> (forward<ARGS> (args)...);
|
2024-06-11 18:23:48 +02:00
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class TY, typename...ARGS>
|
|
|
|
|
SeveralBuilder&&
|
|
|
|
|
emplace (ARGS&& ...args)
|
|
|
|
|
{
|
2024-06-11 23:22:00 +02:00
|
|
|
using Val = typename meta::Strip<TY>::TypeReferred;
|
2024-06-11 18:23:48 +02:00
|
|
|
emplaceNewElm<Val> (forward<ARGS> (args)...);
|
|
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 02:48:23 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Terminal Builder: complete and lock the collection contents.
|
|
|
|
|
* @note the SeveralBuilder is sliced away, effectively
|
|
|
|
|
* returning only the pointer to the ArrayBucket.
|
|
|
|
|
*/
|
2024-05-28 04:03:51 +02:00
|
|
|
Several<I>
|
|
|
|
|
build()
|
2024-05-12 19:05:50 +02:00
|
|
|
{
|
2024-05-28 18:07:08 +02:00
|
|
|
return move (*this);
|
2024-05-12 19:05:50 +02:00
|
|
|
}
|
2024-05-28 18:52:01 +02:00
|
|
|
|
2024-06-11 18:23:48 +02:00
|
|
|
size_t size() const { return Coll::size(); }
|
|
|
|
|
bool empty() const { return Coll::empty();}
|
|
|
|
|
|
|
|
|
|
|
2024-05-28 18:52:01 +02:00
|
|
|
private:
|
2024-06-08 23:59:47 +02:00
|
|
|
template<class IT>
|
|
|
|
|
void
|
|
|
|
|
emplaceCopy (IT& dataSrc)
|
|
|
|
|
{
|
2024-06-11 18:23:48 +02:00
|
|
|
using Val = typename IT::value_type;
|
|
|
|
|
emplaceNewElm<Val> (*dataSrc);
|
2024-06-08 23:59:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class TY, typename...ARGS>
|
|
|
|
|
void
|
2024-06-11 18:23:48 +02:00
|
|
|
emplaceNewElm (ARGS&& ...args)
|
2024-06-08 23:59:47 +02:00
|
|
|
{
|
2024-06-11 23:22:00 +02:00
|
|
|
static_assert (is_object_v<TY> and not (is_const_v<TY> or is_volatile_v<TY>));
|
|
|
|
|
|
2024-06-08 23:59:47 +02:00
|
|
|
// mark when target type is not trivially movable
|
2024-06-11 02:48:23 +02:00
|
|
|
probeMoveCapability<TY>();
|
2024-06-08 23:59:47 +02:00
|
|
|
|
|
|
|
|
// ensure sufficient element capacity or the ability to adapt element spread
|
2024-06-11 02:48:23 +02:00
|
|
|
if (Coll::spread() < reqSiz<TY>() and not (Coll::empty() or canWildMove()))
|
2024-06-08 23:59:47 +02:00
|
|
|
throw err::Invalid{_Fmt{"Unable to place element of type %s (size=%d)"
|
|
|
|
|
"into container for element size %d."}
|
2024-06-11 00:57:24 +02:00
|
|
|
% util::typeStr<TY>() % reqSiz<TY>() % Coll::spread()};
|
2024-06-08 23:59:47 +02:00
|
|
|
|
2024-06-09 23:45:24 +02:00
|
|
|
// ensure sufficient storage or verify the ability to re-allocate
|
2024-06-11 18:23:48 +02:00
|
|
|
if (not (Coll::empty() or Coll::hasReserve(reqSiz<TY>())
|
2024-06-11 00:57:24 +02:00
|
|
|
or POL::canExpand(reqSiz<TY>())
|
2024-06-11 02:48:23 +02:00
|
|
|
or canDynGrow()))
|
2024-06-09 23:45:24 +02:00
|
|
|
throw err::Invalid{_Fmt{"Unable to accommodate further element of type %s "}
|
|
|
|
|
% util::typeStr<TY>()};
|
|
|
|
|
|
2024-06-11 00:57:24 +02:00
|
|
|
size_t elmSiz = reqSiz<TY>();
|
2024-06-08 23:59:47 +02:00
|
|
|
size_t newPos = Coll::size();
|
|
|
|
|
size_t newCnt = Coll::empty()? INITIAL_ELM_CNT : newPos+1;
|
|
|
|
|
adjustStorage (newCnt, max (elmSiz, Coll::spread()));
|
2024-06-09 17:19:40 +02:00
|
|
|
ENSURE (Coll::data_);
|
2024-06-09 23:45:24 +02:00
|
|
|
ensureDeleter<TY>();
|
2024-06-08 23:59:47 +02:00
|
|
|
POL::template createAt<TY> (Coll::data_, newPos, forward<ARGS> (args)...);
|
2024-06-09 23:45:24 +02:00
|
|
|
Coll::data_->cnt = newPos+1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class TY>
|
|
|
|
|
void
|
|
|
|
|
ensureDeleter()
|
|
|
|
|
{
|
|
|
|
|
// ensure clean-up can be handled properly
|
2024-06-11 02:48:23 +02:00
|
|
|
Deleter deleterFunctor = selectDestructor<TY>();
|
2024-06-09 23:45:24 +02:00
|
|
|
if (Coll::data_->deleter) return;
|
|
|
|
|
Coll::data_->deleter = deleterFunctor;
|
2024-06-08 23:59:47 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-28 18:52:01 +02:00
|
|
|
void
|
|
|
|
|
adjustStorage (size_t cnt, size_t spread)
|
|
|
|
|
{
|
2024-06-07 19:04:06 +02:00
|
|
|
size_t demand{cnt*spread};
|
2024-06-08 23:59:47 +02:00
|
|
|
size_t buffSiz{Coll::data_? Coll::data_->buffSiz : 0};
|
|
|
|
|
if (demand == buffSiz)
|
|
|
|
|
return;
|
|
|
|
|
if (demand > buffSiz)
|
|
|
|
|
{// grow into exponentially expanded new allocation
|
|
|
|
|
size_t safetyLim = LUMIERA_MAX_ORDINAL_NUMBER * Coll::spread();
|
|
|
|
|
size_t expandAlloc = min (safetyLim
|
|
|
|
|
,max (2*buffSiz, demand));
|
|
|
|
|
if (expandAlloc < demand)
|
|
|
|
|
throw err::State{_Fmt{"Storage expansion for Several-collection "
|
|
|
|
|
"exceeds safety limit of %d bytes"} % safetyLim
|
|
|
|
|
,LERR_(SAFETY_LIMIT)};
|
|
|
|
|
// allocate new storage block...
|
|
|
|
|
size_t newCnt = expandAlloc / spread;
|
|
|
|
|
if (newCnt * spread < expandAlloc) ++newCnt;
|
|
|
|
|
Coll::data_ = POL::realloc (Coll::data_, newCnt,spread);
|
|
|
|
|
}
|
2024-06-07 19:04:06 +02:00
|
|
|
ENSURE (Coll::data_);
|
2024-06-11 02:48:23 +02:00
|
|
|
if (canWildMove() and spread != Coll::spread())
|
2024-05-29 01:01:16 +02:00
|
|
|
adjustSpread (spread);
|
2024-06-07 19:04:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
fitStorage()
|
|
|
|
|
{
|
2024-06-11 02:48:23 +02:00
|
|
|
if (not Coll::data)
|
2024-06-08 23:59:47 +02:00
|
|
|
return;
|
2024-06-11 02:48:23 +02:00
|
|
|
if (not canDynGrow())
|
|
|
|
|
throw err::Invalid{"Unable to shrink storage for Several-collection, "
|
|
|
|
|
"since at least one element can not be moved."};
|
2024-06-08 23:59:47 +02:00
|
|
|
Coll::data_ = POL::realloc (Coll::data_, Coll::size(), Coll::spread());
|
2024-05-29 01:01:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** move existing data to accommodate spread */
|
|
|
|
|
void
|
|
|
|
|
adjustSpread (size_t newSpread)
|
|
|
|
|
{
|
2024-06-07 19:04:06 +02:00
|
|
|
REQUIRE (Coll::data_);
|
2024-06-07 23:02:51 +02:00
|
|
|
REQUIRE (newSpread * Coll::size() <= Coll::data_->buffSiz);
|
|
|
|
|
size_t oldSpread = Coll::spread();
|
2024-05-29 01:01:16 +02:00
|
|
|
if (newSpread > oldSpread)
|
|
|
|
|
// need to spread out
|
2024-06-07 22:23:06 +02:00
|
|
|
for (size_t i=Coll::size()-1; 0<i; --i)
|
2024-05-29 01:01:16 +02:00
|
|
|
shiftStorage (i, oldSpread, newSpread);
|
|
|
|
|
else
|
|
|
|
|
// attempt to condense spread
|
2024-06-07 22:23:06 +02:00
|
|
|
for (size_t i=1; i<Coll::size(); ++i)
|
2024-05-29 01:01:16 +02:00
|
|
|
shiftStorage (i, oldSpread, newSpread);
|
|
|
|
|
// data elements now spaced by new spread
|
2024-06-07 19:04:06 +02:00
|
|
|
Coll::data_->spread = newSpread;
|
2024-05-29 01:01:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
shiftStorage (size_t idx, size_t oldSpread, size_t newSpread)
|
|
|
|
|
{
|
|
|
|
|
REQUIRE (idx);
|
|
|
|
|
REQUIRE (oldSpread);
|
|
|
|
|
REQUIRE (newSpread);
|
2024-06-07 19:04:06 +02:00
|
|
|
REQUIRE (Coll::data_);
|
|
|
|
|
byte* oldPos = Coll::data_->storage;
|
2024-05-29 01:01:16 +02:00
|
|
|
byte* newPos = oldPos;
|
|
|
|
|
oldPos += idx * oldSpread;
|
|
|
|
|
newPos += idx * newSpread;
|
|
|
|
|
std::memmove (newPos, oldPos, util::min (oldSpread,newSpread));
|
2024-05-28 18:52:01 +02:00
|
|
|
}
|
2024-06-11 02:48:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ==== Logic do decide about possible element handling ==== */
|
|
|
|
|
|
|
|
|
|
enum DestructionMethod{ UNKNOWN
|
|
|
|
|
, TRIVIAL
|
|
|
|
|
, ELEMENT
|
|
|
|
|
, VIRTUAL
|
|
|
|
|
};
|
|
|
|
|
static Literal
|
|
|
|
|
render (DestructionMethod m)
|
|
|
|
|
{
|
|
|
|
|
switch (m)
|
|
|
|
|
{
|
|
|
|
|
case TRIVIAL: return "trivial";
|
|
|
|
|
case ELEMENT: return "fixed-element-type";
|
|
|
|
|
case VIRTUAL: return "virtual-baseclass";
|
|
|
|
|
default:
|
|
|
|
|
throw err::Logic{"unknown DestructionMethod"};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DestructionMethod destructor{UNKNOWN};
|
|
|
|
|
bool lock_move{false};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Select a suitable method for invoking the element destructors
|
|
|
|
|
* and build a λ-object to be stored as deleter function alongside
|
|
|
|
|
* with the data; this includes a _copy_ of the embedded allocator,
|
|
|
|
|
* which in many cases is a monostate empty base class.
|
|
|
|
|
* @note this collection is _primed_ by the first element added,
|
|
|
|
|
* causing to lock into one of the possible destructor schemes;
|
|
|
|
|
* the reason is, we do not retain the information of the individual
|
|
|
|
|
* element types and thus we must employ one coherent scheme for all.
|
|
|
|
|
*/
|
|
|
|
|
template<typename TY>
|
|
|
|
|
Deleter
|
|
|
|
|
selectDestructor ()
|
|
|
|
|
{
|
|
|
|
|
typename POL::Fac& factory(*this);
|
|
|
|
|
|
|
|
|
|
if (is_Subclass<TY,I>() and has_virtual_destructor_v<I>)
|
|
|
|
|
{
|
|
|
|
|
__ensureMark<TY> (VIRTUAL);
|
|
|
|
|
return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<I> (bucket); };
|
|
|
|
|
}
|
|
|
|
|
if (is_trivially_destructible_v<TY>)
|
|
|
|
|
{
|
|
|
|
|
__ensureMark<TY> (TRIVIAL);
|
|
|
|
|
return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<TY> (bucket); };
|
|
|
|
|
}
|
|
|
|
|
if (is_same_v<TY,E> and is_Subclass<E,I>())
|
|
|
|
|
{
|
|
|
|
|
__ensureMark<TY> (ELEMENT);
|
|
|
|
|
return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<E> (bucket); };
|
|
|
|
|
}
|
|
|
|
|
throw err::Invalid{_Fmt{"Unsupported kind of destructor for element type %s."}
|
|
|
|
|
% util::typeStr<TY>()};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename TY>
|
|
|
|
|
void
|
2024-06-12 00:57:57 +02:00
|
|
|
__ensureMark (DestructionMethod requiredKind)
|
2024-06-11 02:48:23 +02:00
|
|
|
{
|
2024-06-12 00:57:57 +02:00
|
|
|
if (destructor != UNKNOWN and destructor != requiredKind)
|
|
|
|
|
throw err::Invalid{_Fmt{"Unable to handle (%s-)destructor for element type %s, "
|
|
|
|
|
"since this container has been primed to use %s-destructors."}
|
|
|
|
|
% render(requiredKind)
|
|
|
|
|
% util::typeStr<TY>()
|
|
|
|
|
% render(destructor)};
|
|
|
|
|
destructor = requiredKind;
|
2024-06-11 02:48:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** mark that we're about to accept an otherwise unknown type,
|
|
|
|
|
* which can not be trivially moved. This irrevocably disables
|
|
|
|
|
* relocations by low-level `memove` for this container instance */
|
|
|
|
|
template<typename TY>
|
|
|
|
|
void
|
|
|
|
|
probeMoveCapability()
|
|
|
|
|
{
|
|
|
|
|
if (not (is_same_v<TY,E> or is_trivially_copyable_v<TY>))
|
|
|
|
|
lock_move = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
canWildMove()
|
|
|
|
|
{
|
|
|
|
|
return is_trivially_copyable_v<E> and not lock_move;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
canDynGrow()
|
|
|
|
|
{
|
|
|
|
|
return not lock_move;
|
|
|
|
|
}
|
2024-05-12 19:05:50 +02:00
|
|
|
};
|
|
|
|
|
|
2024-05-28 18:52:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-05-28 04:03:51 +02:00
|
|
|
template<typename X>
|
|
|
|
|
SeveralBuilder<X>
|
|
|
|
|
makeSeveral (std::initializer_list<X> ili)
|
|
|
|
|
{
|
2024-05-28 18:52:01 +02:00
|
|
|
return SeveralBuilder<X>{}
|
|
|
|
|
.reserve (ili.size())
|
|
|
|
|
.appendAll (ili);
|
2024-05-28 04:03:51 +02:00
|
|
|
}
|
2024-05-12 19:05:50 +02:00
|
|
|
|
2024-06-11 23:53:38 +02:00
|
|
|
template<typename I, typename E =I>
|
|
|
|
|
SeveralBuilder<I,E>
|
|
|
|
|
makeSeveral()
|
|
|
|
|
{
|
|
|
|
|
return SeveralBuilder<I,E>{};
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-12 19:05:50 +02:00
|
|
|
|
|
|
|
|
} // namespace lib
|
|
|
|
|
#endif /*LIB_SEVERAL_BUILDER_H*/
|