2009-11-14 13 views
5

Sto scrivendo un'estensione Matlab utilizzando la libreria ublas di C++ e mi piacerebbe essere in grado di inizializzare i miei vettori ublas dai C array passati dall'interpeter Matlab. Come posso inizializzare il vettore ublas da un array C senza (per motivi di efficienza) copiando esplicitamente i dati. Sto cercando qualcosa lungo le seguenti righe di codice:Inizializzazione di un vettore ublas da un array C

using namespace boost::numeric::ublas; 

int pv[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; 
vector<int> v (pv); 

In generale, è possibile inizializzare un C++ std::vector da un array? Qualcosa del genere:

#include <iostream> 
#include <vector> 
using namespace std; 

int main() 
{ 
    int pv[4] = { 4, 4, 4, 4}; 
    vector<int> v (pv, pv+4); 

    pv[0] = 0; 
    cout << "v[0]=" << v[0] << " " << "pv[0]=" << pv[0] << endl; 

    return 0; 
} 

ma dove l'inizializzazione non copierà i dati. In questo caso l'uscita è

v[0]=4 pv[0]=0 

ma voglio l'uscita sia la stessa, dove aggiornare la matrice C modifica i dati indicavano dal vettore C++

v[0]=0 pv[0]=0 

risposta

6

Sia std::vector e ublas::vector sono contenitori. L'intero punto dei contenitori è gestire lo spazio di archiviazione e la vita dei loro oggetti contenuti. Questo è il motivo per cui quando si inizializzano, devono copiare i valori nello spazio di cui sono proprietari.

I matrici di C sono aree di memoria fisse per dimensioni e posizione, quindi per loro natura è possibile copiare i loro valori in un contenitore.

È possibile utilizzare i C array come input per molte funzioni dell'algoritmo, quindi forse è possibile farlo per evitare la copia iniziale?

+2

Tranne che * in teoria * potresti creare una sottoclasse di ublas :: vector che ha fatto questo. La sottoclasse potrebbe comportarsi come un const ublas :: vector che non potrebbe mai essere ridimensionato, altrimenti bisognerebbe sovrascrivere tutti i metodi coinvolti nel ridimensionamento del contenitore per assicurare che non liberasse memoria a cui non appartiene . Solo un masochista completo ci proverebbe. –

0

non penso C++ consente quella convenzione come la C.

+1

Ovviamente lo fa. I puntatori sono solo un caso specifico di iteratori (dato un puntatore si può deferenza con * e si può ottenere l'elemento successivo con ++: questo è tutto ciò che serve per inizializzare un vettore std ::). –

4

È possibile inizializzare una std :: vector da un array C facilmente:

vector<int> v(pv, pv+10); 
+0

Grazie per la risposta, ma ciò copierebbe i dati. Voglio che 'v' e' pv' puntino allo stesso blocco di dati. – dzhelil

+1

Non puoi averlo. std :: vector possiede sempre la sua memoria. Puoi scrivere la tua classe vettoriale però ... – shoosh

9

Non sono sicuro di come la tua domanda si riferisca a MATLAB/MEX, ma una nota a margine, potresti voler sapere che MATLAB implementa una strategia copy-on-write.

Ciò significa che quando si copia una matrice, ad esempio, solo alcune intestazioni vengono effettivamente copiate, mentre i dati stessi sono condivisi tra i due array. E una volta che uno di questi viene modificato, viene effettivamente eseguita una copia dei dati.

La seguente è una simluation di quello che potrebbe accadere sotto il cofano (preso in prestito da questo old post):

----------------------------------------- 
>> a = [35.7 100.2 1.2e7]; 

mxArray a 
    pdata -----> 35.7 100.2 1.2e7 
    crosslink=0 

----------------------------------------- 
>> b = a; 

mxArray a 
    pdata -----> 35.7 100.2 1.2e7 
    crosslink /\ 
    |/\  | 
    | |  | 
    | |  | 
    \/|  | 
    crosslink  | 
mxArray b  | 
    pdata -------- 

----------------------------------------- 
>> a(1) = 1; 

mxArray a 
    pdata -----> (1) 100.2 1.2e7 
    crosslink=0 


    crosslink=0 
mxArray b 
    pdata ------> 35.7 100.2 1.2e7 ... 

So che questo in realtà non rispondere alla tua domanda, ho solo pensato che si potrebbe trovare il concetto utile.

+11

Puoi vedere questi meta-dati nel formato di impostazione della finestra di comando MATLAB con 'format debug' – Mikhail

+0

cool trick, grazie per aver condiviso – Amro

+0

Un punto secondario sul tuo diagramma - lo fai sembrare come se MATLAB creasse una nuova copia dei dati, riassegna 'b' per puntare ad esso e muta i dati a cui punta' a'. Quello che succede in realtà è che viene creata una nuova copia dei dati e * 'a' * viene riassegnato per puntare ad esso, e quindi i nuovi dati vengono modificati. –

3

Ecco un paio di funzioni per l'assegnazione sintatticamente conveniente (certamente non inizializzazione):

vector<int> v; 
setVector(v, 3, 
      1, 2, 3); 

matrix<int> m; 
setMatrix(m, 3, 4, 
      1, 2, 3, 4, 
      11, 22, 33, 44, 
      111, 222, 333, 444); 

Le funzioni:

/** 
* Resize a ublas vector and set its elements 
*/ 
template <class T> void setVector(vector<T> &v, int n, ...) 
{ 
    va_list ap; 
    va_start(ap, n); 
    v.resize(n); 
    for (int i = 0; i < n; i++) { 
     v[i] = va_arg(ap, T); 
    } 
    va_end(ap); 
} 

/** 
* Resize a ublas matrix and set its elements 
*/ 
template <class T> void setMatrix(matrix<T> &m, int rows, int cols ...) 
{ 
    va_list ap; 
    va_start(ap, cols); 
    m.resize(rows, cols); 
    for (int i = 0; i < rows; i++) { 
     for (int j = 0; j < cols; j++) { 
      m(i, j) = va_arg(ap, T); 
     } 
    } 
    va_end(ap); 
} 
2

Ci sono due classi irregolari in uBLAS storage.hpp. È possibile modificare la classe di archiviazione predefinita (unbounded_array) in ublas :: vector con uno di questi.

  • La prima classe, array_adaptor, crea una copia dei dati quando ublas :: vector chiama copy constructor, classe non molto utile. Preferirei semplicemente il costruttore appropriato per farlo nelle classi unbounded_array o bounded_array.
  • Il secondo, shallow_array_adaptor, contiene solo un riferimento ai dati dell'utente, quindi è possibile utilizzare il vettore per modificare direttamente l'array C. Sfortunatamente, ha alcuni bug, quando assegni un'espressione perde il puntatore dei dati originale. Ma puoi creare una classe derivata che risolva questo problema.

Ecco la patch e un esempio:

// BOOST_UBLAS_SHALLOW_ARRAY_ADAPTOR must be defined before include vector.hpp 
#define BOOST_UBLAS_SHALLOW_ARRAY_ADAPTOR 

#include <boost/numeric/ublas/vector.hpp> 
#include <algorithm> 
#include <iostream> 

// Derived class that fix base class bug. Same name, different namespace.  
template<typename T> 
class shallow_array_adaptor 
: public boost::numeric::ublas::shallow_array_adaptor<T> 
{ 
public: 
    typedef boost::numeric::ublas::shallow_array_adaptor<T> base_type; 
    typedef typename base_type::size_type     size_type; 
    typedef typename base_type::pointer      pointer; 

    shallow_array_adaptor(size_type n) : base_type(n) {} 
    shallow_array_adaptor(size_type n, pointer data) : base_type(n,data) {} 
    shallow_array_adaptor(const shallow_array_adaptor& c) : base_type(c) {} 

    // This function must swap the values ​​of the items, not the data pointers. 
    void swap(shallow_array_adaptor& a) { 
     if (base_type::begin() != a.begin()) 
     std::swap_ranges(base_type::begin(), base_type::end(), a.begin()); 
    } 
}; 

void test() { 
    using namespace boost::numeric; 
    typedef ublas::vector<double,shallow_array_adaptor<double> > vector_adaptor; 

    struct point { 
     double x; 
     double y; 
     double z; 
    }; 

    point p = { 1, 2, 3 }; 
    vector_adaptor v(shallow_array_adaptor<double>(3, &p.x)); 

    std::cout << p.x << ' ' << p.y << ' ' << p.z << std::endl; 
    v += v*2.0; 
    std::cout << p.x << ' ' << p.y << ' ' << p.z << std::endl; 
} 

uscita:

1 2 3 
3 6 9 
2

Il suggerimento del solito per utilizzare l'adattatore gamma superficiale sembra tipo di sarcastico a me - di essere in grado di semplicemente l'accesso un array attraverso un puntatore dovresti metterlo in una shared_array con tutto il reference counting shebang (che non arriva a nulla, dal momento che non possiedi la matrice) e cosa c'è di più con un incubo di data aliasing. In realtà, uBLAS ha un'implementazione completa dello storage (array_adaptor) che consente di utilizzare i vettori con i c array esterni. L'unica presa è il costruttore vettoriale che ne fa una copia. Perché questa bella caratteristica non viene utilizzata nella biblioteca è molto al di là di me, ma in ogni caso, possiamo usare una piccola estensione (in realtà 2 righe di codice circondate con la consueta C++ gonfiare)

template<class T> 
class extarray_vector : 
    public vector<T, array_adaptor<T> > 
{ 
    typedef vector<T, array_adaptor<T> > vector_type; 
public: 
    BOOST_UBLAS_INLINE 
    extarray_vector(size_type size, pointer p) 
    { data().resize(size, p); } 

    template <size_type N> 
    BOOST_UBLAS_INLINE 
    extarray_vector(T (&a)[N]) 
    { data().resize(N, a); } 

    template<class V> 
    BOOST_UBLAS_INLINE 
    extarray_vector& operator = (const vector<T, V>& v) 
    { 
     vector_type::operator = (v); 
     return *this; 
    } 

    template<class VC> 
    BOOST_UBLAS_INLINE 
    extarray_vector& operator = (const vector_container<VC>& v) 
    { 
     vector_type::operator = (v); 
     return *this; 
    } 

    template<class VE> 
    BOOST_UBLAS_INLINE 
    extarray_vector& operator = (const vector_expression<VE>& ae) 
    { 
     vector_type::operator = (ae); 
     return *this; 
    } 
}; 

è possibile utilizzarlo come questo :

int i[] = {1, 4, 9, 16, 25, 36, 49}; 
extarray_vector<int> iv(i); 
BOOST_ASSERT_MSG(i == &iv[0], "Vector should attach to external array\n"); 
iv[3] = 100; 
BOOST_ASSERT(i[3] == 100); 
iv.resize(iv.size() + 1, true); 
BOOST_ASSERT_MSG(i != &iv[0], "And detach from the array on resize\n"); 
iv[3] = 200; 
BOOST_ASSERT(i[3] == 100); 
iv.data().resize(7, i, 0); 
BOOST_ASSERT_MSG(i == &iv[0], "And attach back to the array\n"); 
BOOST_ASSERT(i[3] == 200); 

È possibile collegare in modo dinamico e staccare vettore di memorizzazione esterno tramite il metodo di ridimensionamento del array_adaptor (tenere o scartare i dati). Al ridimensionamento si stacca automaticamente dallo spazio di archiviazione e diventa vettore normale. L'assegnazione dai contenitori passa direttamente allo storage, ma l'assegnazione dall'espressione avviene tramite un temporaneo e il vettore viene staccato dall'archiviazione, utilizzare noalias() per impedirlo. C'è un piccolo overhead nel costruttore dato che data_ è membro privato e dobbiamo inizializzarlo con la nuova T [0], quindi riassegnare alla matrice esterna. Puoi cambiarlo in protetto e assegnarlo allo storage direttamente nel costruttore.