2011-02-04 12 views
6

Qualcuno sa di un'implementazione STL che consente di passare gli allocatori dinamici a un'istanza di un contenitore prima dell'utilizzo.Un'implementazione STL che utilizza un allocatore dinamico/a stato?

Lo scenario è che abbiamo un allocatore di memoria generale che gestisce un numero di pool di memoria e per ogni istanza di dire stl :: vector vogliamo allocare ogni istanza da un diverso pool di memoria.

Il problema con le implementazioni STL standard è che è possibile definire il pool di memoria solo su un tipo, vale a dire che tutti i vettori di tipo int dovrebbero allocarsi dallo stesso pool.

Ho già scambiato il nostro stl di default :: allocatore per uno che ha uno stato cioè la piscina che vogliamo destinare questo caso da, ma questo non funziona bene per dire stl :: lista in cui assegna le cose in il ctor predefinito.

Per ragioni correlate alla nostra libreria, non abbiamo un pool valido nel ctor per tutti gli oggetti e quindi vogliamo chiamare una funzione 'set memory pool' prima che gli utenti possano usare il contenitore stl.

Qualcuno si è imbattuto in un'implementazione che supporta questo genere di cose?

+0

è che Microsoft STL che alloca il nodo principale della lista nel suo costruttore? L'implementazione STL ideale (leggi GNU) non userebbe alcuna allocazione di memoria durante la costruzione di contenitori vuoti. –

+0

Sia Microsoft che la mia porta GNU (circa gcc 3.4.1) allocano entrambi il nodo principale nel ctor. STLPort d'altra parte non lo è e quindi questo supporta i miei requisiti vedere la mia risposta per l'intera fonte di esempio. – user176168

risposta

2

L'allocatore tipizzato può utilizzare un allocatore generale sottostante per eseguire le assegnazioni.

allocatore deve supportare queste funzioni:

pointer address (reference x) const; 
    const_pointer address (const_reference x) const; 
    pointer allocate (size_type n, allocator<void>::const_pointer hint=0); 
    void deallocate (pointer p, size_type n); 
    size_type max_size() const throw(); 
    void construct (pointer p, const_reference val); 

Supponendo di avere un meccanismo che appena alloca la memoria e rilascia la memoria, è possibile utilizzare tale per implementare alcune delle funzioni sopra.

Il vantaggio degli allocatori digitati è che sapete che creerete molti oggetti della stessa dimensione e quindi potrete creare le vostre "pagine" per adattarle. Il problema più grande può essere il fatto che sei costretto da allocate() a restituire buffer contigui (e in effetti il ​​vettore ne ha bisogno).

http://www.cplusplus.com/reference/std/memory/allocator/

La tua domanda è ancora un po 'poco chiaro il motivo per cui questo non è sufficiente. È possibile inizializzare il pool di memoria con una logica "una volta". (Se è multi-thread è possibile utilizzare boost :: una volta per ottenere ciò).

+0

Tenete a mente anche se volete diverse istanze distinte dello stesso allocatore, ed è sufficiente passare semplicemente da una all'altra in fase di compilazione, potete anche passare un intero o un argomento di tipo tag ad un allocatore di modelli. – bdonlan

0

Un'opzione consisterebbe nell'utilizzare una variabile locale del thread per mantenere il puntatore nel pool di memoria da utilizzare e per catturarlo nell'implementazione di allocator. Ad esempio (usando boost::thread_specific_ptr):

// Global variable 
boost::thread_specific_ptr<allocator_impl> real_allocator; 

struct set_allocator : boost::noncopyable { 
private: 
    allocator_impl *old; 
public: 
    set_allocator(allocator_impl *newAllocator) { 
     old = real_allocator.get(); 
     real_allocator.reset(newAllocator); 
    } 
    ~set_allocator() { 
     real_allocator.reset(old); 
    } 
}; 

template<typename T> 
struct myallocator { 
private: 
    real_allocator *delegate; 
public: 
    myallocator() { 
     delegate = real_allocator.get(); 
    } 
    T *allocate(size_t n, allocator<void>::const_pointer hint=0) 
    { 
     return delegate->allocate(sizeof(T), n, hint); 
    } 
    // Other mandatory STL Allocator functions and typedefs follow 
}; 

// later: 
allocator_impl *allocator = new allocator_impl(); 
set_allocator sa(allocator); // Set current allocator using RAII 
std::list<int, myallocator> mylist; // using *allocator as the backing allocator 

L'API che myallocator devono attuare descritto here.

Sfortunatamente, a causa delle limitazioni dell'API STL, questo è il massimo che si può ottenere senza reimplementare il STL. Potrebbero tuttavia esserci librerie di terze parti che consentono di passare un allocatore al costruttore dell'oggetto da qualche parte.

1

Il problema con il standard STL implementazioni è che si può solo definire il lotto di memoria su una base di tipo cioè tutti vettore di di tipo int sarebbero assegnare dallo stesso pool.

Questo non è esattamente vero. Puoi avere diversi vettori che contengono gli elementi int ognuno con un diverso tipo di allocatore.

Tuttavia, per quanto riguarda la questione -

Qualcuno sa di un implementazione STL che permette dinamiche allocatori da passare in un un'istanza di un contenitore prima dell'uso.

- semplicemente non è supportato dalla libreria standard C++ (STL), quindi, mentre può darsi che ci siano implementazioni in cui gli allocatori per oggetto funzionano, non è portabile.

Per un'analisi dettagliata del perché e come utilizzare ripartitori personalizzati vedere Scott Meyers's libro Effective STL, specificamente Articolo 11: Capire l'uso legittimo della ripartitori personalizzati.

4

Non sono molto sicuro della tua domanda. Quindi coprirò il caso di un allocatore di stato completo.

In C++ 03, qualsiasi allocatore deve essere in grado di deallocare le risorse allocate da un altro allocatore dello stesso tipo.

Lo standard C++ 0x effettivamente rimosso questa limitazione e consente allocatori stato pieno da passare a contenitori STL purché siano Allocator Aware contenitori (Credo che copre tutti i contenitori confezionati con lo STL, poiché modello Sequenza).

Ad esempio: [allocator.adaptor] $20.10 Class scoped_allocator fa ora parte del C++ 0x STL.

0

Ok così la porta STL sembra supportare questo tipo di funzionalità, dove come Microsoft (VS 2008) e l'implementazione GNU (una porta di stl circa gcc 3.4.1) non assegnano/deallocano le cose nel ctors/dtors.

Ecco il mio codice di prova che mostra come procedere. Attenzione, non è un'implementazione completa con qualsiasi mezzo!

#include <list> 
#include <assert.h> 

namespace my 
{ 

class memory_pool 
{ 
public: 
    memory_pool() : m_top(0){}; 
    ~memory_pool(){}; 

    void* alloc(int size, int align) { void* p = (void*)&m_pool[m_top]; m_top += size; return p; } 
    void free (void* p) { assert((p >= m_pool) && (p < &m_pool[m_top])); } 
private: 
    char m_pool[0xFFFF]; 
    int m_top; 
}; 

template<class T> 
class dynamic_allocator 
{ 
    template<typename U> friend class dynamic_allocator; 
public: 
    typedef T     value_type;   
    typedef size_t    size_type;   
    typedef value_type*   pointer;    
    template <class _Tp1> struct rebind { typedef dynamic_allocator<_Tp1> other; }; 

    dynamic_allocator() : m_pPool(NULL){} 
    dynamic_allocator(memory_pool* pPool){ m_pPool = pPool; } 
    dynamic_allocator(const dynamic_allocator<T>& alloc) : m_pPool(alloc.m_pPool){} 
    template< typename U > 
    dynamic_allocator(const dynamic_allocator<U>& alloc) : m_pPool(alloc.m_pPool){} 
    ~dynamic_allocator() {} 

    pointer allocate(size_type count){ return allocate(count, NULL); } 
    pointer allocate(size_type count, const void*) { assert(m_pPool); return (pointer)m_pPool->alloc(count * sizeof(T), __alignof(T)); } 
    void deallocate(pointer p, size_type count) { assert(m_pPool); m_pPool->free(p); } 

    void set(memory_pool* pPool) { m_pPool = pPool; } 

private: 
    memory_pool* m_pPool; 
}; 

template< typename T, typename Al = dynamic_allocator<T> > 
class list : public std::list<T, Al> 
{ 
public: 
    typedef typename std::list<T, Al>::allocator_type allocator_type; 

    list() : std::list<T, Al>(){}; 
    list(const allocator_type& a) : std::list<T, Al>(a){}; 
    ~list(){}; 

    void initialise(memory_pool& pool){ std::list<T, Al>::_M_node.set(&pool); } // or something like this 
    void terminate(void){ clear(); std::list<T, Al>::_M_node.set(NULL); }     // or something like this 
}; 

}; // namespace my 

class lemon 
{ 
public: 
    lemon(){}  // must be empty ctor as we don't want to have active mem pool in ctor for users to use 
    ~lemon(){} 

    void initialise(my::memory_pool& pool){ m_list.initialise(pool); } 
    void terminate(void)     { m_list.terminate(); } 

    void add(float f) { m_list.push_back(f); } 

private: 
    my::list<float> m_list; 
}; 

int main(void) 
{ 
    my::memory_pool poolA; 
    my::memory_pool poolB; 

    my::dynamic_allocator<float> aa(&poolA); 
    my::list<float> a(aa); 
    my::list<float> fail; 

    std::list<float>::allocator_type bb; 
    std::list<float> b(bb); 

    a.push_back(0.2f); 
    b.push_back(50.0f); 
    //fail.push_back(19.0f); 

    a.clear(); 
    b.clear(); 

    lemon lemons[2]; 

    lemons[0].initialise(poolA); 
    lemons[1].initialise(poolB); 

    lemons[0].add(10.0f); 
    lemons[1].add(20.0f); 
    lemons[1].add(18.0f); 

    lemons[0].terminate(); 
    lemons[1].terminate(); 

    scanf("press any key\n"); 

    return 0; 
} 
Problemi correlati