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
|
||||
** 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
|
||||
** _roughly adequate_ at that time, yet should be revalidated once more
|
||||
** 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
|
||||
**
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ namespace lib {
|
|||
Deleter deleter;
|
||||
|
||||
/** mark start of the storage area */
|
||||
alignas(I)
|
||||
alignas(void*)
|
||||
std::byte storage[sizeof(I)];
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@ using std::array;
|
|||
using std::rand;
|
||||
|
||||
using lib::explore;
|
||||
using util::isSameObject;
|
||||
using util::isLimited;
|
||||
using util::getAddr;
|
||||
using util::isnil;
|
||||
using util::join;
|
||||
|
||||
|
|
@ -185,6 +187,9 @@ namespace test{
|
|||
void
|
||||
check_Builder()
|
||||
{
|
||||
// prepare to verify proper invocation of all constructors / destructors
|
||||
Dummy::checksum() = 0;
|
||||
|
||||
{ // Scenario-1 : Baseclass and arbitrary subclass elements
|
||||
SeveralBuilder<Dummy> builder;
|
||||
CHECK (isnil (builder));
|
||||
|
|
@ -240,6 +245,8 @@ namespace test{
|
|||
CHECK (10 == elms.size());
|
||||
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.
|
||||
* - all these failure conditions are handled properly, including exceptions emanating
|
||||
* 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
|
||||
check_ErrorHandling()
|
||||
{
|
||||
// prepare to verify proper invocation of all constructors / destructors
|
||||
Dummy::checksum() = 0;
|
||||
CHECK (0 == Dummy::checksum());
|
||||
|
||||
{ // Scenario-1 : Baseclass and arbitrary subclass elements
|
||||
SeveralBuilder<Dummy> builder;
|
||||
|
|
@ -464,12 +470,83 @@ namespace test{
|
|||
}
|
||||
|
||||
|
||||
/** @test TODO verify correct placement of instances within storage
|
||||
* @todo WIP 6/24 🔁 define ⟶ implement
|
||||
/** @test verify correct placement of instances within storage
|
||||
* - 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
|
||||
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"/>
|
||||
</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"/>
|
||||
<node CREATED="1718035817292" ID="ID_908227037" MODIFIED="1718035826853" TEXT="Verwendungen von sizeof(TY)">
|
||||
<icon BUILTIN="idea"/>
|
||||
|
|
@ -81835,7 +81835,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</html></richcontent>
|
||||
</node>
|
||||
<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 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"/>
|
||||
|
|
@ -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>()">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1718067214231" ID="ID_1276439764" LINK="#ID_1830672111" MODIFIED="1718067239581" TEXT="testen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1718067214231" ID="ID_1276439764" LINK="#ID_1830672111" MODIFIED="1718293971538" TEXT="testen"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -83648,8 +83647,24 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716857382441" ID="ID_1397767362" MODIFIED="1718219037095" TEXT="check_ElementStorage">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1718067224015" ID="ID_1830672111" MODIFIED="1718067235928" TEXT="Alignment-Behandlung verifizieren">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1718290606176" ID="ID_1457988951" MODIFIED="1718290634409" TEXT="grundsätzlich: Platzierung mit gleichmäßigem Spread">
|
||||
<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 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;"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue