2009-10-13 17 views
9

Ho recentemente pubblicato una domanda generica su RAII allo SO. Tuttavia, ho ancora alcuni problemi di implementazione con il mio esempio HANDLE.Creazione di un comando HANDLE RAII utilizzando shared_ptr con un deleter personalizzato

A HANDLE è impostato su void * in windows.h. Pertanto, la corretta shared_ptr definizione deve essere

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

Esempio 1CreateToolhelp32Snapshot: torna HANDLE e lavora.

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

come io uso void nella definizione (qual è il modo corretto?) Problemi andare avanti, quando provo a chiamare un po 'winapi comandi con questo puntatore. Funzionano funzionalmente, ma sono brutti e sono sicuro che ci deve essere una soluzione migliore.

Negli esempi seguenti, h è un puntatore creato tramite la definizione nella parte superiore.

Esempio 2OpenProcessToken: l'ultimo argomento è un PHANDLE. medio brutto con il cast.

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

Esempio 3Process32First: primo argomento è un HANDLE. DAVVERO brutto.

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

Esempio 4 semplice confronto con una costante HANDLE. DAVVERO brutto.

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

Qual è il modo corretto di creare un shared_ptr corretto per una MANIGLIA?

risposta

9

Esempio 1 OK OK

L'esempio 2 è errato. Colando ciecamente a PHANDLE, la logica shared_ptr viene ignorata. Dovrebbe essere qualcosa di simile a questo, invece:

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

o, da assegnare a uno shared_ptr pre-exising:

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

o, creare il proprio, cassetta di sicurezza, versione di OpenProcessToken che restituisce un handle condiviso invece di prendere un PHANDLE:

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

Esempio 3: Non c'è bisogno di prendere queste deviazioni.Questo dovrebbe essere ok:

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

Esempio 4: Anche in questo caso, nessuna deviazione:

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

Per rendere le cose più bello, si potrebbe typedef qualcosa come:

typedef shared_ptr<void> SharedHandle; 

o, meglio ancora, se tutti gli handle devono essere chiusi con CloseHandle(), creare una classe SharedHandle che include un shared_ptr e fornire automaticamente il delet corretto:

// 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

C'è qualche possibilità di cancellare il 'HANDLE' non sicuro dopo averlo convertito in uno sicuro nel primo frammento di codice di esempio 2? – Etan

+0

È possibile creare una funzione che avvolge OpenProcessHandle() (l'ho aggiunto al post) o eseguire la stessa operazione rispetto al secondo snippet, con shared_h inizializzato su INVALID_HANDLE_VALUE –

1

Ecco il mio alternativa, che è molto bella, tranne il necessario per dereference sempre dopo .get() e richiede un funtore o lambda:

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

poi:

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

quello che mi piace di più di questo è che non c'è lavoro supplementare per avere accesso a questo:

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

e, naturalmente, la funzione di supporto funziona con qualsiasi tipo di maniglia dei Mi piace.

3

Non preoccuparti di shared_ptr, usa ATL :: CHandle.

Ecco perché:

  • Quando vedi CHandle si sa che si tratta di un wrapper RAII per una maniglia.
  • Quando vedi shared_ptr<void> non sai di cosa si tratta.
  • CHandle non rende condivisa una proprietà (tuttavia in alcuni casi è possibile che si desideri una proprietà condivisa).
  • CHandle è uno standard per uno stack di sviluppo di Windows.
  • CHandle è più compatto di shared_ptr<void> con delet personalizzato (meno digitazione/lettura).
+0

È possibile espanderlo? La [documentazione] (https://msdn.microsoft.com/en-us/library/5fc6ft2t.aspx) non dice molto, ma sembra più un unique_ptr, non vedo come CHandle faciliti la condivisione. – phant0m

+0

@ phant0m CHandle non fornisce una proprietà condivisa. –

Problemi correlati