2009-10-13 7 views
9

Ich habe vor kurzem eine allgemeine Frage über RAII bei SO. Allerdings habe ich noch einige Implementierungsprobleme mit meinem Beispiel von HANDLE.Making ein HANDLE RAII-konform mit shared_ptr mit einem benutzerdefinierten Löscher

Ein HANDLE ist typedeffed zu void * in windows.h. Daher muss die richtige shared_ptr Definition

std::tr1::shared_ptr<void> myHandle (INVALID_HANDLE_VALUE, CloseHandle); 

Beispiel 1CreateToolhelp32Snapshot sein: gibt HANDLE und arbeitet.

const std::tr1::shared_ptr<void> h 
    (CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL), CloseHandle); 

Wie verwende ich void in der Definition (was der richtige Weg ist?) Probleme weitergehen, wenn ich versuche, etwas mehr zu nennen winapi mit diesem Zeiger-Befehlen. Sie funktionieren funktional, sind aber hässlich und ich bin mir sicher, dass es eine bessere Lösung geben muss.

In den folgenden Beispielen ist h ein Zeiger, der über die Definition oben erstellt wurde.

Beispiel 2OpenProcessToken: Letztes Argument ist ein PHANDLE. Medium hässlich mit der Besetzung.

OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
    (PHANDLE)&h); 

Beispiel 3Process32First: erste Argument ist ein HANDLE. Echt hässlich.

Process32First(*((PHANDLE)&h), &pEntry); 

Beispiel 4 einfacher Vergleich mit einer konstanten HANDLE. Echt hässlich.

if (*((PHANDLE)&h) == INVALID_HANDLE) { /* do something */ } 

Was ist die richtige Methode, um ein korrektes shared_ptr für ein HANDLE zu erstellen?

Antwort

9

Beispiel 1 ist OK

Beispiel 2 ist falsch. Durch blindes Übergeben an PHANDLE wird die shared_ptr-Logik umgangen. Es sollte stattdessen so etwas wie dieses:

HANDLE h; 
OpenProcessToken(...., &h); 
shared_ptr<void> safe_h(h, &::CloseHandle); 

oder zu einem vorher exising Shared_ptr zuweisen:

shared_ptr<void> safe_h = .... 
{ 
    HANDLE h; 
    OpenProcessToken(...., &h); 
    safe_h.reset(h, &::CloseHandle); 
}//For extra safety, limit visibility of the naked handle 

oder erstellen Sie Ihre eigene, sichere Version von Open, die stattdessen einen gemeinsamen Handle zurückgibt der Einnahme eines PHANDES:

// Using SharedHandle defined at the end of this post 
SharedHandle OpenProcess(....) 
{ 
    HANDLE h = INVALID_HANDLE_VALUE; 
    ::OpenProcessToken(...., &h); 
    return SharedHandle(h); 
} 

Beispiel 3: Keine Notwendigkeit, diese Umwege zu nehmen.Dies sollte in Ordnung sein:

Process32First(h.get(), ...); 

Beispiel 4: Wieder kein Umweg:

if (h.get() == INVALID_HANDLE){...} 

Um die Dinge schöner zu machen, könnte man so etwas wie typedef:

typedef shared_ptr<void> SharedHandle; 

oder besser noch, wenn Alle Handles werden mit CloseHandle() geschlossen, erstellen eine SharedHandle-Klasse, die ein shared_ptr umhüllt und automatisch den richtigen Deleter bereitstellt:

// Warning: Not tested. For illustration purposes only 
class SharedHandle 
{ 
public: 
    explicit SharedHandle(HANDLE h) : m_Handle(h, &::CloseHandle){}; 
    HANDLE get()const{return m_Handle.get();} 

    //Expose other shared_ptr-like methods as needed 
    //... 

private: 
    shared_ptr<void> m_Handle; 
}; 
+0

Gibt es eine Möglichkeit, den unsicheren 'GRIFF' zu löschen, nachdem Sie ihn in Ihrem ersten Beispiel 2 Code-Snippet in einen sicheren umgewandelt haben? – Etan

+0

Sie könnten ein Funktionswrapping OpenProcessHandle() erstellen (ich fügte dies dem Post hinzu) oder dasselbe tun als im zweiten Snippet, wobei shared_h mit INVALID_HANDLE_VALUE initialisiert wird –

1

Hier ist meine Alternative, die ganz nett ist, außer Sie nach .get() und erfordert einen Funktor oder Lambda immer dereferenzieren müssen:

auto closeHandleDeleter = [](HANDLE* h) { ::CloseHandle(*h); }; 
std::shared_ptr<HANDLE> sp = make_shared_handle(a_HANDLE, closeHandleDeleter); 
f_that_takes_handle(*sp.get()); 

:

template<typename HandleType, typename Deleter> 
std::shared_ptr<HandleType> make_shared_handle(HandleType _handle, Deleter _dx) 
{ 
    return std::shared_ptr<HandleType>(new HandleType(_handle), _dx); 
} 

dann

Was mir am meisten gefällt ist, dass es keine zusätzliche Arbeit gibt, um Zugang zu diesem zu haben:

std::weak_ptr<HANDLE> wp = sp; // Yes. This could make sense in some designs. 

und natürlich funktioniert die Hilfsfunktion mit jedem Handle-Typ der Likes.

3

Stören Sie nicht mit shared_ptr dafür, verwenden Sie ATL :: CHandle. Hier

Deshalb:

  • Wenn Sie sehen, CHandle Sie wissen, dass es ein RAH-Wrapper für einen Handgriff ist.
  • Wenn Sie sehen, wissen Sie nicht, was es ist.
  • CHandle wird keine Eigentumsrechte freigegeben (in manchen Fällen möchten Sie jedoch eine gemeinsame Eigentümerschaft).
  • CHandle ist ein Standard für einen Windows-Entwicklungsstack.
  • CHandle ist kompakter als mit benutzerdefinierten Deleter (weniger tippen/lesen).
+0

Können Sie das erweitern? Die [Dokumentation] (https://msdn.microsoft.com/en-us/library/5fc6ft2t.aspx) sagt nicht viel, aber es sieht eher wie ein unique_ptr aus, ich sehe nicht, wie CHandle das Teilen erleichtert. – phant0m

+0

@ phant0m CHandle bietet keine gemeinsame Eigentümerschaft. –

Verwandte Themen