Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::unique_ptr with custom deleter for win32 LocalFree

I have the win32 API CommandLineToArgvW which returns a LPWSTR* and warns me that

CommandLineToArgvW allocates a block of contiguous memory for pointers to the argument strings, and for the argument strings themselves; the calling application must free the memory used by the argument list when it is no longer needed. To free the memory, use a single call to the LocalFree function.

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

What is a C++ idiomatic way to free the memory in the above case?

I was thinking to a std::unique_ptr with a custom deleter, something like that:

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

Is there any problem in the above code?

like image 720
Alessandro Jacopson Avatar asked Mar 27 '12 15:03

Alessandro Jacopson


3 Answers

It looks correct to me. You could make it a little more succinct by specifying the unique_ptr's deleter inline rather than creating a functor for it.

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

Or, if you don't want to mess with LocalFree's signature and calling conventions you can use a lambda to do the deletion.

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

Note: At the time this answer was first written, VS2010 was the released VS version available. It doesn't support conversion of capture-less lambdas to function pointers, so you'd have to use std::function in the second example

std::unique_ptr<LPWSTR, std::function<void(LPWSTR *)>> 
      p( ::CommandLineToArgvW( L"cmd.exe p1 p2 p3", &n ), 
         [](LPWSTR *ptr){ ::LocalFree( ptr ); } );
like image 190
Praetorian Avatar answered Oct 11 '22 21:10

Praetorian


Declaring of custom deleter is not so pretty, use of decltype() is faster. std::shared_ptr is an alternative, but it is larger than std::unique_ptr. If you don't want to share a pointer, then take a unique_ptr.

std::unique_ptr<LPWSTR, decltype(::LocalFree)> 
     p( ::CommandLineToArgvW( L"cmd.exe p1 p2 p3", &n ), ::LocalFree );
like image 20
Alexander Drichel Avatar answered Oct 11 '22 22:10

Alexander Drichel


I find shared_ptr a bit more useful as a generic resource guard. It does not require the deleter to be the part of template arguments and as such can be easily passed around.

std::shared_ptr<LPWSTR> p(
    ::CommandLineToArgvW(L"cmd.exe p1 p2 p3", &n),
    ::LocalFree);
like image 35
Peter Vrabel Avatar answered Oct 11 '22 22:10

Peter Vrabel