2009-11-11 10 views
8

Necesito envolver una matriz dinámicamente asignada (de a = new double [100] por ejemplo) en std :: vector (preferiblemente) sin copiar la matriz. Esta restricción se impone porque la matriz que quiero envolver está mapeada desde un archivo, por lo que al hacer el vector (a, a + size) se duplicará el uso de la memoria.¿Cómo envolver la matriz dinámica en el contenedor STL/Boost?

¿Hay algún truco para hacer eso?

+1

De todos modos no obtendrá todo el conjunto de capacidades de std :: vector (por ejemplo, cambiar el tamaño, probablemente no funcionará), ¿entonces podría ser bueno enumerar qué es exactamente lo que necesita de la envoltura? – maxim1000

Respuesta

11

Una de las mejores soluciones para esto es algo así como STLSoft's array_proxy<> plantilla. Lamentablemente, la página de documento generada a partir del código fuente por doxygen no ayuda mucho a comprender la plantilla. El código fuente en realidad podría ser un poco mejor:

La plantilla array_proxy<> se describe muy bien en Matthew Wilson's book, Imperfect C++. La versión que he usado es una versión reducida de lo que hay en el sitio de STLSoft, así que no tuve que recurrir a toda la biblioteca. Mi versión no es tan portátil, pero eso lo hace mucho más simple que lo que está en STLSoft (que salta a través de una gran cantidad de aros de portabilidad).

Si configura una variable de este modo:

int myArray[100]; 

array_proxy<int> myArrayProx(myArray); 

La variable myArrayProx tiene muchas de las interfaces de STL - begin(), end(), size(), iteradores, etc.

lo tanto, en muchos sentidos, el objeto array_proxy<> se comporta como un vector (aunque no es push_back() allí desde el array_proxy<> no puede crecer - no gestionar la memoria de la matriz, sólo se envuelve en algo un poco más cerca a un vector).

Una cosa realmente agradable con array_proxy<> es que si los usa como tipos de parámetros de función, la función puede determinar el tamaño de la matriz pasada, lo que no es cierto para las matrices nativas. Y el tamaño del conjunto envuelto no forma parte del tipo de plantilla, por lo que es bastante flexible de usar.

+1

+1, parece ser la mejor solución, especialmente porque el proxy no debe reasignar memoria por lo que debe estar libre de muchos problemas. –

1

No, eso no es posible usando un std::vector.

Pero, si es posible, puede crear el vector con este tamaño, y posiblemente asigne el archivo a ese tamaño.

std::vector<double> v(100); 
mmapfile_double(&v[0], 100); 
1

¿Qué pasa con el vector de punteros que apuntan a los elementos de su área mapeada (consumo de memoria reducido como sizeof (double *) < sizeof (doble))? ¿Esto está bien para ti?

Hay algunos inconvenientes (es primordial que necesite predicados especiales para el ordenamiento) pero también se beneficia cuando puede, por ejemplo, eliminar elementos sin cambiar el contenido mapeado real (o tener incluso un número de matrices con diferente orden de elementos sin cualquier cambio a los valores reales).

Hay un problema común de todas las soluciones con std :: vector en el archivo mapeado: para 'clavar' el contenido vectorial en el área asignada. No se puede rastrear, solo puedes mirar para no utilizar algo que pueda redistribuir el contenido vectorial. Así que ten cuidado en cualquier caso.

2

Una vez estaba decidido a lograr exactamente lo mismo. Después de unos días de pensar e intentar, decidí que no valía la pena. Terminé creando mi propio vector personalizado que se comportaba como std :: vector pero solo tenía la funcionalidad que realmente necesitaba, como verificación encuadernada, iteradores, etc.

Si aún desea usar std :: vector, la única forma en que podría pensar en aquel entonces era crear un asignador personalizado. Nunca he escrito uno, pero ya que esta es la única manera de controlar la administración de memoria de STL, tal vez haya algo que se pueda hacer allí.

+2

Sugiero no escribir un contenedor propio. La mayoría de las veces simplemente no vale la pena y genera bastantes problemas de interoperabilidad. Además, el tiempo dedicado a la depuración de cosas reinventadas es tiempo perdido. Sin embargo, el asignador personalizado es probablemente bastante fácil de implementar y tiene un mejor enfoque. – MP24

+0

Normalmente estoy de acuerdo, pero realmente creo que depende de lo que vayas a hacer con él. La mayoría de las operaciones "complejas" en std :: vector probablemente tienen algo que ver con la administración de la memoria y sucede que en este caso no se requiere ninguna de ellas. Por lo tanto, escribir una clase pequeña con comprobación de límites y un puntero como iterador no debería llevar demasiado tiempo. No puedo comentar sobre la interoperabilidad ya que no tengo conocimiento de dónde/cómo el autor pretende usarlo. Pero la compatibilidad con iteradores debería hacer que funcione con los algoritmos de STL. –

+0

Entonces, ¿tiene un enlace al código para esto? – einpoklum

1

Puedes ir con array_proxy <>, o echar un vistazo a Boost.Array. Le da tamaño(), frente(), atrás(), en(), operador [], etc. Personalmente, preferiría Boost.Array ya que Boost es más frecuente de todos modos.

+0

-1, Boost.Array copia el contenido de la matriz – CharlesB

9

A boost::iterator_range proporciona una interfaz de contenedor-como:

// Memory map an array of doubles: 
size_t number_of_doubles_to_map = 100; 
double* from_mmap = mmap_n_doubles(number_of_doubles_to_map); 

// Wrap that in an iterator_range 
typedef boost::iterator_range<double*> MappedDoubles; 
MappedDoubles mapped(from_mmap, from_mmap + number_of_doubles_to_map); 

// Use the range 
MappedDoubles::iterator b = mapped.begin(); 
MappedDoubles::iterator e = mapped.end(); 
mapped[0] = 1.1; 
double first = mapped(0); 

if (mapped.empty()){ 
    std::cout << "empty"; 
} 
else{ 
    std::cout << "We have " << mapped.size() << "elements. Here they are:\n" 
     << mapped; 
} 
+1

La mejor respuesta para mí, ¡ya que usa boost! – CharlesB

1

bien, la plantilla vector permite que proporcionar su propio asignador de memoria. Nunca lo hice yo mismo, pero supongo que no es tan difícil hacer que apunte a tu matriz, tal vez con la colocación de un nuevo operador ... solo una conjetura, escribo más si intento tener éxito.

0

Aquí tiene la solución a su pregunta. Había estado intentando esto sin parar durante bastante tiempo antes de encontrar una solución viable. La advertencia es que tienes que poner a cero los punteros después de su uso para evitar duplicar la memoria.

#include <vector> 
#include <iostream> 

template <class T> 
void wrapArrayInVector(T *sourceArray, size_t arraySize, std::vector<T, std::allocator<T> > &targetVector) { 
    typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *vectorPtr = 
    (typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *)((void *) &targetVector); 
    vectorPtr->_M_start = sourceArray; 
    vectorPtr->_M_finish = vectorPtr->_M_end_of_storage = vectorPtr->_M_start + arraySize; 
} 

template <class T> 
void releaseVectorWrapper(std::vector<T, std::allocator<T> > &targetVector) { 
    typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *vectorPtr = 
     (typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *)((void *) &targetVector); 
    vectorPtr->_M_start = vectorPtr->_M_finish = vectorPtr->_M_end_of_storage = NULL; 
} 

int main() { 

    int tests[6] = { 1, 2, 3, 6, 5, 4 }; 
    std::vector<int> targetVector; 
    wrapArrayInVector(tests, 6, targetVector); 

    std::cout << std::hex << &tests[0] << ": " << std::dec 
      << tests[1] << " " << tests[3] << " " << tests[5] << std::endl; 

    std::cout << std::hex << &targetVector[0] << ": " << std::dec 
      << targetVector[1] << " " << targetVector[3] << " " << targetVector[5] << std::endl; 

    releaseVectorWrapper(targetVector); 
} 

alternativa usted puede simplemente hacer una clase que hereda de vectores y nulos los punteros a la destrucción:

template <class T> 
class vectorWrapper : public std::vector<T> 
{ 
public: 
    vectorWrapper() { 
    this->_M_impl _M_start = this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = NULL; 
    } 

    vectorWrapper(T* sourceArray, int arraySize) 
    { 
    this->_M_impl _M_start = sourceArray; 
    this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = sourceArray + arraySize; 
    } 

    ~vectorWrapper() { 
    this->_M_impl _M_start = this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = NULL; 
    } 

    void wrapArray(T* sourceArray, int arraySize) 
    { 
    this->_M_impl _M_start = sourceArray; 
    this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = sourceArray + arraySize; 
    } 
}; 
Cuestiones relacionadas