适用于任何类型资源的自定义 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 可能提出的问题
- What is ScopeGuard in C++?
- C++11 scope exit guard, a good idea?
- https://en.cppreference.com/w/cpp/experimental/scope_exit
实际智能指针(由@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
}
我检查了很多关于定制清洁剂的链接,例如 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 可能提出的问题
- What is ScopeGuard in C++?
- C++11 scope exit guard, a good idea?
- https://en.cppreference.com/w/cpp/experimental/scope_exit
实际智能指针(由@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
}