Library: start design draft to replace RefArray

Some decisions
 - use a single template with policy base
 - population via separate builder class
 - implemented similar to vector (start/end)
 - but able to hold larger (subclass) objects
This commit is contained in:
Fischlurch 2024-05-28 04:03:51 +02:00
parent e4f91ecb4d
commit 73dd24ecef
4 changed files with 171 additions and 262 deletions

View file

@ -57,119 +57,24 @@ namespace lib {
* Wrap a vector holding objects of a subtype and
* provide array-like access using the interface type.
*/
template<class B, class IM = B>
class RefArrayVectorWrapper
: public RefArray<B>
template<class I>
class SeveralBuilder
{
typedef vector<IM> const& Tab;
Tab table_;
public:
RefArrayVectorWrapper (Tab toWrap)
: table_(toWrap)
{ }
virtual size_t size() const
Several<I>
build()
{
return table_.size();
}
virtual B const& operator[] (size_t i) const
{
REQUIRE (i < size());
return table_[i];
UNIMPLEMENTED ("materialise into Several-Container");
}
};
/**
* This variation of the wrapper actually
* \em is a vector, but can act as a RefArray
*/
template<class B, class IM = B>
class RefArrayVector
: public vector<IM>,
public RefArrayVectorWrapper<B,IM>
{
typedef RefArrayVectorWrapper<B,IM> Wrap;
typedef vector<IM> Vect;
typedef typename Vect::size_type Size_t;
typedef typename Vect::value_type Val_t;
public:
RefArrayVector() : Vect(), Wrap((Vect&)*this) {}
RefArrayVector(Size_t n, Val_t const& v = Val_t()) : Vect(n,v), Wrap((Vect&)*this) {}
RefArrayVector(Vect const& ref) : Vect(ref), Wrap((Vect&)*this) {}
using Vect::size;
using Wrap::operator[];
};
/**
* RefArray implementation based on a fixed size array,
* i.e. the storage is embedded. Embedded subclass obj
* either need to be default constructible or be
* placed directly by a factory
*/
template<class B, size_t n, class IM = B>
class RefArrayTable
: public RefArray<B>
{
char storage_[n*sizeof(IM)];
IM* array_;
public:
RefArrayTable() ///< objects created in-place by default ctor
: array_ (reinterpret_cast<IM*> (&storage_))
{
size_t i=0;
try
{
while (i<n)
new(&array_[i++]) IM();
}
catch(...) { cleanup(i); throw; }
}
template<class FAC>
RefArrayTable(FAC& factory) ///< objects created in-place by factory
: array_ (reinterpret_cast<IM*> (&storage_))
{
size_t i=0;
try
{
while (i<n)
factory(&array_[i++]);
}
catch(...) { cleanup(i-1); throw; } // destroy finished part, without the failed object
}
~RefArrayTable() { cleanup(); }
private:
void cleanup(size_t top=n) noexcept
{
while (top) array_[--top].~IM();
}
public: //-----RefArray-Interface------------
virtual size_t size() const
{
return n;
}
virtual B const& operator[] (size_t i) const
{
REQUIRE (i < size());
return array_[i];
}
};
template<typename X>
SeveralBuilder<X>
makeSeveral (std::initializer_list<X> ili)
{
UNIMPLEMENTED ("start building a Several-Container");
}
} // namespace lib

View file

@ -45,18 +45,43 @@ namespace lib {
* Typically the return type is an interface,
* and the Implementation wraps some datastructure
* holding subclasses.
* @todo ouch -- a collection that isn't iterable... ///////////////////////TICKET #1040
* @todo ouch -- a collection that isn't iterable... ///////////////////////TICKET #1040 //////////OOO cookie jar
* @warning in rework 5/2025
*/
template<class T>
class RefArray
: util::NonCopyable
template<class I>
class Several
: util::MoveOnly
{
public:
virtual ~RefArray() {} ///< this is an interface
virtual T const& operator[] (size_t i) const =0;
virtual size_t size() const =0;
size_t
size() const
{
UNIMPLEMENTED ("determine storage size");
}
I&
operator[] (size_t i)
{
UNIMPLEMENTED ("subscript");
}
I&
back()
{
UNIMPLEMENTED ("storage access");
}
using iterator = I*;
using const_iterator = I const*;
iterator begin() { UNIMPLEMENTED ("iteration"); }
iterator end() { UNIMPLEMENTED ("iteration"); }
const_iterator begin() const { UNIMPLEMENTED ("iteration"); }
const_iterator end() const { UNIMPLEMENTED ("iteration"); }
friend auto begin (Several const& svl) { return svl.begin();}
friend auto end (Several const& svl) { return svl.end(); }
};

View file

@ -27,70 +27,64 @@
#include "lib/test/run.hpp"
#include "lib/test/testdummy.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/test/diagnostic-output.hpp"////////////////TODO
#include "lib/iter-explorer.hpp"
#include "lib/format-util.hpp"
#include "lib/util.hpp"
#include "lib/several-builder.hpp"
#include <vector>
#include <array>
using ::test::Test;
using std::vector;
using std::rand;
using lib::explore;
using util::join;
namespace lib {
namespace test{
namespace { // test types
namespace { // diagnostic test types
struct I
/**
* Instance tracking sub-dummy
* - implements the Dummy interface
* - holds additional storage
* - specific implementation of the virtual operation
* - includes content of the additional storage into the
* checksum calculation, allowing to detect memory corruption
*/
template<uint i>
class Num
: public test::Dummy
{
virtual int op(int i) const =0;
virtual ~I() {}
};
struct Sub1 : I
{
int offs_;
std::array<int,i> ext_;
Sub1 (int o=1) : offs_(o) {}
int op (int i) const { return i+offs_; }
};
struct Sub2 : I
{
const char* letterz_;
Sub2() : letterz_("ABCDEFGHIJKLMNOPQRSTUVWXYZ") {}
int op (int i) const { return (int)letterz_[i % 26]; }
};
struct Sub3 : I
{
int id_;
static long sum;
static long trigger;
Sub3(int id) : id_(id)
public:
Num (uint seed=i)
: Dummy{seed}
{
sum +=id_;
if ( id_ == trigger )
throw trigger; // fail while in construction
ext_.fill(seed);
setVal ((i+1)*seed);
}
~Sub3()
{
sum -=id_;
~Num()
{
setVal (getVal() - explore(ext_).resultSum());
}
long
acc (int ii) override
{
return i+ii + explore(ext_).resultSum();
}
int op (int i) const { return i + id_; }
};
long Sub3::sum = 0;
long Sub3::trigger = -1;
} // (END) test types
@ -100,15 +94,11 @@ namespace test{
/***************************************************************//**
* @test build several wrappers, each based on a different storage,
* all providing RefArray access to a given vector. The rationale
* for RefArray is to expose just the interface: the data structure
* within the actual implementation holds subclass instances of
* the specified interface.
* - RefArrayVectorWrapper is a ref to an existing vector
* - RefArrayVector subclasses std::vector
* - RefArrayTable holds a fix sized table, i.e. embedded storage
*
* @test use lib::Several to establish small collections of elements,
* possibly with sub-classing and controlled allocation.
* - the container is populated through a separate builder
* - the number of elements is flexible during population
* - the actual container allows random-access via base interface
* @see several-builder.hpp
*/
class SeveralBuilder_test : public Test
@ -117,122 +107,62 @@ namespace test{
virtual void
run (Arg)
{
checkWrapper();
checkVector();
checkTable();
checkTable_inplaceCreation();
checkTable_errorHandling();
simpleUsage();
check_Builder();
check_ErrorHandling();
check_ElementAccess();
check_CustomAllocator();
}
/** @test TODO demonstrate basic behaviour
* @todo WIP 5/24 🔁 define implement
*/
void
checkWrapper()
simpleUsage()
{
vector<Sub2> subz(10);
RefArrayVectorWrapper<I,Sub2> subWrap (subz);
RefArray<I> & rArr (subWrap);
CHECK (subWrap.size()==subz.size());
CHECK (INSTANCEOF(I, &rArr[0]));
for (size_t i=0; i<rArr.size(); ++i)
{
CHECK (&rArr[i] == &subz[i]);
CHECK (rArr[i].op(i) == subz[i].op(i));
}
auto elms = makeSeveral({1,1,2,3,5,8,13}).build();
CHECK (elms.size() == 7);
CHECK (elms.back() == 13);
CHECK (elms[3] == 3);
CHECK (join (elms,"-") == "1-1-2-3-5-8-13"_expect);
}
/** @test TODO various ways to build an populate the container
* @todo WIP 5/24 🔁 define implement
*/
void
checkVector()
check_Builder()
{
RefArrayVector<I,Sub2> subz(10);
vector<Sub2> & vect (subz);
RefArray<I> & rArr (subz);
CHECK (vect.size()==subz.size());
CHECK (INSTANCEOF(I, &rArr[0]));
for (size_t i=0; i<rArr.size(); ++i)
{
CHECK (&rArr[i] == &vect[i]);
CHECK (rArr[i].op(i) == vect[i].op(i));
}
}
#define ADR(OBJ) (ulong)&(OBJ)
/** @test TODO proper handling of exceptions during population
* @todo WIP 5/24 🔁 define implement
*/
void
checkTable()
check_ErrorHandling()
{
RefArrayTable<I,20,Sub1> tab;
// creates 20 Sub1-objects in-place
// which are indeed located within the object
CHECK (sizeof(tab) >= 20 * sizeof(Sub1));
CHECK (ADR(tab) < ADR(tab[19]) && ADR(tab[19]) < ADR(tab) + sizeof(tab));
RefArray<I> & rArr (tab);
CHECK (20 == tab.size());
CHECK (INSTANCEOF(I, &rArr[0]));
for (size_t i=0; i<rArr.size(); ++i)
{
CHECK (i*sizeof(Sub1) == ADR(rArr[i]) - ADR(rArr[0]) ); // indeed array-like storage
CHECK (int(i+1) == rArr[i].op(i)); // check the known result
}
}
template<class SUB>
struct Fac ///< fabricating a series of subclass instances with varying ctor parameter
{
int offset_;
Fac ( ) : offset_ (0) {}
void operator() (void* place)
{
CHECK (place);
new(place) SUB (offset_++); // note: varying ctor parameter
}
};
/** @test TODO verify access operations on the actual container
* @todo WIP 5/24 🔁 define implement
*/
void
checkTable_inplaceCreation()
check_ElementAccess()
{
Fac<Sub1> theFact;
RefArrayTable<I,30,Sub1> tab (theFact);
RefArray<I> & rArr (tab);
CHECK (30 == tab.size());
for (size_t i=0; i<rArr.size(); ++i)
CHECK (int(i+i) == rArr[i].op(i)); // each one has gotten another offset ctor parameter
}
/** @test TODO demonstrate integration with a custom allocator
* @todo WIP 5/24 🔁 define implement
*/
void
checkTable_errorHandling()
check_CustomAllocator()
{
for (uint i=0; i<500; ++i)
{
Sub3::sum = 0;
Sub3::trigger = (rand() % 50); // when hitting the trigger Sub3 throws
try
{
{
Fac<Sub3> factory;
RefArrayTable<I,30,Sub3> table (factory);
CHECK (Sub3::sum == (29+1)*29/2);
}
CHECK (Sub3::sum == 0);
}
catch(long id)
{
CHECK (id == Sub3::trigger);
CHECK (Sub3::sum == id);
// meaning: all objects have been cleaned up,
// with the exception of the one hitting the trigger
} }
}
};

View file

@ -81442,15 +81442,18 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</html></richcontent>
</node>
<node CREATED="1715620143727" ID="ID_828708828" MODIFIED="1715620159863" TEXT="Zugriff auf den Allocator per DI (statisch) oder Thread-local"/>
<node CREATED="1715620242056" ID="ID_427769562" MODIFIED="1715620649844" TEXT="Spezieller Allocator mit Block-de-Allocation">
<node CREATED="1715620242056" ID="ID_427769562" MODIFIED="1716856637040" TEXT="Spezieller Allocator mit Block-de-Allocation">
<richcontent TYPE="NOTE"><html>
<head/>
<head>
</head>
<body>
<p>
...man verwendet <i>nur speziell im produktiven Einsatz im Node-Graph</i>&#160; einen besonderen Allocator, der zwar den Destruktor aufruf, aber den Speicher nicht freigibt; alloziert wird immer in einen kompakten Block hinein, der dann auf der Basis der Proze&#223;-Kenntnis als Ganzes verworfen und neu verwendet wird.
...man verwendet <i>nur speziell im produktiven Einsatz im Node-Graph</i>&#160; einen besonderen Allocator, der zwar den Destruktor aufruft, aber den Speicher nicht freigibt; alloziert wird immer in einen kompakten Block hinein, der dann auf der Basis der Proze&#223;-Kenntnis als Ganzes verworfen und neu verwendet wird.
</p>
</body>
</html></richcontent>
</html>
</richcontent>
<icon BUILTIN="forward"/>
</node>
</node>
@ -81661,9 +81664,56 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715627195825" ID="ID_40203233" MODIFIED="1715627201745" TEXT="Details festlegen">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1716856805949" ID="ID_792128264" MODIFIED="1716856808446" TEXT="Design">
<node CREATED="1716856809343" ID="ID_1193014237" MODIFIED="1716856820503" TEXT="der Allocator wird &#xfc;ber eine Policy eingebunden">
<node CREATED="1716856840256" ID="ID_817075746" MODIFIED="1716856864012" TEXT="per Default ist das std::allocator"/>
<node CREATED="1716856892993" ID="ID_299290154" MODIFIED="1716856907499" TEXT="es gibt eine explizite Spezialisierung f&#xfc;r AllocationCluster"/>
<node CREATED="1716856915174" ID="ID_872073061" MODIFIED="1716856955511" TEXT="die Builder-Policy steuert auch eine Detail-Policy in den eigentlichen Container-Typ"/>
</node>
</node>
<node CREATED="1716857432809" ID="ID_976896851" MODIFIED="1716857436700" TEXT="API">
<node CREATED="1716861302691" ID="ID_346534745" MODIFIED="1716861305151" TEXT="Builder">
<node CREATED="1716861306163" ID="ID_1889956528" MODIFIED="1716861327876" TEXT="kann man direkt konstruieren"/>
<node CREATED="1716861328512" ID="ID_533852363" MODIFIED="1716861336547" TEXT="convenience front-end: makeSeveral">
<node CREATED="1716861337631" ID="ID_1982319827" MODIFIED="1716861345282" TEXT="kann Typ deduzieren"/>
</node>
</node>
<node CREATED="1716861357884" ID="ID_417347881" MODIFIED="1716861368151" TEXT="Access">
<node CREATED="1716861369635" ID="ID_282709475" MODIFIED="1716861396747" TEXT="const-correctness &#x27fa; sch&#xfc;tzt die Elemente"/>
<node CREATED="1716861397663" ID="ID_834481286" MODIFIED="1716861403175" TEXT="Subscript-Operator"/>
<node CREATED="1716861404218" ID="ID_773669668" MODIFIED="1716861408113" TEXT="front() und back()"/>
<node CREATED="1716861408733" ID="ID_526137450" MODIFIED="1716861420560" TEXT="Iteration per Lumiera-Iterator"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716861706141" ID="ID_32527710" MODIFIED="1716861721964" TEXT="knifflige technische Details">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716861725635" ID="ID_1822785959" MODIFIED="1716861768215" TEXT="festlegen des Element-Spread">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1716861791476" ID="ID_1153264423" MODIFIED="1716861797994" TEXT="Zielkonflikt">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1716861799273" ID="ID_521198508" MODIFIED="1716861807996" TEXT="man m&#xf6;chte Elemente verdrahten"/>
<node CREATED="1716861808632" ID="ID_361446966" MODIFIED="1716861818586" TEXT="man k&#xf6;nnte die Storage noch dynamisch anpassen"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1715625082614" ID="ID_1575150785" MODIFIED="1715625089779" TEXT="SeveralBuilder_test">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716857382440" ID="ID_46730448" MODIFIED="1716857402102" TEXT="simpleUsage">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716857382441" ID="ID_748929457" MODIFIED="1716857402102" TEXT="check_Builder">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716857382441" ID="ID_610081580" MODIFIED="1716857402102" TEXT="check_ErrorHandling">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716857382441" ID="ID_1397767362" MODIFIED="1716857402102" TEXT="check_ElementAccess">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716857382442" ID="ID_28139752" MODIFIED="1716857402101" TEXT="check_CustomAllocator">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
</node>
@ -81991,8 +82041,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
es gibt noch gar keine Verwendungen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="ksmiletris"/>
</node>
</node>