2013-03-18 7 views
15

Ich werde einen benutzerdefinierten Zuordner, preallocating einen großen Block (Array) zum Speichern von N Elementen einer Klasse T, bauen und dann erhöhe nur einen Index innerhalb der Array für Servicezuweisungsanfragen.Rebinding in einem benutzerdefinierten STL Zuordner mit vorab zugewiesenen Block

Da ich nicht für die Elemente jeder Initialisierung wollen im Voraus zugewiesenen Block, so etwas wie das wird nicht funktionieren:

T buffer[N]; 

, weil in diesem Fall T ‚s Konstruktor wird aufgerufen für die N Elemente des Blocks.

Da mein Verständnis ist, dass std::aligned_storage nicht T ‚s Konstruktor nicht nennen, dachte ich an std::aligned_storage verwenden, etwa so:

std::aligned_storage< 
    N * sizeof(T), 
    std::alignment_of<T>::value 
>::type buffer; 

T* base = static_cast<T*>(static_cast<void*>(&buffer)); 

Und dann kann der Zuordner nur den Basiszeiger erhöht werden, wenn eine Zuweisung für ein T wird angefordert (bis (base+N)), und T kann an Ort und Stelle (mit Platzierung new) bei Bedarf gebaut werden.

Ich möchte dieses Schema verwenden, um eine benutzerdefinierte Zuordnung für STL-Container zu definieren. Es scheint mir jedoch, dass es hier ein Problem für geben könnte, das wiederverbindet. Wenn mein Verständnis korrekt ist, sollte ein STL-Zuordner tatsächlich eine erneute Bindung von einem Typ T an einen Typ U unterstützen, z. weil Container wie std::list<T> (oder einem anderen Knoten basierende Behälter wie std::map) verwenden Verteilern Knoten, die nicht tatsächlich vom Typ T, aber von einem anderen Typ U (enthaltend T und andere „header“ Overhead-Information für den Knoten) zuzuteilen. Also, würde die oben genannte std::aligned_storage Ansatz gut für die erneute Bindung funktionieren? Oder (wie ich denke) eine korrekte Ausrichtung für T s tut nicht implizieren eine korrekte Ausrichtung für einen anderen anderen Typ U?

Wie könnte dieses Problem gelöst werden?

Wie könnte ich die oben genannten buffer definieren, damit es auch für das erneute Binden an einen anderen Typ U funktioniert?

Sollte dieses Problem aus einer anderen Perspektive angegriffen werden? Wenn ja, was?

+0

Können Sie nicht einfach 'std :: alignment_of :: value' verwenden, damit es für jeden von den C++ - Standardzuordnern unterstützten Typ richtig ausgerichtet wird? Natürlich wird dies nicht für Typen mit speziellen (strengeren) Ausrichtungsanforderungen funktionieren (bestes Beispiel SSE), aber diese sind am Ende immer ein Problem, selbst für die Standardzuordner. –

Antwort

10

Sie sind auf dem richtigen Weg.

Eine irritierende Details ist, dass Kopien von Allokatoren gleiche, sogar konvertieren (Rebound) Kopien vergleichen müssen. Gleiches zu vergleichen bedeutet, dass sie die Zeiger des anderen aufheben können. So wird ein Behälter wie std::list<int>your_alloc<int> an your_alloc<node<int>> binden und dann einen your_alloc<node<int>> unter Verwendung eines your_alloc<int> konstruieren. Und technisch muss Ihr your_alloc<node<int>> einen Zeiger freigeben, der durch your_alloc<int> zugeteilt wird.

ist mein Versuch, diese Anforderung zu erfüllen. Fühlen Sie sich frei, diesen Code zu kopieren, zu modifizieren oder zu missbrauchen. Meine Absicht ist es, zu erziehen, nicht der weltweite Allokator zu werden (was sowieso nicht gerade lukrativ wäre :-)).

Dieses Beispiel nimmt einen etwas anderen Ansatz zur Ausrichtung: Ich passieren, dass auf meiner Plattform von Interesse wissen (OS X, iOS), dass malloc Renditen 16-Byte-ausgerichteten Speicher, und damit ist alles meine Gewohnheit allocator zurückgeben muss. Sie können diese Nummer auf das für Ihr System passende ändern.

Diese festverdrahtete Ausrichtung bedeutet, dass ein einzelner Pool mehrere allocator<int> und allocator<node<int>> sicher versorgen kann, da sie alle 16 Byte ausgerichtet sind (und das reicht). Und es bedeutet auch, dass Kopien, selbst konvertierte Kopien, erkennen können, wenn der Zeiger in den Puffer zeigt, da alle Kopien den gleichen Puffer haben.

Etwas anders gesagt, das C++ - Komitee hat effektiv festgelegt, dass Zuweiser Referenztypen sind: Kopien sind äquivalent und zeigen auf den gleichen Speicherpool.

Sie können mit dem Betrug umgehen, indem Sie Speicherpools verwenden, die tatsächlich in den Zuordner eingebettet sind, aber nur mit einigen Containern in einigen Implementierungen, und Sie können dies nicht mit einem Zitat aus dem Standard unterstützen.

+0

Vielen Dank für Ihre Vorschläge und für die Freigabe Ihres Beispiel-Zuweiser-Codes. –

+1

Also, wenn Sie wollen, sagen wir, 64-Byte-Ausrichtung (für Cache-Alignment Zwecke) aus dem 'stack_allocator', müssen Sie auch den globalen' :: operator new' neu definieren, um die gleiche 64-Byte-Ausrichtung zu bieten? – TemplateRex

+1

@rhalbersma, könnten Sie tun, oder Sie könnten immer eine zusätzliche '64 - alignof (std :: max_align_t)' von ':: Operator new 'anfordern und dann nur an der 64-Byte-Grenze zu verwenden (aber Sie würden muss den ursprünglichen nicht ausgerichteten Zeiger irgendwie an ':: operator delete' übergeben. –

Verwandte Themen