2012-03-27 7 views
13

Ich habe die win32 API CommandLineToArgvW die eine LPWSTR* und gibt mir warnt davor, dassstd :: unique_ptr mit benutzerdefinierten deleter für win32 Localfree

CommandLineToArgvW für Zeiger auf die Argument Strings einen Block von zusammenhängenden Speicher zuweist, und für die Argumentketten selbst; Die aufrufende Anwendung muss den von der Argumentliste verwendeten Speicher freigeben, wenn sie nicht mehr benötigt wird. Um den Speicher freizugeben, verwenden Sie einen Einzelruf an die LocalFree-Funktion.

Siehe http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx

Was ist ein C++ idiomatische Weg, um den Speicher im obigen Fall zu befreien?

ich zu einem std::unique_ptr mit einem benutzerdefinierten deleter dachte, so etwas wie die:

#include <Windows.h> 
#include <memory> 
#include <iostream> 

template< class T > 
struct Local_Del 
{ 
    void operator()(T*p){::LocalFree(p);} 
}; 

int main(int argc, char* argv[]) 
{ 
    { 
     int n = 0; 
     std::unique_ptr< LPWSTR, Local_Del<LPWSTR> > p(::CommandLineToArgvW(L"cmd.exe p1 p2 p3",&n)); 
     for (int i = 0; i < n; i++) { 
     std::wcout << p.get()[i] << L"\n"; 
     } 
    } 

    return 0; 
} 

Gibt es irgendein Problem in dem obigen Code?

Antwort

10

Es sieht für mich richtig aus. Sie könnten es ein wenig prägnanter machen, indem Sie den Deleter der unique_ptr inline angeben, anstatt einen Funktor dafür zu erstellen.

std::unique_ptr<LPWSTR, HLOCAL(__stdcall *)(HLOCAL)> 
     p(::CommandLineToArgvW(L"cmd.exe p1 p2 p3", &n), ::LocalFree); 

Oder, wenn Sie mit LocalFree ‚Unterschrift zu Chaos nicht wollen und Konventionen fordern Sie eine Lambda verwenden können, das Löschen zu tun.

std::unique_ptr<LPWSTR, void(*)(LPWSTR *)> 
     p(::CommandLineToArgvW(L"cmd.exe p1 p2 p3", &n), 
     [](LPWSTR *ptr){ ::LocalFree(ptr); }); 

Hinweis: Zum Zeitpunkt der Antwort zuerst geschrieben wurde, wurde VS2010 die VS-Version freigegeben. Es doesn't support Umwandlung von Capture-weniger lambdas Zeiger funktionieren, so müssten Sie std::function im zweiten Beispiel

std::unique_ptr<LPWSTR, std::function<void(LPWSTR *)>> 
     p(::CommandLineToArgvW(L"cmd.exe p1 p2 p3", &n), 
     [](LPWSTR *ptr){ ::LocalFree(ptr); }); 
+1

Sie brauchen nicht 'std :: function' für das letzte Beispiel, ich denke: Stateless Lambdas sind konvertierbar zu Funktionszeigern. I.e. 'std :: unique_ptr p (...)' – MSalters

+0

@ MSalters Ich habe das versucht, aber es konnte nicht unter VC10 und g ++ 4.6.2 kompiliert werden. Die erste Fehlermeldung lautet 'Fehler C2664: 'std :: unique_ptr <_Ty,_Dx> :: eindeutige_ptr (wchar_t *, void (__stdcall * const &) (LPWSTR *))': kann Parameter 2 von 'anonymer Namespace' :: 'nicht konvertieren 'void (__stdcall * const &) (LPWSTR *)' ' – Praetorian

+0

@MSalters Sie haben Recht, Captureless Lambdas können in einen Funktionszeiger umgewandelt werden, so dass 'std :: function' nicht notwendig ist. VC10 [implementiert dies jedoch nicht] (https://connect.microsoft.com/VisualStudio/feedback/details/572138).Ich weiß nicht, wie ich es vermasselt habe, als ich das erste Mal mit g ++ versuchte, aber es funktioniert definitiv. – Praetorian

4

I shared_ptr nützlicher als eine generische Ressource guard etwas finden verwenden. Es erfordert nicht, dass der Löscher der Teil von Vorlagenargumenten ist und als solcher leicht weitergegeben werden kann.

std::shared_ptr<LPWSTR> p(
    ::CommandLineToArgvW(L"cmd.exe p1 p2 p3", &n), 
    ::LocalFree); 
+1

danke für Ihre Antwort. Ich bin mir nicht sicher, ob ich die einfache Syntax der richtigen Semantik vorziehen würde. IMHO shared_ptr ist in meinem Fall nicht geeignet. Siehe http://www2.research.att.com/~bs/C++0xFAQ.html#std-shared_ptr vs http://www2.research.att.com/~bs/C++0xFAQ.html#std -unique_ptr –

+0

Richtig, Sie möchten Ihren Zeiger nicht weitergeben, aber es könnte hilfreich sein, über verwandte Techniken Bescheid zu wissen. –

6

von benutzerdefinierten deleter Deklarieren nicht so schön ist, die Verwendung von decltype() ist schneller. std::shared_ptr ist eine Alternative, aber es ist größer als std::unique_ptr. Wenn Sie keinen Zeiger teilen möchten, nehmen Sie eine unique_ptr.

+1

In Visual Studio 2010 muss es 'std :: unique_ptr ' sein, sonst schlägt der Build mit einem kryptischen Fehler fehl. Das liegt daran, dass 'declltype (:: LocalFree)' kein Funktionszeigertyp ist, sondern ein Funktionstyp. –