是否可以使用 std::unique_ptr 来管理 DLL 资源?
Is it possible to use std::unique_ptr to manage DLL resource?
我的项目中有很多LoadLibrary
,需要为每个LoadLibrary
手动调用FreeLibrary
。我想将 std::unique_ptr
与特定的 deleter
一起使用,使其自动释放我的 dll 资源。
这就是我要定义的内容:
std::unique_ptr<HMODULE, BOOL(*)(HMODULE)> theDll(LoadLibrary("My.dll"), FreeLibrary);
但是编译器抱怨类型不匹配。我发现它期望从 LoadLibrary
得到 *HMODULE
。也就是说 std::unique_ptr<A>
将期望 A*
作为其指针类型。看起来我仍然需要定义一个新的 class 来管理 DLL 资源(构造函数中的 LoadLibrary
和析构函数中的 FreeLibrary
)。
是否可以让 std::unique_ptr<A>
只期望 A
作为它的指针类型?
已更新,
以下是新 class 和使用 std::unique_ptr 的优缺点,
从答案中总结。
再创建一个dll管理class,
优点:
- 完全可控以自定义 DLL 语义。
- 将DLL相关部分隔离成一个class,一个人负责。
- 如果 DLL 需要更多功能(如公开符号),则易于扩展。
缺点:
- 需要重建 RAII 部分,标准自动指针已经完成。
- RAII部分有机会出错
- 需要声明一个新的 class。
将 std::unique_ptr
与自定义删除器一起使用,
优点:
- 无需再声明一个 class。
- 重用
unique_ptr
的 RAII 部分。
- 也许
move semantics
阻止了 DLL 模块实例被复制?
缺点:
- Dll 资源语义可能不符合标准自动指针,容易出错?
unique_ptr
中的模板参数很复杂,很难找到错误所在。
HMODULE
是 void*
,无类型,与 unique_ptr 集成时可能有问题?
如有错误请评论指正
It looks I still need to define a new class to manage DLL resource
为什么你认为这是个坏主意?
为什么不这样?
class DynamicLibrary
{
protected:
HANDLE _handle;
string _path;
public:
DynamicLibrary();
DynamicLibrary(const string& path, bool autoLoad = false); // if(autoLoad) this->Load()
~DynamicLibrary(); // if(this->IsLoaded()) this->Unload()
Result Load(); // LoadLibrary() here
void Unload(); //FreeLibrary() here
bool IsLoaded() const; // return this->_handle != nullptr
};
然后,您甚至可以扩展此 class 以使符号检索更清晰:
class DynamicLibrary
{
//cut
void* RetrieveSymbol(const char* symName) const;
template <class Cast_type>
Cast_type RetrieveSymbolAs(const char* symName) const;
};
示例:
DynamicLibrary lib("XXX.dll", true);
assert( lib.IsLoaded() );
FuncType dll_func = lib.RetrieveSymbolAs<FuncType>("dll_func_name");
我认为,旨在表示 DLL
实例的显式类型要好得多。然后,您可以存储此类对象的数组,所有这些都会自动进行。干净、简单、易于实施。
存储 std::unique_ptr<HANDLE, Some_custom_unloader>
的 list/array 在我看来是个糟糕的设计。
根据this page,HMODULE是HINSTANCE,HINSTANCE是HANDLE,HANDLE是PVOID,PVOID是void *。这意味着 HMODULE 是指针类型。所以以下应该有效:
std::unique_ptr<std::remove_pointer_t<HMODULE>, BOOL(*)(HMODULE)> theDll(LoadLibrary("My.dll"), FreeLibrary);
如果您使用它来管理 T*
未引用的资源 T
,则需要为 unique_ptr
提供相应的 ::pointer
类型。这里 T
是 unique_ptr
.
的第一个模板参数
如果没有::pointer
类型未定义,则使用T*
。在您的情况下,HMODULE*
是错误的。
struct tLibraryDeleter
{
typedef HMODULE pointer;
void operator()(HMODULE h) { FreeLibrary(h); }
};
std::unique_ptr<HMODULE, tLibraryDeleter>(::LoadLibraryA("My.dll"));
查看 here and here。
请在Windows Implementation Libraries (WIL)
中查看unique_hmodule
可以按原样使用它,也可以借用它的实现。
我的项目中有很多LoadLibrary
,需要为每个LoadLibrary
手动调用FreeLibrary
。我想将 std::unique_ptr
与特定的 deleter
一起使用,使其自动释放我的 dll 资源。
这就是我要定义的内容:
std::unique_ptr<HMODULE, BOOL(*)(HMODULE)> theDll(LoadLibrary("My.dll"), FreeLibrary);
但是编译器抱怨类型不匹配。我发现它期望从 LoadLibrary
得到 *HMODULE
。也就是说 std::unique_ptr<A>
将期望 A*
作为其指针类型。看起来我仍然需要定义一个新的 class 来管理 DLL 资源(构造函数中的 LoadLibrary
和析构函数中的 FreeLibrary
)。
是否可以让 std::unique_ptr<A>
只期望 A
作为它的指针类型?
已更新,
以下是新 class 和使用 std::unique_ptr 的优缺点, 从答案中总结。
再创建一个dll管理class,
优点:
- 完全可控以自定义 DLL 语义。
- 将DLL相关部分隔离成一个class,一个人负责。
- 如果 DLL 需要更多功能(如公开符号),则易于扩展。
缺点:
- 需要重建 RAII 部分,标准自动指针已经完成。
- RAII部分有机会出错
- 需要声明一个新的 class。
将 std::unique_ptr
与自定义删除器一起使用,
优点:
- 无需再声明一个 class。
- 重用
unique_ptr
的 RAII 部分。 - 也许
move semantics
阻止了 DLL 模块实例被复制?
缺点:
- Dll 资源语义可能不符合标准自动指针,容易出错?
unique_ptr
中的模板参数很复杂,很难找到错误所在。HMODULE
是void*
,无类型,与 unique_ptr 集成时可能有问题?
如有错误请评论指正
It looks I still need to define a new class to manage DLL resource
为什么你认为这是个坏主意?
为什么不这样?
class DynamicLibrary
{
protected:
HANDLE _handle;
string _path;
public:
DynamicLibrary();
DynamicLibrary(const string& path, bool autoLoad = false); // if(autoLoad) this->Load()
~DynamicLibrary(); // if(this->IsLoaded()) this->Unload()
Result Load(); // LoadLibrary() here
void Unload(); //FreeLibrary() here
bool IsLoaded() const; // return this->_handle != nullptr
};
然后,您甚至可以扩展此 class 以使符号检索更清晰:
class DynamicLibrary
{
//cut
void* RetrieveSymbol(const char* symName) const;
template <class Cast_type>
Cast_type RetrieveSymbolAs(const char* symName) const;
};
示例:
DynamicLibrary lib("XXX.dll", true);
assert( lib.IsLoaded() );
FuncType dll_func = lib.RetrieveSymbolAs<FuncType>("dll_func_name");
我认为,旨在表示 DLL
实例的显式类型要好得多。然后,您可以存储此类对象的数组,所有这些都会自动进行。干净、简单、易于实施。
存储 std::unique_ptr<HANDLE, Some_custom_unloader>
的 list/array 在我看来是个糟糕的设计。
根据this page,HMODULE是HINSTANCE,HINSTANCE是HANDLE,HANDLE是PVOID,PVOID是void *。这意味着 HMODULE 是指针类型。所以以下应该有效:
std::unique_ptr<std::remove_pointer_t<HMODULE>, BOOL(*)(HMODULE)> theDll(LoadLibrary("My.dll"), FreeLibrary);
如果您使用它来管理 T*
未引用的资源 T
,则需要为 unique_ptr
提供相应的 ::pointer
类型。这里 T
是 unique_ptr
.
如果没有::pointer
类型未定义,则使用T*
。在您的情况下,HMODULE*
是错误的。
struct tLibraryDeleter
{
typedef HMODULE pointer;
void operator()(HMODULE h) { FreeLibrary(h); }
};
std::unique_ptr<HMODULE, tLibraryDeleter>(::LoadLibraryA("My.dll"));
查看 here and here。
请在Windows Implementation Libraries (WIL)
中查看unique_hmodule
可以按原样使用它,也可以借用它的实现。