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
|
|
|
|
|
** Some (library-) implementations of the RefArray interface.
|
|
|
|
|
**
|
|
|
|
|
** Being an array-like object exposing just a const ref, it is typically used
|
|
|
|
|
** on interfaces, and the type of the array "elements" usually is a ABC or interface.
|
|
|
|
|
** The actual implementation typically holds a subclass, and is either based on a vector,
|
|
|
|
|
** or a fixed storage contained within the implementation. The only price to pay is
|
|
|
|
|
** a virtual call on element access.
|
|
|
|
|
**
|
|
|
|
|
** For advanced uses it would be possible to have a pointer-array or even an embedded
|
|
|
|
|
** storage of variant-records, able to hold a mixture of subclasses. (the latter cases
|
|
|
|
|
** will be implemented when needed).
|
|
|
|
|
**
|
|
|
|
|
** @warning WIP and in rework 5/2025 -- not clear yet where this design leads to...
|
|
|
|
|
** @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-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-07 19:04:06 +02:00
|
|
|
using util::max;
|
|
|
|
|
using util::min;
|
|
|
|
|
using util::_Fmt;
|
2024-06-06 23:15:49 +02:00
|
|
|
|
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
|
|
|
static size_t
|
|
|
|
|
calcSize (size_t cnt, size_t spread)
|
|
|
|
|
{
|
|
|
|
|
return sizeof(Bucket) - sizeof(Bucket::storage)
|
|
|
|
|
+ cnt * spread;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
size_t storageBytes = calcSize (cnt, spread);
|
|
|
|
|
std::byte* loc = AlloT::allocate (baseAllocator(), storageBytes);
|
|
|
|
|
|
|
|
|
|
using BucketAlloT = typename AlloT::template rebind_traits<Bucket>;
|
|
|
|
|
auto bucketAllo = adaptAllocator<Bucket>();
|
2024-06-07 19:04:06 +02:00
|
|
|
try { BucketAlloT::construct (bucketAllo, reinterpret_cast<Bucket*> (loc), spread); }
|
2024-06-06 21:53:04 +02:00
|
|
|
catch(...)
|
|
|
|
|
{
|
|
|
|
|
AlloT::deallocate (baseAllocator(), loc, storageBytes);
|
|
|
|
|
throw;
|
|
|
|
|
}
|
2024-06-07 19:04:06 +02:00
|
|
|
return reinterpret_cast<Bucket*> (loc);
|
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>();
|
|
|
|
|
E* loc = & bucket->subscript (idx);
|
|
|
|
|
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-06 18:41:07 +02:00
|
|
|
destroy (ArrayBucket<I>* bucket, size_t size)
|
|
|
|
|
{
|
2024-06-06 21:13:50 +02:00
|
|
|
REQUIRE (bucket);
|
|
|
|
|
using ElmAlloT = typename AlloT::template rebind_traits<E>;
|
|
|
|
|
auto elmAllo = adaptAllocator<E>();
|
2024-06-06 18:41:07 +02:00
|
|
|
for (size_t i=0; i<size; ++i)
|
2024-06-06 21:13:50 +02:00
|
|
|
ElmAlloT::destroy (elmAllo, & bucket->subscript(i));
|
2024-06-06 18:41:07 +02:00
|
|
|
|
2024-06-07 19:04:06 +02:00
|
|
|
size_t storageBytes = calcSize (size, bucket->spread); ////////////////////////////////////OOO das ist naiv ... es kann mehr Storage sein
|
|
|
|
|
AlloT::deallocate (baseAllocator(), reinterpret_cast<std::byte*> (bucket), storageBytes);
|
2024-06-06 18:41:07 +02:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2024-06-06 23:15:49 +02:00
|
|
|
template<class I, template<typename> class ALO>
|
|
|
|
|
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-07 19:04:06 +02:00
|
|
|
Bucket*
|
|
|
|
|
realloc (Bucket* data, size_t cnt, size_t& storage, size_t demand)
|
2024-06-06 23:15:49 +02:00
|
|
|
{
|
2024-06-07 19:04:06 +02:00
|
|
|
if (demand == storage)
|
|
|
|
|
return data;
|
|
|
|
|
if (demand > storage)
|
|
|
|
|
{// grow into exponentially expanded new allocation
|
|
|
|
|
size_t spread = data? data->spread : sizeof(I);
|
|
|
|
|
size_t safetyLim = LUMIERA_MAX_ORDINAL_NUMBER * spread;
|
|
|
|
|
size_t expandAlloc = min (safetyLim
|
|
|
|
|
,max (2*storage, 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 = demand / spread;
|
|
|
|
|
if (newCnt * spread < demand) ++newCnt;
|
|
|
|
|
Bucket* newBucket = Fac::create (newCnt, spread);
|
|
|
|
|
// move (or copy) existing data...
|
|
|
|
|
ENSURE (data or cnt==0);
|
|
|
|
|
for (size_t i=0; i<cnt; ++i)
|
|
|
|
|
Fac::template createAt<I> (newBucket, i
|
|
|
|
|
,std::move_if_noexcept (data->subscript(i)));
|
|
|
|
|
////////////////////////////////////////////////////////OOO schee... aba mia brauchn E, ned I !!!!!
|
|
|
|
|
// discard old storage
|
|
|
|
|
if (data)
|
|
|
|
|
Fac::template destroy<I> (data, cnt); /////////////////////////////////////////////OOO Problem mit der überschüssigen Storage
|
|
|
|
|
storage = newCnt*spread;
|
|
|
|
|
return newBucket;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{// shrink into precisely fitting new allocation
|
|
|
|
|
Bucket* newBucket{nullptr};
|
|
|
|
|
if (cnt > 0)
|
|
|
|
|
{
|
|
|
|
|
REQUIRE (data);
|
|
|
|
|
newBucket = Fac::create (cnt, data->spread);
|
|
|
|
|
for (size_t i=0; i<cnt; ++i)
|
|
|
|
|
Fac::template createAt<I> (newBucket, i
|
|
|
|
|
,std::move_if_noexcept (data->subscript(i))); ////////////OOO selbes Problem: E hier
|
|
|
|
|
Fac::template destroy<I> (data, cnt); /////////////////////////////////////////////OOO nicht passende Storage!!
|
|
|
|
|
storage = cnt * data->spread;
|
|
|
|
|
}
|
|
|
|
|
return newBucket;
|
|
|
|
|
}
|
2024-06-06 23:15:49 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<class I>
|
|
|
|
|
using HeapOwn = AllocationPolicy<I, std::allocator>;
|
|
|
|
|
|
|
|
|
|
|
2024-06-04 23:24:11 +02:00
|
|
|
using std::is_trivially_move_constructible_v;
|
|
|
|
|
using std::is_trivially_destructible_v;
|
|
|
|
|
using std::has_virtual_destructor_v;
|
|
|
|
|
using std::is_same_v;
|
|
|
|
|
using lib::meta::is_Subclass;
|
|
|
|
|
|
|
|
|
|
template<class I, class E>
|
|
|
|
|
struct MemStrategy
|
|
|
|
|
{
|
|
|
|
|
bool disposable :1 ;
|
|
|
|
|
bool wild_move :1 ;
|
|
|
|
|
|
|
|
|
|
template<typename TY>
|
|
|
|
|
bool
|
|
|
|
|
canDestroy()
|
|
|
|
|
{
|
|
|
|
|
return disposable
|
|
|
|
|
or (is_trivially_destructible_v<TY> and is_trivially_destructible_v<I>)
|
|
|
|
|
or (has_virtual_destructor_v<I> and is_Subclass<TY,I>())
|
|
|
|
|
or (is_same_v<TY,E> and is_Subclass<E,I>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<typename TY>
|
|
|
|
|
bool
|
|
|
|
|
canDynGrow()
|
|
|
|
|
{
|
|
|
|
|
return is_same_v<TY,E>
|
|
|
|
|
or (is_trivially_move_constructible_v<TY> and wild_move);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto
|
|
|
|
|
getDeleter()
|
|
|
|
|
{
|
|
|
|
|
if constexpr (disposable or
|
|
|
|
|
(is_trivially_destructible_v<E> and is_trivially_destructible_v<I>))
|
|
|
|
|
return nullptr;
|
|
|
|
|
if constexpr (has_virtual_destructor_v<I>)
|
|
|
|
|
return nullptr;
|
|
|
|
|
else
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
};
|
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-07 19:04:06 +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)
|
2024-06-06 23:15:49 +02:00
|
|
|
,class POL =HeapOwn<I> ///< 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
|
|
|
|
|
|
|
|
size_t storageSiz_{0};
|
|
|
|
|
|
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)...}
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SeveralBuilder&&
|
|
|
|
|
reserve (size_t cntElm)
|
|
|
|
|
{
|
2024-06-07 19:04:06 +02:00
|
|
|
adjustStorage (cntElm, sizeof(E));
|
2024-05-28 18:52:01 +02:00
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class IT>
|
|
|
|
|
SeveralBuilder&&
|
|
|
|
|
appendAll (IT&& data)
|
|
|
|
|
{
|
|
|
|
|
explore(data).foreach ([this](auto it){ emplaceElm(it); });
|
|
|
|
|
return move(*this);
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void
|
|
|
|
|
adjustStorage (size_t cnt, size_t spread)
|
|
|
|
|
{
|
2024-06-07 19:04:06 +02:00
|
|
|
size_t demand{cnt*spread};
|
|
|
|
|
if (demand > storageSiz_)
|
2024-06-04 23:24:11 +02:00
|
|
|
{ // need more storage...
|
2024-06-07 19:04:06 +02:00
|
|
|
Coll::data_ = static_cast<typename Coll::Bucket> (POL::realloc (Coll::data_, Coll::size_, storageSiz_, demand));
|
2024-05-29 01:01:16 +02:00
|
|
|
}
|
2024-06-07 19:04:06 +02:00
|
|
|
ENSURE (Coll::data_);
|
|
|
|
|
if (spread != Coll::data_->spread)
|
2024-05-29 01:01:16 +02:00
|
|
|
adjustSpread (spread);
|
2024-06-07 19:04:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
fitStorage()
|
|
|
|
|
{
|
|
|
|
|
if (not Coll::data_) return;
|
|
|
|
|
size_t demand{Coll::size_ * Coll::data_->spread};
|
|
|
|
|
if (demand < storageSiz_)
|
2024-06-04 23:24:11 +02:00
|
|
|
{ // attempt to shrink storage
|
2024-06-07 19:04:06 +02:00
|
|
|
Coll::data_ = static_cast<typename Coll::Bucket> (POL::realloc (Coll::data_, Coll::size_, storageSiz_, demand));
|
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::size_);
|
|
|
|
|
REQUIRE (Coll::data_);
|
|
|
|
|
REQUIRE (newSpread * Coll::size_ <= storageSiz_);
|
|
|
|
|
size_t oldSpread = Coll::data_->spread;
|
2024-05-29 01:01:16 +02:00
|
|
|
if (newSpread > oldSpread)
|
|
|
|
|
// need to spread out
|
2024-06-07 19:04: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 19:04: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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class IT>
|
|
|
|
|
void
|
|
|
|
|
emplaceElm (IT& dataSrc)
|
|
|
|
|
{
|
|
|
|
|
using Val = typename IT::value_type;
|
|
|
|
|
size_t elmSiz = sizeof(Val);
|
2024-06-07 19:04:06 +02:00
|
|
|
adjustStorage (Coll::size_+1, requiredSpread(elmSiz));
|
2024-05-28 18:52:01 +02:00
|
|
|
UNIMPLEMENTED ("emplace data");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
|
requiredSpread (size_t elmSiz)
|
|
|
|
|
{
|
2024-06-07 19:04:06 +02:00
|
|
|
size_t currSpread = Coll::empty()? 0 : Coll::data_->spread;
|
2024-05-28 18:52:01 +02:00
|
|
|
return util::max (currSpread, elmSiz);
|
|
|
|
|
}
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace lib
|
|
|
|
|
#endif /*LIB_SEVERAL_BUILDER_H*/
|