【问题标题】:Is it possible to use std::unique_ptr to manage DLL resource?是否可以使用 std::unique_ptr 来管理 DLL 资源?
【发布时间】:2015-07-29 06:32:52
【问题描述】:

我的项目中有很多LoadLibrary,需要为每个LoadLibrary手动调用FreeLibrary。我想使用带有特定deleterstd::unique_ptr 使其自动释放我的dll 资源。

这就是我想要定义的:

std::unique_ptr<HMODULE, BOOL(*)(HMODULE)> theDll(LoadLibrary("My.dll"), FreeLibrary);

但是编译器抱怨类型不匹配。我发现它期望来自LoadLibrary*HMODULE。那就是std::unique_ptr&lt;A&gt; 将期望A* 作为它的指针类型。看来我仍然需要定义一个新类来管理 DLL 资源(构造函数中的LoadLibrary 和析构函数中的FreeLibrary)。

是否可以让std::unique_ptr&lt;A&gt; 只期望A 作为其指针类型?

更新,

以下是新类和使用 std::unique_ptr 的优缺点, 从答案中总结出来。

创建另一个dll管理类

优点:

  • 完全可控,可针对 DLL 语义进行自定义。
  • 将与 DLL 相关的部分隔离到一个具有一个职责的类中。
  • 如果需要 DLL 的更多功能(例如公开符号),可以轻松扩展。

缺点:

  • 需要重建 RAII 部分,标准自动指针已完成。
  • 有机会在 RAII 部分出错。
  • 需要声明一个新类。

std::unique_ptr 与自定义删除器一起使用,

优点:

  • 无需声明另一个类。
  • 重复使用 unique_ptr 的 RAII 部分。
  • 也许move semantics 阻止了 DLL 模块实例被复制?

缺点:

  • Dll 资源语义可能不符合标准自动指针,并且容易出错?
  • unique_ptr 中的模板参数很复杂,很难找到错误所在。
  • HMODULEvoid*,是无类型类型,与unique_ptr 集成可能有问题?

如果我错了,请在评论中纠正我。

【问题讨论】:

标签: c++ c++11 dll raii


【解决方案1】:

根据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);

【讨论】:

  • 您也可以将BOOL(*)(HMODULE)替换为decltype(&amp;::FreeLibrary)
【解决方案2】:

如果您使用T管理未被T*引用的资源,则需要为unique_ptr提供对应的::pointer类型。这里Tunique_ptr 的第一个模板参数。

如果没有定义::pointer 类型,则使用T*。在您的情况下,HMODULE* 是错误的。

struct tLibraryDeleter
{
  typedef HMODULE pointer;
  void operator()(HMODULE h) { FreeLibrary(h); }
};

std::unique_ptr<HMODULE, tLibraryDeleter>(::LoadLibraryA("My.dll"));

查看herehere

【讨论】:

  • 是的,这应该可以。但是再多几行代码,你就有了漂亮、简单的包装类,而不是为DLLs 定制的奇怪且容易出错的删除器,这很简单,但不是正确的做法。
  • 我建议你重新考虑一下你在说什么。我不知道为什么以及如何将这个定义明确的自定义删除器视为weird and error-prone。如果你真的这么想,至少告诉我们它怎么会出错。如果您有任何疑虑,请查看herehere。事实上,您正在重新发明容易出错的轮子。
  • @MateuszGrzejek 为什么自定义删除器容易出错?难道只是描述告诉unique_ptr在删除的时候应该调用什么释放函数?
  • 这个答案就是我要找的。看起来这是描述自定义类型的不同指针类型的标准方法,HMODULE 而不是 *HMODULE,与 std::unique_ptr 集成。
  • 是的,目前我只需要Windows平台上我的dll资源的自动释放功能。如果我决定为 dll 扩展更多功能或使其更通用,我会考虑将其移至专用类。谢谢。
【解决方案3】:

请在Windows Implementation Libraries (WIL)中查看unique_hmodule

可以按原样使用它,也可以只是借用实现。

【讨论】:

    【解决方案4】:

    看来我还是需要定义一个新的类来管理 DLL 资源

    为什么你认为这是一个坏主意?

    为什么不这样呢?

    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 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&lt;HANDLE, Some_custom_unloader&gt; 的列表/数组是一个糟糕的设计。

    【讨论】:

    • std::unique_ptr 已经实现了delete-on-destruct,所以我想为什么不重用它的代码,只传递自定义删除器。定义一个 typedef 来隐藏其复杂的声明可能会减少创建显式类型类的工作量。
    • 通过将非常重要和特定的问题隐藏在奇怪的结构中,而不是简单、易于维护、可定制的单一职责的类,您避免了打字,但增加了错误的可能性并降低了代码的清晰度但这是您的代码- 你的选择。
    • 虽然没有回答他的问题我认为这是更好的方法+1
    • 我看不出编写自己的类比使用标准功能更不容易出错。
    • @MateuszGrzejek OP 没有要求错误处理或符号加载。 unique_ptr 是一个标准功能,它非常适合 OP 的用例。
    猜你喜欢
    • 1970-01-01
    • 2023-03-18
    • 2014-03-16
    • 1970-01-01
    • 1970-01-01
    • 2012-02-23
    • 2014-12-14
    • 2015-01-01
    • 1970-01-01
    相关资源
    最近更新 更多