将unique_ptr / shared_ptr与API函数一起使用,通过指针将资源作为出参数返回

Kir*_*nko 2 winapi pointers smart-pointers c++11 c++14

我现在在当前项目中赶上C ++ 11/14。我在使用unique_ptr/ shared_ptr和API函数通过指针返回资源作为出参数时遇到麻烦。

让我们考虑DsGetDcName(..., __out PDOMAIN_CONTROLLER_INFO* ppDCI)一个例子。

在c ++ 11之前,存在使用Windows API的广泛模式。我将拥有一个RIIA类,其中包含PDOMAIN_CONTROLLER_INFO,调用NetApiBufferFree析构函数,并具有PDOMAIN_CONTROLLER_INFO* operator&()这样的功能:

info_ptr spInfo;
DsGetDcName(..., &spInfo);
Run Code Online (Sandbox Code Playgroud)

现在使用C ++ 11/14,我看到很多文章都提倡unique_ptr/ shared_ptr使用自定义删除器以适当的方式释放资源。当资源作为函数的r值返回时,它确实很好用(这LoadLibrary是一个很好的例子)。

我发现的一种解决方案是在API调用之后将原始指针附加到智能指针:

PDOMAIN_CONTROLLER_INFO pDCI = nullptr;
DsGetDcName(..., &pDCI);
std::unique_ptr<DOMAIN_CONTROLLER_INFO, decltype(&NetApiBufferFree)> spInfo(pDCI, &NetApiBufferFree);
Run Code Online (Sandbox Code Playgroud)

但是,这不是我喜欢的方式。据我了解,可能不是因为“安全第一”方法而设计新的智能指针来处理参数。

这里真的可以做点什么吗?也许某种函子类用作参数代理,以便我可以编写DsGetDcName(..., &out_param(spInfo))

Rud*_*lis 5

好吧,您可以制作一个临时包装,该包装将std::unique_ptr在销毁时初始化:

#include <windows.h>
#include <Dsgetdc.h>
#include <Lm.h>

#include <memory>

template<typename T, typename D>
struct OutParameterWrapper
{
    OutParameterWrapper(std::unique_ptr<T, D>& Target) : m_Target(Target), m_pSource(nullptr)
    {
    }
    ~OutParameterWrapper()
    {
        m_Target.reset(m_pSource);
    }
    operator T**()
    {
        return &m_pSource;
    }
    std::unique_ptr<T, D>& m_Target;
    T* m_pSource;
};

int main(int argc, char** argv)
{
    std::unique_ptr<DOMAIN_CONTROLLER_INFO, decltype(&NetApiBufferFree)> spInfo(nullptr, NetApiBufferFree);
    DsGetDcName(NULL, NULL, NULL, NULL, 0, OutParameterWrapper<DOMAIN_CONTROLLER_INFO, decltype(&NetApiBufferFree)>(spInfo));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编辑

要进行自动演绎并使其适用于两者std::unique_ptrstd::shared_ptr您可以执行以下操作:

#include <windows.h>
#include <Dsgetdc.h>
#include <Lm.h>

#pragma comment(lib, "Netapi32.lib")

#include <memory>

template<typename T, typename P>
struct OutParameterWrapper
{
    OutParameterWrapper(P& Target) : m_Target(Target), m_pSource(nullptr)
    {
    }
    ~OutParameterWrapper()
    {
        m_Target.reset(m_pSource);
    }
    operator T**()
    {
        return &m_pSource;
    }

    P& m_Target;
    T* m_pSource;
};

template<typename P>
OutParameterWrapper<typename P::element_type, P> MakeOutParameterWrapper(P& Target)
{
    return OutParameterWrapper<typename P::element_type,P>(Target);
}

int main(int argc, char** argv)
{
    std::unique_ptr<DOMAIN_CONTROLLER_INFO, decltype(&NetApiBufferFree)> spInfo(nullptr, NetApiBufferFree);
    std::shared_ptr<DOMAIN_CONTROLLER_INFO> spInfo2(nullptr, NetApiBufferFree);
    auto nResult = DsGetDcName(NULL, NULL, NULL, NULL, 0, MakeOutParameterWrapper(spInfo));
    DsGetDcName(NULL, NULL, NULL, NULL, 0, MakeOutParameterWrapper(spInfo2));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • @KirillKovalenko-在您给出了更详细的约束后,我更新了答案:) (2认同)