2013-03-18 14 views
15

Sto per costruire un allocatore personalizzato , preallocare un grosso blocco (array) per memorizzare N elementi di qualche classe T, e poi basta aumentare un indice all'interno array per soddisfare le richieste di allocazione.ricollegamento in un allocatore STL personalizzato con preassegnati blocco

Dal momento che Non vogliono alcuna inizializzazione per gli elementi nel blocco pre-assegnati, qualcosa come questo non funziona:

T buffer[N]; 

perché in questo caso s' T sarà chiamato costruttore per gli elementi N del blocco.

Dal momento che la mia comprensione è che std::aligned_storage non chiama il costruttore T s', ho pensato di usare std::aligned_storage, qualcosa di simile:

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

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

E poi l'allocatore può solo incrementare il puntatore base quando una dotazione per a T è richiesto (fino a (base+N)), e T può essere costruito sul posto (con il posizionamento new) quando necessario.

Vorrei utilizzare questo schema per definire un allocatore personalizzato per i contenitori STL. Tuttavia, mi sembra che potrebbe esserci un problema qui per che ricongiunge. In effetti, se la mia comprensione è corretta, un allocatore STL dovrebbe supportare il rifacimento da un tipo T a un tipo U, ad es. perché contenitori come std::list<T> (o altri contenitori nodo-based come std::map) utilizzano allocatori allocare nodi che non sono effettivamente di tipo T, ma di diverso tipo U (contenente T e altri "header" informazioni overhead per il nodo). Quindi, il suddetto approccio std::aligned_storage funziona bene per il riavvolgimento? O (come penso) un allineamento corretto per T s non implica un allineamento corretto per un altro tipo diverso U?

Come si può risolvere questo problema?

Come potrei definire il sopracitato buffer per farlo funzionare anche per il rifasamento di un tipo diverso U?

Questo problema può essere attaccato da una prospettiva diversa? E allora?

+0

Non puoi semplicemente usare 'std :: alignment_of :: value' invece, per renderlo correttamente allineato per qualsiasi tipo supportato dagli assegnatori standard C++? Ovviamente questo non funzionerà per i tipi con requisiti di allineamento speciali (più rigorosi) (esempio migliore SSE), ma quelli sono alla fine sempre un problema, anche per gli allocatori standard. –

risposta

10

Sei sulla strada giusta.

Un dettaglio irritante è che le copie degli allocatori devono confrontare copie uguali, anche di conversione (rimbalzo). Il confronto di pari significa che possono deallocare i puntatori l'uno dell'altro. Quindi un contenitore come std::list<int> rebind your_alloc<int> a your_alloc<node<int>> e quindi creare un your_alloc<node<int>> utilizzando un your_alloc<int>. E tecnicamente il tuo your_alloc<node<int>> deve deallocare un puntatore allocato da your_alloc<int>.

Here è il mio tentativo di soddisfare questo requisito. Sentiti libero di copiare/modificare/abusare di questo codice. Il mio intento è quello di educare, non diventare il fornitore di allocatori del mondo (che comunque non sarebbe esattamente redditizio :-)).

Questo esempio richiede un approccio leggermente diverso rispetto all'allineamento: mi è capitato di sapere che sulla mia piattaforma di interesse (OS X, iOS) che malloc restituisce memoria allineata a 16 byte e quindi è necessario restituire tutto l'allocatore personalizzato. È possibile modificare tale numero in qualsiasi cosa sia appropriata per il proprio sistema.

Questa hardwiring di allineamento significa che un singolo pool in grado di fornire in modo sicuro diversi allocator<int> e allocator<node<int>>, in quanto sono tutti di 16 byte allineati (e basta). Significa anche che le copie, anche convertite, possono rilevare quando il puntatore punta nel buffer, poiché tutte le copie condividono lo stesso buffer.

Detto in modo leggermente diverso, il comitato C++ ha effettivamente specificato che gli allocatori sono tipi di riferimento: le copie sono equivalenti e puntano allo stesso pool di memoria.

È possibile farla franca, utilizzando pool di memoria effettivamente incorporati nell'allocatore, ma solo con alcuni contenitori su alcune implementazioni e non è possibile eseguire il backup con una citazione dallo standard.

+0

Grazie mille per i tuoi suggerimenti e per condividere il tuo codice di allocatore di campioni. –

+1

Quindi, se si desidera, ad esempio, l'allineamento a 64 byte (per scopi di allineamento della cache, ad es.) Da 'stack_allocator', è necessario ridefinire l'operatore globale' :: new' per fornire lo stesso allineamento a 64 byte? – TemplateRex

+1

@rhalbersma, si potrebbe fare, o si potrebbe sempre richiedere un '64 - alignof (std :: max_align_t)' aggiuntivo da ':: operator new' e quindi iniziare ad usarlo solo sul limite di 64 byte (ma si dovrebbe è necessario in qualche modo passare il puntatore non allineato originale a ':: operator delete') –

Problemi correlati