I checked many links about custom cleaners like C++ Destructors with Vectors, Pointers, but still didn't find the answer.
For the ones interested in solutions jump to Update 1/2 on the bottom of question. Solutions 1/2 where found during discussion, thanks to active and attentive participants.
I want to make a custom automatic deleter for something. Let's look at following sample
#include <gdiplus.h>
#pragma comment (lib,"Gdiplus.lib")
....
int HeigthMapFromImg(const wchar_t* imgPath, std::vector<std::vector<float>>& heights)
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
int x = HeightMapFromImgApi(imgPath, heights);
Gdiplus::GdiplusShutdown(gdiplusToken);
return x;
}
Note, std::unique_ptr, deleters and the Win32 API is not an answer. The question is not about writing own wrappers and not about WinAPI. One-liner for RAII on non pointer? is a similar question but the answers are overengineered.
In the sample the GDI+ token is used to call GdiplusShutdown when work finished. I want to automate it with unique_ptr and I'm trying something like this
int HeigthMapFromImg(const wchar_t* imgPath, std::vector<std::vector<float>>& heights)
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
std::unique_ptr<ULONG_PTR, decltype(Gdiplus::GdiplusShutdown) > ptr(gdiplusToken, Gdiplus::GdiplusShutdown);
int x = HeightMapFromImgApi(imgPath, heights);
return x;
}
But no matter that I do it doesn't compile, the code is written in VisualStudio
1>...BitmapReader.cpp(145): message : see reference to class template instantiation 'std::unique_ptr<ULONG_PTR,void (ULONG_PTR)>' being compiled
1>...BitmapReader.cpp(145,109): error C2660: 'std::unique_ptr<ULONG_PTR,void (ULONG_PTR)>::unique_ptr': function does not take 2 arguments
1>...\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29333\include\memory(2686,5): message : see declaration of 'std::unique_ptr<ULONG_PTR,void (ULONG_PTR)>::unique_ptr'
The STL pointer wrappers works very well with anything that looks like a pointer but the GDI+ token is defined as ULONG_PTR which is defined as __int64. I would expect it to be a PTR but it is not. When I use pointers is no problem, for instance a buffer of wchar_t:
std::wostream& ShowLastError(std::wostream& os)
{
auto localdeleterw = [](wchar_t* a) {::LocalFree(a); };
wchar_t* pBuffer = 0;
int ret = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, (LPWSTR)&pBuffer, 0, 0);
if (!(ret && pBuffer))
{
os << L"failed to read error";
return os;
}
std::unique_ptr<wchar_t, decltype(localdeleterw)> ptr (pBuffer, localdeleterw);
os << pBuffer << std::flush;
return os;
}
To anticipate writing wrappers, I already written my own which is much cleaner:
template<class T, auto deleter> struct cleaner
{
T resource;
~cleaner() { deleter (resource); }
};
It works pretty well, but I still have the feeling that it can be done without my own wrappers/workarounds:
int HeigthMapFromImg(const wchar_t* imgPath, std::vector<std::vector<float>>& heights)
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
cleaner<ULONG_PTR, Gdiplus::GdiplusShutdown> c{ gdiplusToken };
int x = HeightMapFromImgApi(imgPath, heights);
return x;
}
Answers:
Update 1:
As pointed by apple_apple (thanks), a pointer is required, so I transformed it to a pointer by passing &gdiplusToken instead of gdiplusToken, then indirecting it in the cleaner function, it works, but still looks like a workaround:
auto dtr = [](ULONG_PTR* a) {Gdiplus::GdiplusShutdown(*a); };
std::unique_ptr<ULONG_PTR, decltype(dtr) > ptr(&gdiplusToken, dtr);
Update 2:
As experimental solution, the suggestion from Igor Tandetnik (thanks) is also an option, but looks like not being part of VisualStudio
https://en.cppreference.com/w/cpp/experimental/scope_exit
Update 3:
Answers suggesting writing a wrapper, please read attentive the question. I already have my own, see section about the class cleaner. Anyway, I highlight it once again:
template<class T, auto deleter> struct cleaner
{
T resource;
~cleaner() { deleter (resource); }
};
///// and using
...
{
....
cleaner<ULONG_PTR, Gdiplus::GdiplusShutdown> c{ gdiplusToken };