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, otherwiseT*
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
C++ Class Header Files Organization
C/C++ MACro/Template Blackmagic to Generate Unique Name
New to Xcode Can't Open Files in C++
Heap Corruption Under Win32; How to Locate
How Is the Std::Tr1::Shared_Ptr Implemented
Writing Function Definition in Header Files in C++
Returning Const Reference to Local Variable from a Function
Returning a Const Reference to an Object Instead of a Copy
Why Would I Prefer Using Vector to Deque
How to Implement a BéZier Curve in C++
How to Enable Core Dump in My Linux C++ Program
Why Should I Ever Use Inline Code
How to See the Template Instantiated Code by C++ Compiler
Run a Program with More Than One Source Files in Gnu C++ Compiler
At What Point Does Dereferencing the Null Pointer Become Undefined Behavior