2015-04-02 11 views
10

Häufig muss ich den Anfang eines dynamischen Arrays an eine 16, 32 oder 64 Byte Grenze für die Vektorisierung ausrichten, z. B. für SSE, AVX, AVX-512. Ich suche nach einer transparenten und sicheren Möglichkeit, dies in Verbindung mit intelligenten Zeigern zu verwenden, insbesondere std::unique_ptr.Ausgerichtetes dynamisches Array und intelligenter Zeiger

eine Implementierung von Zuweisung und Freigabe-Routinen gegeben, sagen

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

ich so etwas wie dieses

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

tun möchte, aber ich konnte nicht herausfinden, wie unique_ptr zu tun bekommen das verwenden richtige Deleter, ohne vom Benutzer zu verlangen, es zu spezifizieren (was eine mögliche Ursache für Fehler ist). Die Alternative war ich fand eine Vorlage Alias ​​verwenden

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

Dann können wir

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

Das verbleibende Problem verwenden ist, dass nichts den Benutzer davon ab, den rohen Zeiger in einen std::unique_ptr hält.

Abgesehen davon, sehen Sie irgendetwas falsch damit? Gibt es eine Alternative, die weniger fehleranfällig ist, aber für den Benutzer nach der Zuweisung vollständig transparent ist?

Antwort

10

Sie sollten nie einen eigenen rohen Zeiger zurückgeben. allocate_aligned verletzt das. Ändern Sie den entsprechenden Smart-Pointer stattdessen zurück:

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

Auf diese Weise kann kein Client den Rohzeiger in einem ungeeigneten Smart-Pointer setzen, weil sie nie die Rohzeiger an erster Stelle bekommen. Und Sie vermeiden Speicherlecks, indem Sie den rohen Zeiger nicht versehentlich in einen intelligenten stecken.

Wie @KonradRudolph wies darauf hin, der Standard selbst geht auf diese Weise — in C++ 14, std::make_unique ist genau so ein Wrapper für Plain new.

+2

Es ist vielleicht erwähnenswert, dass dies im Wesentlichen das ist, was 'std :: make_unique' in' 'new' 'in C++ 14 macht. –

+0

@KonradRudolph Guter Punkt, bearbeitet in. – Angew