2015-04-02 4 views
10

Ho spesso bisogno di allineare l'inizio di una matrice dinamica a un limite di 16, 32 o 64 byte per la vettorizzazione, ad es. Per SSE, AVX, AVX-512. Sto cercando un modo trasparente e sicuro per utilizzarlo in combinazione con i puntatori intelligenti, in particolare std::unique_ptr.Array dinamico e puntatore intelligente allineati

Dato un implementazione di allocazione e deallocazione routine, dicono

template<class T> 
T * allocate_aligned(int alignment, int length) 
{ 
    // omitted: check minimum alignment, check error 
    T * raw = 0; 
    // using posix_memalign as an example, could be made platform dependent... 
    int error = posix_memalign((void **)&raw, alignment, sizeof(T)*length); 
    return raw; 
} 

template<class T> 
struct DeleteAligned 
{ 
    void operator()(T * data) const 
    { 
     free(data); 
    } 
}; 

mi piacerebbe fare qualcosa di simile

std::unique_ptr<float[]> data(allocate_aligned<float>(alignment, length)); 

ma non riuscivo a capire come si ottiene unique_ptr di utilizzare la corretto Deleter senza richiedere all'utente di specificarlo (che è una potenziale causa di errori). L'alternativa che ho trovato è stato quello di utilizzare un modello alias

template<class T> 
using aligned_unique_ptr = std::unique_ptr<T[], DeleteAligned<T>>; 

Poi possiamo usare

aligned_unique_ptr<float> data(allocate_aligned<float>(alignment, length)); 

Il restante problema è che nulla mantiene l'utente di mettere il puntatore del grezzo in un std::unique_ptr.

A parte questo, vedi qualcosa di sbagliato in questo? Esiste un'alternativa meno incline agli errori, ma completamente trasparente per l'utente dopo che è stata effettuata l'allocazione?

risposta

10

Non si dovrebbe mai restituire un puntatore raw proprietario. allocate_aligned viola questo. Cambiarlo in restituire il puntatore intelligente appropriata invece:

template<class T> 
std::unique_ptr<T[], DeleteAligned<T>> allocate_aligned(int alignment, int length) 
{ 
    // omitted: check minimum alignment, check error 
    T * raw = 0; 
    // using posix_memalign as an example, could be made platform dependent... 
    int error = posix_memalign((void **)&raw, alignment, sizeof(T)*length); 
    return std::unique_ptr<T[], DeleteAligned<T>>{raw}; 
} 

In questo modo, nessun cliente può mettere il puntatore grezza in un puntatore intelligente inadeguato perché non hanno mai ottenere il puntatore del grezzo, in primo luogo. E tu previ che le perdite di memoria non accidentalmente si inseriscano il puntatore grezzo in uno intelligente.

Come ha sottolineato @KonradRudolph, lo standard stesso sta andando in questo modo — in C++ 14, std::make_unique è esattamente un tale wrapper per semplice new.

+2

Vale la pena ricordare che questo è essenzialmente ciò che 'std :: make_unique' sta facendo per' new' in C++ 14. –

+0

@KonradRudolph Buon punto, modificato in. – Angew