2015-03-10 15 views
5

Stiamo cercando di implementare un nuovo codice C++ nel mio gruppo di ricerca per eseguire simulazioni numeriche di grandi dimensioni (elementi finiti, metodi di differenza finiti, ottimizzazione della topologia, ecc.) Il software sarà utilizzato da persone provenienti dal mondo accademico e industriale.Come scrivere una classe wrapper libreria di terze parti attorno a modelli di espressione

Per la parte algebra lineare densa del software, vogliamo utilizzare Eigen o Armadillo. Desideriamo costruire un wrapper attorno a questi pacchetti per due motivi: 1. per esporre la nostra API agli utenti piuttosto che all'API di terze parti; e 2. nel caso in cui avessimo bisogno di cambiare libreria in futuro. Capisco che la ragione 2 è una forma di assicurazione molto costosa, ma abbiamo riscontrato questa situazione con il nostro precedente software di simulazione.

Le informazioni che ho incontrato per quanto riguarda il confezionamento librerie di terze parti proviene da queste fonti:

mia domanda riguarda per quanto riguarda il modo migliore per costruisci questa classe di wrapper. Idealmente, un involucro a strato sottile sarebbe il migliore, come:

template< typename T > 
class my_vec { 
private: 
    arma::Col<T> _arma_vec; 
}; 

o equivalente con un vettore Eigen.

Poi, la mia classe avrebbe chiamato la classe libreria di terze parti come:

my_vec::foo() { return _arma_vec.foo(); } 

penso (e vorrei conferma su questo) che il problema con questo sottile strato è che perdo la velocità acquisita dai modelli di espressione queste librerie hanno implementato sotto il cofano. Ad esempio, nel Armadillo, la seguente operazione:

// Assuming these vectors were already populated. 
a = b + c + d; 

diventa qualcosa di simile:

for (std::size_t i = 0; i < a.size(); ++i) { 
    a[i] = b[i] + c[i] + d[i]; 
} 

senza creare provvisori a causa della loro attuazione expression templates. La stessa situazione si applica a Eigen.

Per quanto ne so, il motivo per cui perdo il potere dei modelli di espressione è che, mentre Armadillo o Eigen non creano temporari propri, la mia classe my_vec lo fa. L'unico modo per aggirare questo problema sarebbe quello di costruire un wrapper a strato sottile attorno ai loro modelli di espressione. Tuttavia, a questo punto, questa sembra essere una violazione del principio YAGNI.

Questa domanda relativa qui:

suggerisce di utilizzare qualcosa come:

my_vec a, b, c; 
// ... populate vectors 
a._arma_vec = b._arma_vec + c._arma_vec; 

E 'possibile usare qualcosa come questo, invece?

template< typename T > 
arma::Col<T> & 
my_vec<T>::data() { return _arma_vec; } 

a.data() = b.data() + c.data(); 

Oppure utilizzare un sovraccarico dell'operatore per nascondere i dati() dall'utente? Quali altre alternative ci sono se non vogliamo usare direttamente le librerie? Usando le macro? Usando gli alias se decidiamo di usare C++ 11?

O quale sarebbe il modo più conveniente per creare questa classe wrapper?

risposta

1

Solo per riferimento futuro, questo è il modo ho deciso di implementare la mia soluzione: ho sovraccaricato l'operatore + nel seguente modo:

template< typename T1, typename T2 > 
auto 
operator+(
     const my_vec<T1> & X, 
     const my_vec<T2> & Y) ->decltype(X.data() + Y.data()) 
{ 
    return X.data() + Y.data(); 
} 

template< typename T1, typename T2 > 
auto 
operator+(
     const my_vec<T1> & X, 
     const T2 &   Y) ->decltype(X.data() + Y) 
{ 
    return X.data() + Y; 
} 

template< typename T1, typename T2 > 
auto 
operator+(
     const T1 &   X, 
     const my_vec<T2> & Y) ->decltype(X + Y.data()) 
{ 
    return X + Y.data(); 
} 

Poi, ho sovraccaricato il mio operatore = in classe my_vec con il seguente:

template< typename T > 
template< typename A > 
const my_vec<T> & 
my_vec<T>::operator=(
     const A & X) 
{ 
    _arma_vec = X; 

    return *this; 
} 
+0

Appena incappato in questa domanda - e sto anche cercando di raggiungere un obiettivo simile. Alcune domande mi sono venute in mente guardando il tuo codice: 1. La classe wrapper ha solo un membro privato vettoriale? Per un membro matrix, hai scritto un'altra classe wrapper? 2. Come è stata realizzata la moltiplicazione del vettore matrice? 3. La vostra implementazione preserva l'efficienza dei modelli di espressione? – endbegin

+0

1. Sì. O una riga o un membro privato di Matrix. Puoi scrivere un wrapper per ogni classe (Col, Row, Mat), o semplicemente usare una matrice per ogni cosa e scrivere un singolo wrapper. 2. È possibile scrivere più wrapper o un singolo wrapper, come detto in 1. Quindi è necessario sovraccaricare gli operatori. 3. È efficiente, ma mai efficiente quanto l'implementazione non elaborata. Direi che perdiamo ovunque tra l'1 e il 10% di efficienza nel tempo di calcolo. Ciò che otteniamo è la possibilità di passare da una libreria all'altra. Al momento, possiamo passare facilmente da Eigen a Armadillo, senza modificare l'API pubblica. –

+0

Ho cercato di scrivere una singola classe di wrapper Matrix sia per le matrici che per i vettori solo così posso mettere gli operatori sovraccaricati in un file wrapper. Tuttavia, Eigen non rende facile farlo. Mi piacerebbe avere un solo membro privato - una matrice non inizializzata - che può essere ridimensionata per essere sia una matrice MxN o un vettore Mx1, ma le operazioni vettoriali Eigen (come dot prod) non funzionano perché un vettore deve essere inizializzato come una matrice . – endbegin

Problemi correlati