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:
parent
e4f91ecb4d
commit
73dd24ecef
4 changed files with 171 additions and 262 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
} }
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -81442,15 +81442,18 @@ Date:   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>  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ß-Kenntnis als Ganzes verworfen und neu verwendet wird.
|
||||
...man verwendet <i>nur speziell im produktiven Einsatz im Node-Graph</i>  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ß-Kenntnis als Ganzes verworfen und neu verwendet wird.
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="forward"/>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -81661,9 +81664,56 @@ Date:   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 ü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ü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 ⟺ schü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öchte Elemente verdrahten"/>
|
||||
<node CREATED="1716861808632" ID="ID_361446966" MODIFIED="1716861818586" TEXT="man kö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:   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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue