From 5259000bc429e0d22b8d8276593d5a73b76675bc Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 26 May 2024 01:51:23 +0200 Subject: [PATCH] Library: how to use a standard allocator for `LinkedElements` By default, LinkedElements uses a policy OwningHeapAllocated; while retaining this interface, this policy should be recast to rely on a standard compliant allocator, with a default fallback to `std::allocator` This way, a single policy would serve all the cases where objects are actually owned and managed by `LinkedElements`, and most special policies would be redundant. This turns out to be quite tedious and technical however, since the newer standard mandates to use std::allocator_traits as front-end, and moreover the standard allocators are always tied to one specific target type, while `LinkedElements` is deliberately used to maintain a polymorphic sequence. --- src/lib/linked-elements.hpp | 79 ++++++++++++++++++++++++++++ wiki/thinkPad.ichthyo.mm | 100 +++++++++++++++++++++++++++++++++--- 2 files changed, 173 insertions(+), 6 deletions(-) diff --git a/src/lib/linked-elements.hpp b/src/lib/linked-elements.hpp index d77442677..00985340a 100644 --- a/src/lib/linked-elements.hpp +++ b/src/lib/linked-elements.hpp @@ -66,6 +66,7 @@ #include "lib/util.hpp" #include +#include namespace lib { @@ -109,6 +110,84 @@ namespace lib { } }; + template + struct OwningAlloc + : util::MoveOnly + { + using Allo = ALO; + using AlloT = std::allocator_traits; + using BaseType = typename Allo::value_type; + + Allo allocator_; + + OwningAlloc (Allo allo = Allo{}) + : allocator_{allo} + { } + + template + auto + adaptAllocator (Allo const& baseAllocator) + { + using XAllo = typename AlloT::template rebind_alloc; + if constexpr (std::is_constructible_v) + return XAllo(allocator_); + else + return XAllo(); + } + + template + auto& + construct (typename ALOT::alocator_type& allo, ARGS&& ...args) + { + auto loc = ALOT::allocate (allocator_, 1); + ALOT::construct (allocator_, loc, std::forward(args)...); + return *loc; + } + + template + void + destroy (typename ALOT::alocator_type& allo, typename ALOT::pointer elm) + { + ALOT::destroy (allo, elm); + ALOT::deallocate (allo, elm, 1); + } + + + /** create new element using the embedded allocator */ + template + TY& + create (ARGS&& ...args) + { + if constexpr (std::is_same_v) + { + return construct (allocator_, std::forward(args)...); + } + else + { + using XAlloT = typename AlloT::template rebind_traits; + auto xAllo = adaptAllocator (allocator_); + return construct (xAllo, std::forward(args)...); + } + } + + template + void + destroy (TY* elm) + { + if constexpr (std::is_same_v) + { + destroy (allocator_, elm); + } + else + { + using XAlloT = typename AlloT::template rebind_traits; + auto xAllo = adaptAllocator (allocator_); + destroy (xAllo, elm); + } + } + + }; + diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 1a291ed56..485498827 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -64932,10 +64932,89 @@ - + + + + + + + + + +

+ man möchte ein reines Funktor-Objekt, das irgendwie hintenrum verdrahtet ist. Auf diesem ruft man nur noch den Funktionsoperator auf mit den konkreten Argumenten, denn der Zieltyp ist bereits festgelegt +

+ +
+
+ + + + +

+ ...auch hier soll die Verdrahtung verborgen bleiben; jedoch ist man nicht auf einen Objekttyp festgelegt, und außerdem soll es die Möglichkeit geben, esplizit eine destroy()-Funktion aufzurufen, oder alternativ schon aus der Erzeugung ein managing-handle zu bekommen +

+ +
+
+ + + + +

+ Speziell bei Containern treten verschiedene Nutzungsmuster und Variationen auf... +

+
    +
  • + man möchte das Erzeugen neuer Objekte komplett unterbinden +
  • +
  • + man möchte zum Erzeugen einen eingebetteten Custom-Allocator verwenden +
  • +
  • + man möchte Destuktoren aufrufen oder nicht aufrufen +
  • +
  • + man möchte beim Verwerfen eines Objekts die Allokation freigeben oder auch nicht und ggfs. auch bloß mitzählen +
  • +
+ +
+
+ + + + +

+ Das ist in etwa die Funktionalität, die man von der Standard-Library bekommt, und die damit auch von STL-Containern genutzt werden kann; eigentlich braucht man hier nur die Bereitstellung (und Freigabe) von uninitialisiertem Speicher; das eigentliche Konstruieren und Zerstören erledigen die std::allocator_traits +

+ +
+
+
+ + + + + + + + + + + +
+ + + + + + + + @@ -81623,7 +81702,16 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + + + + + @@ -81700,9 +81788,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

muß hier leider eine schecklicke low-level-Trickserei machen; das ist die Konsequenz der Entscheidung, mit dem absolut minimalen Storage-Overhead zu arbeiten, und außerdem muß ich auch noch das Non-Copyable aushebeln... @@ -82144,9 +82230,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -121704,6 +121791,7 @@ std::cout << tmpl.render({"what", "World"}) << s +