2016-04-17 6 views
1

Ich arbeite viel mit Windows-API. Jedes Mal, wenn ich eine Art von Betriebssystem-Ressource (wie ein Handle. Registrierungsschlüssel, Socket usw.) bekomme, wickle ich es manuell mit einer Art von unique_ptr<HandleType,SomeDeleter>, um mit dem richtigen Schließen dieses Handle umzugehen.toUniquePtr Implementierung

Ich versuche eine Lightwight toUniquePtr Funktion zu erstellen, die eine Handle und eine Closing-Funktion erhält und automatisch eine unique_ptr aus den beiden erstellt.

für exmaple:

auto ptr = CreateFile(/**/); 
auto unique = toUniquePtr(ptr,&CloseHandle); 

Meine Implementierung bisher:

template <class T, class ClosingFunction> 
struct CostumeDeleter { 

    ClosingFunction closingFunction; 

    CostumeDeleter(ClosingFunction closingFunction_) : 
     closingFunction(closingFunction_) {} 

    CostumeDeleter(const CostumeDeleter&) = default; 
    CostumeDeleter(CostumeDeleter&&) = default; 

    void operator() (T t) { 
     closingFunction(t); 
    } 
}; 

template <class T, class F> 
inline auto toUniquePtr(T t, F f) { 
    CostumeDeleter<T, F> deleter(f); 
    std::unique_ptr<T, decltype(deleter)> pointer(t, deleter); 
    return std::move(pointer); 
} 

Beispiel Verwendung + Übersetzungsfehler *:

void* handle = malloc(100); 
auto ptr = toUniquePtr(handle, &free); 

Der Fehler, den ich von Visual Studio bekommen 2015 RTM 1 :

Error C2664 'std::unique_ptr<T,CostumeDeleter<T,F>>::unique_ptr(const std::unique_ptr<T,CostumeDeleter<T,F>> &)': cannot convert argument 1 from 'void *' to 'void *' 

Was ist komisch. irgendeine Hilfe?

* Nein, ich benutze nicht malloc in meiner täglichen Arbeit in C++, es ist nur für das Beispiel, da es eine einfache Funktion ist, den Kompilierungsfehler zu replizieren.

+0

wird den Aufruf der Copykonstruktor aus irgendeinem Grund. Der Kopierkonstruktor wird für "unique_ptr" gelöscht. –

+0

Meinst du nicht 'inline auto toUniquePtr (T * t, F f) {', dh, 'T' wird derzeit als Zeigertyp –

+0

@PiotrSkotnicki hergeleitet, es sollte dem Compiler sowieso egal sein, und einiges Ressourcen sind keine Zeigertypen (zB SOCKET) und haben den Kompilierungsfehler nicht gelöst. –

Antwort

4
template <class T, class F> 
inline auto toUniquePtr(T t, F f) 

die Funktionsvorlage oben gegeben, T wird als void * abgeleitet. So std::unique_ptr<T, decltype(deleter)> erwartet, dass eine T *, auch bekannt als void ** übergeben werden.

die Fehler zu beheben, um die Vorlage zu

template <class T, class F> 
inline auto toUniquePtr(T *t, F f) 
//      ^^^^ 

ähnlich ändern, ändern operator() die deleter der ein T * zu akzeptieren.

Live demo


Ein andere Lösung ist, alles zu verlassen, wie Sie es derzeit haben, und fügen Sie die deleter Definition folgenden Alias.

using pointer = T; 

Nun wird die unique_ptr verwalten ein <deleter_type>::pointer, das heißt void *, statt void **, und Ihr Code kompilieren.

Live demo

+0

Sie schlagen mich für ein paar Sekunden :( –

+0

Ich ging mit dem 'using pointer = T' seit (wie ich kommentierte) einige der Ressourcen sind keine Zeiger. Es funktioniert schön. Danke! –

+1

@DavidHaim Der Typ, dass ' unique_ptr' is managing wird benötigt, um die Anforderungen eines * NullablePointer * zu erfüllen, das heißt, Sie können damit keine Ressource verwalten, die nicht mit 'nullptr' verglichen werden kann, aber einige stdlib-Implementierungen erlauben Ihnen dies immer noch . [Dieses Beispiel] (http://coliru.stacked-crooked.com/a/443545bb28711275) wird von clang + libC++ und MSVC akzeptiert, letzteres funktioniert, weil sie die Ressource mit einem initialisierten Wert "unique_ptr :: pointer" vergleichen. Das ist in diesem Fall "0". libstdC++ lehnt es ab, weil sie die Ressource mit 'nullptr' vergleichen. – Praetorian