相关话题
std::unique_ptr,deleters and the Win32 API
要使用Win32句柄作为RAII,我可以使用以下行
std::unique_ptr<std::remove_pointer<HANDLE>::type,decltype(&CloseHandle)> m_mutex(CreateMutex(NULL,FALSE,NULL),&::CloseHandle);
对我来说,这是一个干净的一线,完全正是我想要的.
对于SOCKET而言,由于SOCKET不能为空,所以不会使用同一行进行编译.
我需要做的是使其工作如下:
struct SocketDeleter { typedef SOCKET pointer; void operator()(SOCKET h) { ::closesocket(h); } }; // Start listen socket. std::unique_ptr<SOCKET,SocketDeleter> sock(socket(AF_UNSPEC,SOCK_STREAM,IPPROTO_UDP));
我在这个实现中不喜欢的是,我想要使用的任何不同类型的资源,我需要复制/粘贴相同的代码才能更改关闭功能.
我可以使用一个宏,但这真的很丑,不能使用两次
#define RAII_UNIQUE_RESOURCE(varName,classType,init,closure) \ struct deleterMacro \ { \ typedef classType pointer; \ void operator()(classType h) \ { \ closure(h); \ } \ }; \ std::unique_ptr<classType,deleterMacro> varName(init); // Compile,but breaks as soon as 2 sockets defined. RAII_UNIQUE_RESOURCE(sock,SOCKET,socket(AF_UNSPEC,IPPROTO_UDP),::closesocket);
我试图使用一个模板,但是据我所知,我不能将我的函数指针传递给operator()函数.
template<class T,class methodDeclaration,class pFuncPointer> struct deleter { typedef T pointer; void operator()(T h) { // Is there a way?? methodDeclaration toCall = pFuncPointer; toCall(h); } }; // With a call such as ... std::unique_ptr<SOCKET,deleter<SOCKET,std::function<decltype(::closesocket)>,::closesocket>> sock2(socket(AF_UNSPEC,IPPROTO_UDP));
解决方法
使用std :: unique_ptr,RAII一个FILE *的例子是众所周知的:
struct FILEDeleter { typedef FILE *pointer; void operator()(FILE *fp) { fclose(fp); } }; typedef std::unique_ptr<FILE,FILEDeleter> FilePtr; FilePtr f(fopen("file.txt","r"));
唉,类似于POSIX的方法close()对RAII的文件描述符是不可能的:
struct FDDeleter { typedef int pointer; void operator()(int fd) { close(fp); } }; typedef std::unique_ptr<int,FDDeleter> FD;
虽然一些编译器将工作正常,但它是无效的,因为fd == 0是一个有效的文件描述符!空值应为-1.但是无论如何,即使是0,它仍然无效,因为FDDeleter ::指针将满足NullablePointer(求和)的要求:
>它应该与nullptr相当.
>它将被值初始化为等于nullptr的值.
因此,UniqueHandle诞生了!
#include <memory> template <typename T,T TNul = T()> class UniqueHandle { public: UniqueHandle(std::nullptr_t = nullptr) :m_id(TNul) { } UniqueHandle(T x) :m_id(x) { } explicit operator bool() const { return m_id != TNul; } operator T&() { return m_id; } operator T() const { return m_id; } T *operator&() { return &m_id; } const T *operator&() const { return &m_id; } friend bool operator == (UniqueHandle a,UniqueHandle b) { return a.m_id == b.m_id; } friend bool operator != (UniqueHandle a,UniqueHandle b) { return a.m_id != b.m_id; } friend bool operator == (UniqueHandle a,std::nullptr_t) { return a.m_id == TNul; } friend bool operator != (UniqueHandle a,std::nullptr_t) { return a.m_id != TNul; } friend bool operator == (std::nullptr_t,UniqueHandle b) { return TNul == b.m_id; } friend bool operator != (std::nullptr_t,UniqueHandle b) { return TNul != b.m_id; } private: T m_id; };
它的用法很容易,最好用一个例子来看:
struct FDDeleter { typedef UniqueHandle<int,-1> pointer; void operator()(pointer p) { close(p); } }; typedef std::unique_ptr<int,FDDeleter> FD; FD fd(open("test.txt",O_RDONLY));
如果你真的想要一个班轮,你可以随着这个泛化:
template <typename T,T TNul = T(),typename RD,RD (*D)(T)> struct OLDeleter { typedef UniqueHandle<T,TNul> pointer; void operator()(pointer p) { D(p); } };
然后只有一行:
std::unique_ptr<int,OLDeleter<int,-1,int,close> > FD fd(open("test.txt",O_RDONLY));
问题是你必须添加close()的返回值作为模板参数,并假设没有任何有趣的这个函数,阻止它转换为int(*)(int)(奇怪的调用约定,额外的参数,宏…),这是非常不方便的.
void my_close(int fd) { close(fd); }
但是,如果你进入它,你也可以写整个结构FDDeleter.