Std::Unique_Ptr, Deleters and the Win32 API

std::unique_ptr, deleters and the Win32 API

Forget about the custom deleter for now. When you say std::unique_ptr<T>, the unique_ptr constructor expects to receive a T*, but CreateMutex returns a HANDLE, not a HANDLE *.

There are 3 ways to fix this:

std::unique_ptr<void, deleter> m_mutex;

You'll have to cast the return value of CreateMutex to a void *.

Another way to do this is use std::remove_pointer to get to the HANDLE's underlying type.

std::unique_ptr<std::remove_pointer<HANDLE>::type, deleter> m_mutex;

Yet another way to do this is to exploit the fact that if the unique_ptr's deleter contains a nested type named pointer, then the unique_ptr will use that type for its managed object pointer instead of T*.

struct mutex_deleter {
void operator()( HANDLE h )
{
::CloseHandle( h );
}
typedef HANDLE pointer;
};
std::unique_ptr<HANDLE, mutex_deleter> m_mutex;
foo() : m_mutex(::CreateMutex(NULL, FALSE, NULL), mutex_deleter()) {}

Now, if you want to pass a pointer to function type as the deleter, then when dealing with the Windows API you also need to pay attention to the calling convention when creating function pointers.

So, a function pointer to CloseHandle must look like this

BOOL(WINAPI *)(HANDLE)

Combining all of it,

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
BOOL(WINAPI *)(HANDLE)> m_mutex(::CreateMutex(NULL, FALSE, NULL),
&::CloseHandle);

I find it easier to use a lambda instead

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
void(*)( HANDLE )> m_mutex;
foo() : m_mutex(::CreateMutex(NULL, FALSE, NULL),
[]( HANDLE h ) { ::CloseHandle( h ); }) {}

Or as suggested by @hjmd in the comments, use decltype to deduce the type of the function pointer.

std::unique_ptr<std::remove_pointer<HANDLE>::type, 
decltype(&::CloseHandle)> m_mutex(::CreateMutex(NULL, FALSE, NULL),
&::CloseHandle);

std::unique_ptr with custom deleter for win32 LocalFree

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

Using std::unique_ptr for Windows HANDLEs

The implementation of unique_ptr checks for the presence of a ::pointer type on the deleter. If the deleter has a ::pointer type then this type is used as the pointer typedef on the unique_ptr. Otherwise a pointer to the first template argument is used.

According to cppreference.com, the unique_ptr::pointer type is defined as

std::remove_reference<D>::type::pointer if that type exists, otherwise T*

How to use a custom deleter using WinAPI with std::make_unique?

make_unique returns unique_ptr with defaulted deleter.

To provide your custom version you have to call unique_ptr(Pointer,Deleter) version of unique_ptr ctor:

template <typename Function>
class PtrDeleter {
Function _Closer;
public:
void operator()(void* memBlock) const {
_Closer(memBlock);
}
};

int main() {
PtrDeleter<decltype(&CloseHandle)> deleter;
void* openThread = OpenThread(0,false,0);
std::unique_ptr<void, decltype(deleter)> ptr(openThread,deleter);
}

Demo

Specifying custom default deleter for std::unique_ptr with typedef

Define a function object that calls the right function:

struct resource_deleter
{
using pointer = std::remove_pointer<ResourcePointer>::type;
void operator()(ResourcePointer p) const { resource_delete(p); }
};

using res_ptr = std::unique_ptr<resource_deleter::pointer, resource_deleter>;

Now you don't need to pass the deleter to the unique_ptr constructor, because the deleter can be default-constructed and will do the right thing:

res_ptr p{ resource_new() };

Custom STL deleter for any type of resource

Summary from comments:


ScopeGuard (as suggested by @IgorTandetnik) and probably what @OP ask

  • What is ScopeGuard in C++?
  • C++11 scope exit guard, a good idea?
  • https://en.cppreference.com/w/cpp/experimental/scope_exit
  • https://stackoverflow.com/a/3670448/5980430

Actual Smart Pointer (as suggested by @RemyLebeau)

  • std::unique_ptr, deleters and the Win32 API
    (which only work for pointer type (despite it compiles, see below answer for detail))
  • One-liner for RAII on non pointer?

The solution from @OP (which is ScopeGuard)

template<typename T, auto deleter> struct cleaner
{
T resource;
~cleaner() { deleter(resource); }
};

/// and using
{
cleaner<ULONG_PTR, Gdiplus::GdiplusShutdown> c{ gdiplusToken };
// do something here
}

How to handle the ownership transfer of a unique pointer's stored raw pointer?

I pass the stored pointer in the unique_ptr with .get(), but this function takes ownership of the raw pointer.

If the function takes ownership, then you should release the ownership from the unique pointer. Otherwise the ownership won't be unique and you will get undefined behaviour due to multiple calls to the deleter. This is what the release member function is for.

but what happens if the ECDSA_SIG_set0 function call is unsuccessful, do I have to manually free the r and s variables?

If the function takes ownership of the pointer, then it is responsible for deleting it (until it transfers the ownership somewhere else). If the function sometimes leaks the pointer, then it is a badly designed API.

What the function does should be specified in its documentation. It would be much better to use std::unique_ptr in the API if it takes ownership.

How does the custom deleter of std::unique_ptr work?

This works for me in MSVC10

int x = 5;
auto del = [](int * p) { std::cout << "Deleting x, value is : " << *p; };
std::unique_ptr<int, decltype(del)> px(&x, del);

And on gcc 4.5, here

I'll skip going to the standard, unless you don't think that example is doing exactly what you'd expect it to do.

When using new to initialize unique_ptrFILE*,File::Close is the custom deleter responsible for freeing that memory?

You are making your use of std::unique_ptr more complicated than it needs to be. DO NOT store a FILE** pointer inside the unique_ptr, store a FILE* instead. That is what fopen_s() outputs, and all access to the FILE is done through FILE* not FILE**. You don't need 2 levels of indirection when 1 level will suffice.

Try this:

#include <Windows.h>
#include <memory>
#include <string>
#include <map>

class File
{
private:

//functors - custom deleter
struct Close { void operator()(FILE* f); };

//type definitions
typedef std::unique_ptr<FILE,File::Close> Handle;
typedef std::map<std::string,Handle> HandleMap;

//static members
static Handle& Open(std::string _name, std::string _mode);
static HandleMap s_handle_map_;

//variables
Handle& handle_;
std::string name_;

public:

//functions
File(std::string _name, std::string _mode);
void Write(std::string _message);

};
File::HandleMap File::s_handle_map_;

File::File(std::string _name, std::string _mode)
: handle_(Open(_name,_mode)), name_(_name)
{
}

File::Handle& File::Open(std::string _name, std::string _mode)
{
auto iter = s_handle_map_.find(_name);

if (iter == s_handle_map_.end())
{
FILE *f = nullptr;

//open new file
if (fopen_s(&f, _name.c_str(), _mode.c_str()) != 0)
throw std::runtime_error("cannot open file");

//transfer ownership of handle
iter = s_handle_map_.emplace(_name, Handle(f)).first;
}

return iter->second;
}

void File::Close::operator()(FILE* f)
{
if (f)
fclose(f);
}

void File::Write(std::string _message)
{
fprintf(handle_.get(), "%s", _message.c_str());
}

int WINAPI WinMain(HINSTANCE _instance, HINSTANCE _previous, LPSTR _cmd, int _show)
{
File file("test.txt", "w");
file.Write("Hello World\n");
return 0;
}


Related Topics



Leave a reply



Submit