2011-02-04 9 views
6

¿Alguien sabe de una implementación de STL que permite asignar asignadores dinámicos a una instancia de un contenedor antes de su uso?¿Implementación de STL que usa un asignador dinámico/basado en estado?

El escenario es que tenemos un asignador de memoria general que gestiona un número de grupos de memoria y para cada instancia de stl :: vector queremos asignar cada instancia desde un grupo de memoria diferente.

El problema con las implementaciones estándar de STL es que solo se puede definir el grupo de memoria por tipo, es decir, todos los vectores de tipo int se asignarían desde el mismo grupo.

Ya he cambiado nuestro stl :: allocator predeterminado por uno que tiene un estado, es decir, el grupo del que queremos asignar esta instancia, pero esto no funciona bien para decir stl :: list donde asigna cosas en el ctor predeterminado.

Por motivos relacionados con nuestra biblioteca, tampoco tenemos un grupo válido en el controlador para todos los objetos, por lo que queremos llamar a una función 'establecer grupo de memoria' antes de que los usuarios puedan usar el contenedor stl.

¿Alguien ha encontrado una implementación que admite este tipo de cosas?

+0

¿Es ese Microsoft STL que asigna el nodo principal de la lista en su constructor? La implementación ideal de STL (leer GNU) no usaría ninguna asignación de memoria al construir contenedores vacíos. –

+0

Sí, tanto Microsoft como mi puerto GNU (circa gcc 3.4.1) asignan el nodo principal en el ctor. STLPort, por otro lado, no y por lo tanto, esto es compatible con mis requisitos, consulte mi respuesta para obtener una fuente de ejemplo completa. – user176168

Respuesta

2

El asignador tipado puede usar un asignador general debajo para realizar las asignaciones.

Allocator debe ser compatible con estas funciones:

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); 

Asumiendo que tiene un mecanismo que simplemente asigna memoria y desasigna la memoria, puede usar eso para poner en práctica algunas de las funciones anteriormente.

La ventaja de los asignadores que se tipean es que usted sabe que va a crear muchos elementos del mismo tamaño y puede crear sus "páginas" para que quepan. El mayor problema puede ser que forzado por allocate() obligue a devolver búferes contiguos (y de hecho el vector los necesita).

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

Su pregunta es todavía un poco confuso en cuanto a por qué esto no es suficiente. Puede inicializar el grupo de memoria con la lógica "una vez". (Si es multiproceso, puede usar boost :: una vez para lograr esto).

+0

Tenga en cuenta también si desea varias instancias distintas del mismo asignador, y es lo suficientemente bueno para simplemente cambiar entre ellas en tiempo de compilación, también puede pasar un entero o un argumento de tipo de etiqueta a un asignador de plantillas. – bdonlan

0

Una opción sería usar una variable de subproceso local para mantener el puntero al grupo de memoria que se va a usar y capturar esto en la implementación de su asignador. Por ejemplo (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 

El API que myallocator deben implementar se describe here.

Desafortunadamente, debido a limitaciones en la API de STL, esto es tan bueno como puede obtener sin volver a implementar el STL. Sin embargo, puede haber bibliotecas de terceros que le permitan pasar un asignador al constructor del objeto por alguna parte.

1

El problema con el estándar STL implementaciones es que sólo se puede definir el bloque de memoria en una base tipo es decir, todos del vector de tipo int se asignar del mismo grupo.

Esto no es exactamente así. Puede tener diferentes vectores que contengan int elementos cada uno con un tipo de asignador diferente.

Sin embargo, en cuanto a la pregunta -

¿Alguien sabe de un aplicación STL que permite dinámicos asignadores de ser pasados ​​a un ejemplo de un envase antes de su uso.

- simplemente no es compatible con la Biblioteca Estándar de C++ (STL), por lo tanto, si bien puede haber implementaciones donde los asignadores por objeto funcionen, no es portátil.

Para un análisis detallado de por qué y cómo utilizar los asignadores personalizados ver Scott Meyers's libro Effective STL, específicamente Tema 11: Entender el uso legítimo de los asignadores personalizados.

4

No estoy tan seguro de su pregunta con precisión. Así que cubriré el caso de un asignador completo del estado.

En C++ 03, cualquier asignador debe ser capaz de desasignar los recursos asignados por otro asignador del mismo tipo.

El estándar C++ 0x en realidad elimina esta limitación y permite asignadores completo de estado para pasar a contenedores STL, siempre y cuando son asignador Aware contenedores (Creo que cubre todos los recipientes envasados ​​con la STL, ya que modelo Secuencia). Por ejemplo: [allocator.adaptor] $20.10 Class scoped_allocator ahora es parte de C++ 0x STL

0

Ok, parece que el puerto STL admite este tipo de funcionalidad, mientras que Microsoft (VS 2008) y la implementación de GNU (un puerto de stl circa gcc 3.4.1) no lo hacen ya que asignan/desasignan cosas en el ctors/dtors.

Aquí está mi código de prueba que muestra cómo hacerlo. ¡Advertencia que no es una implementación completa de ninguna manera!

#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; 
} 
Cuestiones relacionadas