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));
Finally, I want with another Kerrek SB answer. It's the proposal for STD Unique Resource.
#ifndef UNIQUE_RESOURCE_H_
#define UNIQUE_RESOURCE_H_
#include <type_traits>
// From standard proposal http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3949.pdf
// Slightly modified to compile on VS2012.
namespace std
{
namespace experimental
{
enum class invoke_it { once, again };
template<typename R,typename D>
class unique_resource_t
{
R resource;
D deleter;
bool execute_on_destruction; // exposition only
unique_resource_t& operator=(unique_resource_t const &);
unique_resource_t(unique_resource_t const &); // no copies!
public:
// construction
explicit unique_resource_t(R && resource, D && deleter, bool shouldrun=true)
: resource(std::move(resource))
, deleter(std::move(deleter))
, execute_on_destruction(shouldrun)
{
}
// move
unique_resource_t(unique_resource_t &&other) /*noexcept*/
:resource(std::move(other.resource))
,deleter(std::move(other.deleter))
,execute_on_destruction(other.execute_on_destruction)
{
other.release();
}
unique_resource_t& operator=(unique_resource_t &&other)
{
this->invoke(invoke_it::once);
deleter=std::move(other.deleter);
resource=std::move(other.resource);
execute_on_destruction=other.execute_on_destruction;
other.release();
return *this;
}
// resource release
~unique_resource_t()
{
this->invoke(invoke_it::once);
}
void invoke(invoke_it const strategy = invoke_it::once)
{
if (execute_on_destruction) {
try {
this->get_deleter()(resource);
} catch(...){}
}
execute_on_destruction = strategy==invoke_it::again;
}
R const & release() /*noexcept*/{
execute_on_destruction = false;
return this->get();
}
void reset(R && newresource) /*noexcept*/ {
invoke(invoke_it::again);
resource = std::move(newresource);
}
// resource access
R const & get() const /*noexcept*/ {
return resource;
}
operator R const &() const /*noexcept*/
{
return resource;
}
R operator->() const /*noexcept*/
{
return resource;
}
// Couldn't make this function compile on VS2012
// std::add_lvalue_reference<std::remove_pointer<R>::type>::type operator*() const
// {
// return * resource;
// }
// deleter access
const D & get_deleter() const /*noexcept*/
{
return deleter;
}
};
//factories
template<typename R,typename D>
unique_resource_t<R,D> unique_resource( R && r,D t) /*noexcept*/
{
return unique_resource_t<R,D>(std::move(r), std::move(t),true);
}
template<typename R,typename D>
unique_resource_t<R,D>
unique_resource_checked(R r, R invalid, D t ) /*noexcept*/ {
bool shouldrun = (r != invalid);
return unique_resource_t<R,D>(std::move(r), std::move(t), shouldrun);
}
}
}
#endif /* UNIQUE RESOURCE H */
Usage
auto listenSocket = std::experimental::unique_resource_checked(socket(AF_INET,SOCK_STREAM,IPPROTO_TCP), INVALID_SOCKET, closesocket);
Hope this makes std soon enough!