Library: verify element placement into storage
...use some pointer arithmetic for this test to verify some important cases of object placement empirically. Note: there is possibly a very special problematic case when ''over aligned objects'' are not placed in accordance to their alignment requirements. Fixing this problem would be non-trivial, and thus I have only left a note in #1204
This commit is contained in:
parent
fd1ed7e78f
commit
3bbdf40c32
4 changed files with 144 additions and 12 deletions
|
|
@ -43,9 +43,48 @@
|
||||||
** in the element array. The field ArrayBucket<I>::spread defines this spacing
|
** in the element array. The field ArrayBucket<I>::spread defines this spacing
|
||||||
** and thus the offset used for subscript access.
|
** and thus the offset used for subscript access.
|
||||||
**
|
**
|
||||||
|
** # Handling of data elements
|
||||||
|
**
|
||||||
|
** The ability to emplace a mixture of data types into the storage exposed through
|
||||||
|
** the lib::Several front-end creates some complexities related to element handling.
|
||||||
|
** The implementation relies on a rules and criteria based approach to decide on
|
||||||
|
** a case by case base if some given data content is still acceptable. This allows
|
||||||
|
** for rather tricky low-level usages, but has the downside to detect errors only
|
||||||
|
** at runtime — which in this case is ameliorated by the limitation that elements
|
||||||
|
** must be provided completely up-front, through the SeveralBuilder.
|
||||||
|
** - in order to handle any data element, we must be able to invoke its destructor
|
||||||
|
** - an arbitrary mixture of types can thus only be accepted if either we can
|
||||||
|
** rely on a common virtual base class destructor, or if all data elements
|
||||||
|
** are trivially destructible; these properties can be detected at compile
|
||||||
|
** time with the help of the C++ `<type_traits>` library
|
||||||
|
** - this container can accommodate _non-copyable_ data types, under the proviso
|
||||||
|
** that the complete storage required is pre-allocated (using `reserve()` from
|
||||||
|
** the builder API)
|
||||||
|
** - otherwise, data can be filled in dynamically, expanding the storage as needed,
|
||||||
|
** given that all existing elements can be safely re-located by move or copy
|
||||||
|
** constructor into a new, larger storage buffer.
|
||||||
|
** - alternatively, when data elements are even ''trivially copyable'' (e.g. POD data),
|
||||||
|
** then it is even possible to increase the placement spread in the storage at the
|
||||||
|
** point when the requirement to do so is discovered dynamically; objects can be
|
||||||
|
** shifted to other locations by `std::memmove()` in this case.
|
||||||
|
** - notably, lib::AllocationCluster has the ability to dynamically adapt an allocation,
|
||||||
|
** but only if this happens to be currently the last allocation handed out; it can
|
||||||
|
** thus be arranged even for an unknown number of non-copyable objects to be emplaced
|
||||||
|
** when creating the suitable operational conditions.
|
||||||
|
** A key point to note is the fact that the container does not capture and store the
|
||||||
|
** actual data types persistently. Thus, the above rules must be applied in a way
|
||||||
|
** to always ensure safe handling of the contained data. Typically, the first element
|
||||||
|
** actually added will »prime« the container for a certain usage style, and after that,
|
||||||
|
** some other usage patterns may be rejected.
|
||||||
|
**
|
||||||
** @todo this is a first implementation solution from 6/2025 — and was deemed
|
** @todo this is a first implementation solution from 6/2025 — and was deemed
|
||||||
** _roughly adequate_ at that time, yet should be revalidated once more
|
** _roughly adequate_ at that time, yet should be revalidated once more
|
||||||
** observations pertaining real-world usage are available...
|
** observations pertaining real-world usage are available...
|
||||||
|
** @warning there is a known problem with _over-alligned-types,_ which becomes
|
||||||
|
** relevant when the _interface type_ has only lower alignment requirement,
|
||||||
|
** but an individual element is added with higher alignment requirements.
|
||||||
|
** In this case, while the spread is increased, still the placement of
|
||||||
|
** the interface-type is used as anchor, possibly leading to misalignment.
|
||||||
** @see several-builder-test.cpp
|
** @see several-builder-test.cpp
|
||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ namespace lib {
|
||||||
Deleter deleter;
|
Deleter deleter;
|
||||||
|
|
||||||
/** mark start of the storage area */
|
/** mark start of the storage area */
|
||||||
|
alignas(I)
|
||||||
alignas(void*)
|
alignas(void*)
|
||||||
std::byte storage[sizeof(I)];
|
std::byte storage[sizeof(I)];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,9 @@ using std::array;
|
||||||
using std::rand;
|
using std::rand;
|
||||||
|
|
||||||
using lib::explore;
|
using lib::explore;
|
||||||
|
using util::isSameObject;
|
||||||
using util::isLimited;
|
using util::isLimited;
|
||||||
|
using util::getAddr;
|
||||||
using util::isnil;
|
using util::isnil;
|
||||||
using util::join;
|
using util::join;
|
||||||
|
|
||||||
|
|
@ -185,6 +187,9 @@ namespace test{
|
||||||
void
|
void
|
||||||
check_Builder()
|
check_Builder()
|
||||||
{
|
{
|
||||||
|
// prepare to verify proper invocation of all constructors / destructors
|
||||||
|
Dummy::checksum() = 0;
|
||||||
|
|
||||||
{ // Scenario-1 : Baseclass and arbitrary subclass elements
|
{ // Scenario-1 : Baseclass and arbitrary subclass elements
|
||||||
SeveralBuilder<Dummy> builder;
|
SeveralBuilder<Dummy> builder;
|
||||||
CHECK (isnil (builder));
|
CHECK (isnil (builder));
|
||||||
|
|
@ -240,6 +245,8 @@ namespace test{
|
||||||
CHECK (10 == elms.size());
|
CHECK (10 == elms.size());
|
||||||
CHECK (join (elms,"-") == "0-1-2-3-4-5-6-7-8-9"_expect);
|
CHECK (join (elms,"-") == "0-1-2-3-4-5-6-7-8-9"_expect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CHECK (0 == Dummy::checksum());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -265,13 +272,12 @@ namespace test{
|
||||||
* grow dynamically.
|
* grow dynamically.
|
||||||
* - all these failure conditions are handled properly, including exceptions emanating
|
* - all these failure conditions are handled properly, including exceptions emanating
|
||||||
* from element constructors; the container remains sane and no memory is leaked.
|
* from element constructors; the container remains sane and no memory is leaked.
|
||||||
* @todo WIP 6/24 🔁 define ⟶ ✔ implement
|
* @todo WIP 6/24 ✔ define ⟶ ✔ implement
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
check_ErrorHandling()
|
check_ErrorHandling()
|
||||||
{
|
{
|
||||||
// prepare to verify proper invocation of all constructors / destructors
|
CHECK (0 == Dummy::checksum());
|
||||||
Dummy::checksum() = 0;
|
|
||||||
|
|
||||||
{ // Scenario-1 : Baseclass and arbitrary subclass elements
|
{ // Scenario-1 : Baseclass and arbitrary subclass elements
|
||||||
SeveralBuilder<Dummy> builder;
|
SeveralBuilder<Dummy> builder;
|
||||||
|
|
@ -464,12 +470,83 @@ namespace test{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** @test TODO verify correct placement of instances within storage
|
/** @test verify correct placement of instances within storage
|
||||||
* @todo WIP 6/24 🔁 define ⟶ implement
|
* - use a low-level pointer calculation for this test to
|
||||||
|
* draw conclusions regarding the spacing of objects accepted
|
||||||
|
* into the lib::Several-container
|
||||||
|
* - demonstrate the simple data elements are packed efficiently
|
||||||
|
* - verify that special alignment requirements are observed
|
||||||
|
* - emplace several ''non copyable objects'' and then
|
||||||
|
* move-assign the lib::Several container instance; this
|
||||||
|
* demonstrates that the latter is just a access front-end,
|
||||||
|
* while the data elements reside in a fixed storage buffer
|
||||||
|
* @todo WIP 6/24 ✔ define ⟶ ✔ implement
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
check_ElementStorage()
|
check_ElementStorage()
|
||||||
{
|
{
|
||||||
|
auto loc = [](auto& something){ return size_t(getAddr (something)); };
|
||||||
|
auto calcSpread = [&](auto& several){ return loc(several[1]) - loc(several[0]); };
|
||||||
|
|
||||||
|
{ // Scenario-1 : tightly packed values
|
||||||
|
Several<int> elms = makeSeveral({21,34,55}).build();
|
||||||
|
CHECK (21 == elms[0]);
|
||||||
|
CHECK (34 == elms[1]);
|
||||||
|
CHECK (55 == elms[2]);
|
||||||
|
CHECK (3 == elms.size());
|
||||||
|
CHECK (sizeof(elms) == sizeof(void*));
|
||||||
|
|
||||||
|
CHECK (sizeof(int) == alignof(int));
|
||||||
|
size_t spread = calcSpread (elms);
|
||||||
|
CHECK (spread == sizeof(int));
|
||||||
|
CHECK (loc(elms.back()) == loc(elms.front()) + 2*spread);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Scenario-2 : alignment
|
||||||
|
struct Ali
|
||||||
|
{
|
||||||
|
alignas(32)
|
||||||
|
char charm = 'u';
|
||||||
|
};
|
||||||
|
|
||||||
|
auto elms = makeSeveral<Ali>().fillElm(5).build();
|
||||||
|
CHECK (5 == elms.size());
|
||||||
|
CHECK (sizeof(elms) == sizeof(void*));
|
||||||
|
|
||||||
|
size_t spread = calcSpread (elms);
|
||||||
|
CHECK (spread == alignof(Ali));
|
||||||
|
CHECK (loc(elms.front()) % alignof(Ali) == 0);
|
||||||
|
CHECK (loc(elms.back()) == loc(elms.front()) + 4*spread);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Scenario-3 : noncopyable objects
|
||||||
|
auto elms = makeSeveral<ShortBlocker>().fillElm(5).build();
|
||||||
|
|
||||||
|
auto v0 = elms[0].val; auto p0 = loc(elms[0]);
|
||||||
|
auto v1 = elms[1].val; auto p1 = loc(elms[1]);
|
||||||
|
auto v2 = elms[2].val; auto p2 = loc(elms[2]);
|
||||||
|
auto v3 = elms[3].val; auto p3 = loc(elms[3]);
|
||||||
|
auto v4 = elms[4].val; auto p4 = loc(elms[4]);
|
||||||
|
|
||||||
|
CHECK (5 == elms.size());
|
||||||
|
auto moved = move(elms);
|
||||||
|
CHECK (5 == moved.size());
|
||||||
|
CHECK (isnil (elms));
|
||||||
|
|
||||||
|
CHECK (loc(moved[0]) == p0);
|
||||||
|
CHECK (loc(moved[1]) == p1);
|
||||||
|
CHECK (loc(moved[2]) == p2);
|
||||||
|
CHECK (loc(moved[3]) == p3);
|
||||||
|
CHECK (loc(moved[4]) == p4);
|
||||||
|
|
||||||
|
CHECK (moved[0].val == v0);
|
||||||
|
CHECK (moved[1].val == v1);
|
||||||
|
CHECK (moved[2].val == v2);
|
||||||
|
CHECK (moved[3].val == v3);
|
||||||
|
CHECK (moved[4].val == v4);
|
||||||
|
|
||||||
|
CHECK (calcSpread(moved) == sizeof(ShortBlocker));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81780,7 +81780,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
||||||
<icon BUILTIN="idea"/>
|
<icon BUILTIN="idea"/>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#4e3b7f" CREATED="1718034857705" FOLDED="true" ID="ID_725324060" MODIFIED="1718073781365" TEXT="Alignment beachten">
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#4e3b7f" CREATED="1718034857705" FOLDED="true" ID="ID_725324060" MODIFIED="1718294109135" TEXT="Alignment beachten">
|
||||||
<icon BUILTIN="messagebox_warning"/>
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
<node CREATED="1718035817292" ID="ID_908227037" MODIFIED="1718035826853" TEXT="Verwendungen von sizeof(TY)">
|
<node CREATED="1718035817292" ID="ID_908227037" MODIFIED="1718035826853" TEXT="Verwendungen von sizeof(TY)">
|
||||||
<icon BUILTIN="idea"/>
|
<icon BUILTIN="idea"/>
|
||||||
|
|
@ -81835,7 +81835,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
||||||
</html></richcontent>
|
</html></richcontent>
|
||||||
</node>
|
</node>
|
||||||
<node CREATED="1718064742835" ID="ID_871080765" MODIFIED="1718064754941" TEXT="»grob korrekt«">
|
<node CREATED="1718064742835" ID="ID_871080765" MODIFIED="1718064754941" TEXT="»grob korrekt«">
|
||||||
<node CREATED="1718064756929" ID="ID_1870004223" MODIFIED="1718064798319" TEXT="wähle für den Puffer das Alignment eines void* (»slot«)"/>
|
<node CREATED="1718064756929" ID="ID_1870004223" MODIFIED="1718294057405" TEXT="wähle für den Puffer das Alignment des Interface-Typs"/>
|
||||||
|
<node CREATED="1718294058002" ID="ID_1907851352" MODIFIED="1718294103590" TEXT="stelle zudem sicher, daß das Alignment mindestens wie void* ist (ein »slot«)"/>
|
||||||
<node CREATED="1718064809954" ID="ID_79404905" MODIFIED="1718064835981" TEXT="runde den Spread korrekt auf gemäß Element-Alignment"/>
|
<node CREATED="1718064809954" ID="ID_79404905" MODIFIED="1718064835981" TEXT="runde den Spread korrekt auf gemäß Element-Alignment"/>
|
||||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1718064840726" ID="ID_1507935794" MODIFIED="1718065973474" TEXT="Problem: over-aligned objects werden nicht korrekt gehandhabt">
|
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1718064840726" ID="ID_1507935794" MODIFIED="1718065973474" TEXT="Problem: over-aligned objects werden nicht korrekt gehandhabt">
|
||||||
<icon BUILTIN="messagebox_warning"/>
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
|
@ -81875,9 +81876,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
||||||
<node COLOR="#338800" CREATED="1718067163803" ID="ID_1053644006" MODIFIED="1718067198266" TEXT=""sizeof(TY)" ⟼ reqSiz<TY>()">
|
<node COLOR="#338800" CREATED="1718067163803" ID="ID_1053644006" MODIFIED="1718067198266" TEXT=""sizeof(TY)" ⟼ reqSiz<TY>()">
|
||||||
<icon BUILTIN="button_ok"/>
|
<icon BUILTIN="button_ok"/>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1718067214231" ID="ID_1276439764" LINK="#ID_1830672111" MODIFIED="1718067239581" TEXT="testen">
|
<node COLOR="#338800" CREATED="1718067214231" ID="ID_1276439764" LINK="#ID_1830672111" MODIFIED="1718293971538" TEXT="testen"/>
|
||||||
<icon BUILTIN="flag-yellow"/>
|
|
||||||
</node>
|
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
|
|
@ -83648,8 +83647,24 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716857382441" ID="ID_1397767362" MODIFIED="1718219037095" TEXT="check_ElementStorage">
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716857382441" ID="ID_1397767362" MODIFIED="1718219037095" TEXT="check_ElementStorage">
|
||||||
<icon BUILTIN="flag-yellow"/>
|
<icon BUILTIN="flag-yellow"/>
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1718067224015" ID="ID_1830672111" MODIFIED="1718067235928" TEXT="Alignment-Behandlung verifizieren">
|
<node COLOR="#338800" CREATED="1718290606176" ID="ID_1457988951" MODIFIED="1718290634409" TEXT="grundsätzlich: Platzierung mit gleichmäßigem Spread">
|
||||||
<icon BUILTIN="flag-yellow"/>
|
<icon BUILTIN="button_ok"/>
|
||||||
|
</node>
|
||||||
|
<node COLOR="#338800" CREATED="1718067224015" ID="ID_1830672111" MODIFIED="1718293512166" TEXT="Alignment-Behandlung verifizieren">
|
||||||
|
<icon BUILTIN="button_ok"/>
|
||||||
|
<node COLOR="#e036b2" CREATED="1718294214083" ID="ID_1828779752" MODIFIED="1718294228266" TEXT="tja... wer testet">
|
||||||
|
<icon BUILTIN="smiley-oh"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1718294234410" ID="ID_251500539" MODIFIED="1718294355847" TEXT="hab jetzt zudem alignas(I) mit dazugenommen">
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head/>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
dadurch daß alignas(void*) aber auch da ist, stellen wir mindestens »slot«-Alignment sicher; das löst zwar nicht das Problem wenn nur ein einzelner Element-Typ overaligned ist, aber schließt Probleme mit allen regulären Typen aus
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node CREATED="1718202490965" ID="ID_247674037" MODIFIED="1718206085439" TEXT="Handhabung von non-copyable-Objekten">
|
<node CREATED="1718202490965" ID="ID_247674037" MODIFIED="1718206085439" TEXT="Handhabung von non-copyable-Objekten">
|
||||||
<linktarget COLOR="#638ad5" DESTINATION="ID_247674037" ENDARROW="Default" ENDINCLINATION="-777;-16;" ID="Arrow_ID_10606647" SOURCE="ID_1146034926" STARTARROW="None" STARTINCLINATION="-438;67;"/>
|
<linktarget COLOR="#638ad5" DESTINATION="ID_247674037" ENDARROW="Default" ENDINCLINATION="-777;-16;" ID="Arrow_ID_10606647" SOURCE="ID_1146034926" STARTARROW="None" STARTINCLINATION="-438;67;"/>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue