18

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?

Alessandro Jacopson
  • 18,047
  • 15
  • 98
  • 153

4 Answers4

12

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 ); } );
Community
  • 1
  • 1
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 1
    You don't need `std::function` for the last example, I think: stateless lambda's are convertible to function pointers. I.e. `std::unique_ptrp (...)` – MSalters Mar 27 '12 at 18:20
  • @MSalters I did try that but it failed to compile under VC10 and g++4.6.2. The former's error message is `error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(wchar_t *,void (__stdcall *const &)(LPWSTR *))' : cannot convert parameter 2 from 'anonymous-namespace'::' to 'void (__stdcall *const &)(LPWSTR *)'` – Praetorian Mar 27 '12 at 18:25
  • @MSalters You are correct, captureless lambdas can be converted to a function pointer, so `std::function` is not necessary. However, VC10 [does not implement this](https://connect.microsoft.com/VisualStudio/feedback/details/572138). Don't know how I messed it up the first time I tried with g++ but it definitely works. – Praetorian Mar 27 '12 at 22:32
  • I think you might have been confused by another issue. I find the `LPWSTR *` suspect. `LP` is the Microsoft prefix for pointer typedefs; presumably you didn't want to free a pointer to a pointer. – MSalters Mar 28 '12 at 07:50
  • @MSalters [CommandLineToArgvW](http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx) does return an `LPWSTR *` à la `char **argv` – Praetorian Mar 28 '12 at 14:44
  • 1
    "*if you don't want to mess with LocalFree's signature and calling conventions you can use a lambda*" - or use `LocalFree()` as the deleter directly, and just use `decltype(LocalFree)` for the signature. – Remy Lebeau Apr 26 '15 at 22:35
  • +1 for being the only person who actually included the syntax for the VS2010 workaround - I've been stuck on this all morning, and no one else even mentioned std::function – Bear May 22 '17 at 17:46
7

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 );
Alessandro Jacopson
  • 18,047
  • 15
  • 98
  • 153
  • 4
    On Visual Studio 2010, it has to be `std::unique_ptr`, or the build will fail with cryptic error. That's because `decltype(::LocalFree)` is not a function pointer type, but a function type. – Joker_vD Oct 03 '13 at 13:30
5

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);
Alessandro Jacopson
  • 18,047
  • 15
  • 98
  • 153
Peter Vrabel
  • 372
  • 1
  • 8
  • 1
    thank you for your answer. I am not so sure I would prefer easy syntax over the right semantic. IMHO shared_ptr is not suitable in my case. See 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 – Alessandro Jacopson Apr 01 '12 at 08:41
  • True, you are not looking to pass your pointer around, but it might be helpful to know about related techniques. – Peter Vrabel Apr 02 '12 at 21:56
0

And what about an answer using Microsoft Windows Implementation Libraries (WIL)?

First of all "install" WIL (from a terminal):

c:\dev>git clone https://github.com/microsoft/wil.git

And then:

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

#include "c:/dev/wil/include/wil/resource.h"

int main(int argc, char* argv[])
{
   {
      int n = 0;
      wil::unique_hlocal_ptr<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;
}
Alessandro Jacopson
  • 18,047
  • 15
  • 98
  • 153