Related topic
std::unique_ptr, deleters and the Win32 API
To use a Win32 Handle as a RAII, I can use the following line
std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&CloseHandle)> m_mutex(CreateMutex(NULL, FALSE, NULL), &::CloseHandle);
For me this is a clean one-liner and does exactly what I want.
When it comes to SOCKET, it won't compile with this same line since SOCKET cannot be nullptr.
What I need to do to make it work is the following :
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));
What I don't like in this implementation is that any different type of ressources I'll want to use, I'll need to copy/paste the same code to only change the closing function.
I could use a Macro, but this is really ugly and can't be used twice
#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, SOCK_STREAM, IPPROTO_UDP), ::closesocket);
I tried to use a template, but I cannot pass my function pointer to the operator() function, as far as I know.
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, SOCK_STREAM, IPPROTO_UDP));
Kerrek SB answered in the comments and it was exactly what I was looking for!
A slightly different approach (within the premises of the RAII idiom though) is to use boost's scope exit.
Example :
Using this functionality, you'd be practically specifying freestanding "RAII destructor actions". Use where it makes your code clearer and cleaner and avoid when all functionality would be more easily incorporated (or already is) inside a class' destructor.
It seems that soon additional RAII functionality will be added to the language. When available you'll be able to use something like
scoped_resource
which looks like this (I'd refer to that link for a full fledged implementation of what you ask)Finally, I want with another Kerrek SB answer. It's the proposal for STD Unique Resource.
Usage
Hope this makes std soon enough!
Here's one possible solution using as an example the NetCDF C API, which has plain ints as handles:
Of course you can check the value in the lambda, if necessary.
And a typedef makes it a little nicer:
And you probably want a function to reduce duplication:
or:
Since
unique_ptr
implementsoperator bool
, you can also use this as a block scope, likeusing
in C#:I often use this in C++11:
usage:
I like it because it works for arbitrary code, not just pointers or handles. Also the cancel function could be omitted if never used.
It is well known the example to RAII a
FILE*
usingstd::unique_ptr
:Alas, a similar approach to POSIX
close()
to RAII a file descriptor is not possible:Although some compilers will work just fine, it is not valid because the
fd==0
is a valid file descriptor! The null one should be-1
. But anyway, even if it were0
it is still not valid, becauseFDDeleter::pointer
shall satisfy the requirements of NullablePointer (summing up):nullptr
.nullptr
.Thus,
UniqueHandle
is born!Its use is pretty easy, best seen with an example:
If you truly want a one-liner you could go with this generalization:
And then just one line:
The problem is that you must add the return of
close()
as a template argument and assume that there isn't anything funny about this function that prevents its conversion to aint(*)(int)
(weird calling conventions, extra parameters, macros...) and that is quite inconvenient.You could add a function wrapper:
But if you are into it, you could as well write the whole
struct FDDeleter
.