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))?
好吧,您可以制作一个临时包装,该包装将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_ptr,std::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)