diff --git a/src/lib/several-builder.hpp b/src/lib/several-builder.hpp index 31e2b19cd..30e962de9 100644 --- a/src/lib/several-builder.hpp +++ b/src/lib/several-builder.hpp @@ -89,10 +89,12 @@ namespace lib { using util::max; using util::min; using util::_Fmt; + using std::is_nothrow_move_constructible_v; using std::is_trivially_move_constructible_v; using std::is_trivially_destructible_v; using std::has_virtual_destructor_v; using std::is_trivially_copyable_v; + using std::is_copy_constructible_v; using std::is_object_v; using std::is_volatile_v; using std::is_const_v; @@ -241,11 +243,21 @@ namespace lib { std::memmove (newPos, oldPos, amount); } else + if constexpr (is_nothrow_move_constructible_v + or is_copy_constructible_v) { E& oldElm = reinterpret_cast (src->subscript (idx)); Fac::template createAt (tar, idx ,std::move_if_noexcept (oldElm)); } + else + { + NOTREACHED("realloc immovable type (neither trivially nor typed movable)"); + // this alternative code section is very important, because it allows + // to instantiate this code even for »noncopyable« types, assuming that + // sufficient storage is reserved beforehand, and thus copying is irrelevant. + // For context: the std::vector impl. from libStdC++ is lacking this option. + } tar->cnt = idx+1; // mark fill continuously for proper clean-up after exception } }; @@ -516,7 +528,7 @@ namespace lib { */ template Deleter - selectDestructor () + selectDestructor() { typename POL::Fac& factory(*this); diff --git a/tests/library/several-builder-test.cpp b/tests/library/several-builder-test.cpp index 55247aae3..3ce7d42f5 100644 --- a/tests/library/several-builder-test.cpp +++ b/tests/library/several-builder-test.cpp @@ -288,6 +288,12 @@ SHOW_EXPR(ne5.getVal()) builder.fillElm(5); CHECK (5 == builder.size()); + // trigger re-alloc by moving into larger memory block + builder.fillElm(14); + CHECK (19 == builder.size()); + + builder.emplace(); + builder.emplace>(); ///////////////////////////////////OOO this should trigger an exception -> need to code an explicit check right at the start of emplaceNewElm() } } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index d3ec96505..3337fbb83 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -82741,24 +82741,81 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + + + + + + + + + + - +

- ...und das kann vom Allokator abhängen + ...und das kann vom Allokator abhängen — sollte also in den meisten für Lumiera wirklich relevanten Fällen gar nicht vorkommen

+ + + + + + + +

+ ...nicht nur eine Schwäche, sondern ein ausgewachsenes Ärgerniss. Allerdings betrifft das strenggenommen nur die libStc++ +

+

+ Und zwar ist es so: man könnte einen Vector per resize() vordimensionieren, so daß die Storage nicht per alloc-and-copy wachsen muß; danach könnte man eigentlich ohne Weiteres auch non-copyable-Objekte im Vector haben — solange man den Vector selber ebenfalls nicht kopiert (Verschieben wäre möglich). +

+

+ +

+

+ Soweit die Theorie... in der Praxis aber scheitert das, weil der Compiler versucht, den realloc-Code zu instantiieren und dafür keinen Copy/Move-Konstruktor findet +

+ + +
+
+ + + + +

+ Und dazu sind wir im Stande... +

+
    +
  • + wir haben entsprechende Prüf-Logik, die zur Laufzeit eine Exception wirft, wenn die Kapazität nicht reicht. Da Objekte nur im Builder hinzugefügt werden, ist das hier viel akzeptabler als für std::vector, der durch ein solches Verhalten ja doch massiv unzuverlässig würde. +
  • +
  • + wir müssen dann nur sicherstellen, daß der typisierte copy-Code in unserer realloc()-Funktion ebenfalls einen statischen Guard hat, und damit auch überhaupt nicht emittiert wird, wenn wir ihn ohnehin nicht nutzen können +
  • +
+ + +
+
+ + + +
@@ -82894,6 +82951,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

+ + +
@@ -83110,9 +83170,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...also ist das keine gute Idee, da verwirrend @@ -83349,9 +83407,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...und zwar, weil man ein (language)-Array mit operator new[] allozieren muß @@ -83384,9 +83440,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

und der von Dummy muß noexcept sein @@ -83397,22 +83451,113 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + - + - - + + + + - - + + + + +

+ Es ist nämlich so: wenn wir gleich die Move-Sperre setzen, können wir ansonsten das Objekt durchaus reinlassen — vorausgesetzt das mit dem Destruktor bleibt OK — und letzteres prüfen wir ja auch, nur eben später (aus Gründen der einfachen Formulierung im Code, weil wir den Rückgabewert von dieser Prüfung erst später brauchen) +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +

+ das im Test beobachtete Verhalten ist korrekt +

+ +
+ + + +

+ ...es spricht tatsächlich nichts dagegen, diesen Fall auch nachträglich noch zuzulassen — man gibt dann eben die Möglichkeit für re-Allocations auf (und wenn das ein Problem darstellt, macht sich das zu gegebener Zeit eindeutig bemerkbar) +

+ + +
+
+ + + + +
    +
  • + gleich zu Beginn — noch vor allen Storage-Prüfungen — stellen wir fest, ob wir (noch) Ojbekte verschieben können. Wenn das für ein einziges Objekt nicht mehr gilt, dann gilt es eben für den gesamten Container nicht mehr. Es ist zu dem Zeitpunkt noch nicht klar, ob das überhaupt ein Problem darstellt. +
  • +
  • + als Nächstes prüfen wir die Speicheranforderungen; wenn der Speicher nicht reicht und wir zudem nicht verschieben können, dann staubt's +
  • +
  • + nur wenn nötig und möglich wird die Allokation vergrößert. An dem Punkt ist alles wieder sauber für die bestehenden Elemente +
  • +
  • + erst danach müssen wir die Destruktor-Möglichkeit für das neue Element prüfen. Wenn hier ein Fehler auftritt, wurde zwar ggfs der Puffer (unnötigerweise) vergrößert, aber es hat noch keine Objekt-Konstruktion stattgefunden, und wir die Gesamtsituation ist weiterhin konsistent +
  • +
+ +
+ +
+ + +
+ + + + +

+ kann keinen ganz anderen Typ platzieren (der nicht Subklasse ist) +

+ +
+ + +
+