使用带有API函数的unique_ptr / shared_ptr通过指针

时间:2015-07-27 11:29:30

标签: pointers winapi c++11 smart-pointers 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);

现在使用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);

然而,这不是我喜欢的方式。据我所知,新的智能指针并非设计用于输出参数,可能是因为“安全第一”的方法。

这里可以做些什么吗?也许某种仿函数类作为参数代理,所以我可以写DsGetDcName(..., &out_param(spInfo))

2 个答案:

答案 0 :(得分:4)

好吧,你可以创建一个临时包装器,在破坏时初始化你的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;
}

修改

要自动扣除并让它适用于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;
}

答案 1 :(得分:0)

到目前为止,我想出了这段代码:

template<typename T>
class out
{
    T* _p;
    std::shared_ptr<T>& _sp;
public:
    out(std::shared_ptr<T>& sp) : _p(nullptr) , _sp(sp) {}
    ~out() { _sp.reset(_p); }
    T** operator&() { _ASSERT(nullptr == _p); return &_p; }
};

但它有两个缺点

  1. 使用时看起来很重,因为类模板类型不是从构造函数推导出来的,例如我必须写GetMeow(&out<Meow>(sp))而不是GetMeow(&out(sp))
  2. 同名不适用于shared_ptrunique_ptr
  3. 是否有任何模板忍者?

    编辑:

    好的,我将Rudolfs的工作与我的一些调整相结合,最终结果如下:https://gist.github.com/kirillkovalenko/8219e7c1afea9b5da5da