0

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 };
armagedescu
  • 1,758
  • 2
  • 20
  • 31
  • 1
    I think the problem is `std::unique_ptr` actually store a pointer to `ULONG_PTR`, so the deletor not match up (which should looks like `void(ULONG_PTR*)`)? – apple apple Jan 24 '21 at 17:53
  • @appleapple I tried to cast it to a pointer. It still refuses to compile, with different error. – armagedescu Jan 24 '21 at 17:58
  • 1
    well you cannot blindly cast a `ULONG_PTR` to pointer, it's not safe at all – apple apple Jan 24 '21 at 17:59
  • 1
    `std::unique_ptr` would want to pass `T*` to deleter. But `GdiplusShutdown` does not take a pointer type. You could cast `ULONG_PTR` to some pointer type on the way into `std::unique_ptr`, but `std::unique_ptr` won't cast it back when calling the deleter. You could write your own deleter that takes a pointer, casts it and passes it along to `GdiplusShutdown`, I suppose. – Igor Tandetnik Jan 24 '21 at 18:02
  • 1
    But at that point you would be much better off with a proper scope guard class, like [`std::experimental::scope_exit`](https://en.cppreference.com/w/cpp/experimental/scope_exit) or [similar](https://stackoverflow.com/questions/31365013/what-is-scopeguard-in-c) – Igor Tandetnik Jan 24 '21 at 18:03
  • @appleapple Transforming it to pointer works, just by taking the address of variable and dereferencing it later, please see Update 1 – armagedescu Jan 24 '21 at 18:08
  • 1
    @armagedescu well yes it work that way. what I mean is you should not `reinterpret_cast` it – apple apple Jan 24 '21 at 18:11
  • @IgorTandetnik good point about experimental. Reading it now, hope it will be adopted as standard class in the future – armagedescu Jan 24 '21 at 18:11
  • 1
    @armagedescu (continue my previous comment) but then it's not a valid pointer once `gdiplusToken` go out of scope. use it like scopedgaurd would not be a problem, but it shouldn't be treat as `unique_ptr` in many way. – apple apple Jan 24 '21 at 18:17
  • 1
    If you don't want to use `std::experimental`, I actually wrote an implementation of `unique_resource` compatible with C++11 as an independent header-only library-- and it sounds like it might solve your problem. It's available free under Boost license on [github](https://github.com/bitwizeshift/scope) (Shameless self-endorsement) – Human-Compiler Jan 24 '21 at 18:29
  • @Human-Compiler I already have my own. I want one which is available in standard C++ which can be used with writing clean code. – armagedescu Jan 24 '21 at 18:31
  • @RemyLebeau that is not a valid duplicate, the core problem here is `ULONG_PTR` is **not** a pointer type. In that question (and **all** it's answer) treat `HANDLE` as a pointer type (which probably is). – apple apple Jan 24 '21 at 19:10
  • @RemyLebeau The question looks similar. But the answers are very unclean, old styled, cumbersome and overenginered. My question already contains two much cleaner answers. – armagedescu Jan 24 '21 at 20:41
  • @RemyLebeau do you mean [this anwser](https://stackoverflow.com/a/14842047/5980430)? it clearly state *This works because `HANDLE` **is a pointer** and therefore satisfies NullablePointer.* – apple apple Jan 25 '21 at 15:59

1 Answers1

1

Summary from comments:


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


Actual Smart Pointer (as suggested by @RemyLebeau)


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
}
apple apple
  • 10,292
  • 2
  • 16
  • 36