One-liner for RAII on non pointer?

c++, raii, winapi

Solution

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!

Problem

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)); ```

Original source

Related problems