适用于任何类型资源的自定义 STL 删除器

Custom STL deleter for any type of resource

我检查了很多关于定制清洁剂的链接,例如 C++ Destructors with Vectors, Pointers,,但仍然没有找到答案。
对于那些对解决方案感兴趣的人,请跳转到问题底部的更新 1/2。解决方案 1/2 是在讨论过程中找到的,感谢积极和细心的参与者。 我想为 something 制作自定义自动删除器。让我们看看下面的例子

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

请注意,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? 是一个类似的问题,但答案设计过度。
在示例中,GDI+ 令牌用于在工作完成时调用 GdiplusShutdown。我想用 unique_ptr 使它自动化,我正在尝试这样的事情

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

但是不管我怎么编译都不行,代码是用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 Studio19\Community\VC\Tools\MSVC.28.29333\include\memory(2686,5): message : see declaration of 'std::unique_ptr<ULONG_PTR,void (ULONG_PTR)>::unique_ptr'

STL 指针包装器可以很好地处理任何看起来像指针的东西,但 GDI+ 标记定义为 ULONG_PTR,后者定义为 __int64。我希望它是一个 PTR,但它不是。当我使用指针时没问题,例如 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;
}

为了预期编写包装器,我已经编写了自己的包装器,它更干净:

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

效果还不错,不过还是觉得不用自己也能搞定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;
}

答案:
更新 1:
正如 apple_apple 所指出的(谢谢),需要一个指针,所以我通过传递 &gdiplusToken 而不是 gdiplusToken 将其转换为指针,然后在更清洁的函数中间接使用它,它可以工作,但看起来仍然是一种解决方法:

auto dtr = [](ULONG_PTR* a) {Gdiplus::GdiplusShutdown(*a); };
std::unique_ptr<ULONG_PTR, decltype(dtr) > ptr(&gdiplusToken, dtr);

更新 2:
作为实验性解决方案,Igor Tandetnik(感谢)的建议也是一种选择,但看起来不属于 VisualStudio https://en.cppreference.com/w/cpp/experimental/scope_exit
更新 3:
建议编写包装器的答案,请仔细阅读问题。我已经有了自己的,请参阅有关 class cleaner 的部分。无论如何,我再次强调它:

template<class T, auto deleter> struct cleaner
{
    T resource;
    ~cleaner() { deleter (resource); }
};
///// and using
...
{
....
    cleaner<ULONG_PTR, Gdiplus::GdiplusShutdown>  c{ gdiplusToken };

评论总结:


ScopeGuard(如@IgorTandetnik 所建议)以及@OP 可能提出的问题


实际智能指针(由@RemyLebeau 建议)

  • std::unique_ptr, deleters and the Win32 API (仅适用于指针类型(尽管它可以编译,详情请参阅下面的答案))
  • One-liner for RAII on non pointer?

来自@OP的解决方案(即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
}