Search code examples
c++unique-ptrraii

Initialise a functor passed into a unique_ptr custom deleter


I'm using RAII to manage resources in my code by making use of std::unique_ptr with custom deleters. So far this has been relatively trivial, but I now want to add an RAII-wrapper for a resource which requires access to an existing managed resource as part of its cleanup functor.

To break it down, I have one class which has an RAII-managed resource and can acquire additional resources which depend on it:

struct CloseHandleFunctor
{
    typedef HANDLE pointer;
    void operator()(const HANDLE h)
    {
        ::CloseHandle(h);
    }
};

using AutoHandle = std::unique_ptr<HANDLE, CloseHandleFunctor>;

class Helper
{
public:
    // acquires a resource associated with h_
    Resource get_resource();
    // releases the resource acquired by get_resource()
    void release_resource(const Resource res);
    // ...
private:
    AutoHandle h_;
};

I want to wrap the Resource returned by Helper::get_resource(), but can't work out how to use std::unique_ptr to provide access to the instance of the Helper class within the functor:

struct ReleaseResourceFunctor
{
    typedef Resource pointer;
    void operator()(Helper h, const Resource r)
    {
        h.release_resource(r);
    }
};

using AutoResource = std::unique_ptr<Resource, ReleaseResourceFunctor>;
   // no way to pass the instance of Helper ~~~^

This is how I would like to use it:

int main()
{
    Helper h;

    {
        AutoResource res(h.get_resource());
    }
    // h.release_resource() gets called
}

Solution

  • As suggested by Joachim and Casey, I can get it working by storing a pointer to the instance of the Helper class in the functor:

    struct ReleaseResourceFunctor
    {
        typedef Resource pointer;
    
        Helper *helper_;
    
        ReleaseResourceFunctor()
            : helper_(nullptr)
        {
        }
    
        void setHelper(Helper * const h)
        {
            helper_ = h;
        }
    
        void operator()(const Resource r)
        {
            if (helper_ != nullptr)
            {
                helper_->release_resource(r);
            }
        }
    };
    

    ...and then by assigning the helper instance after constructing the std::unique_ptr:

    int main()
    {
        Helper h;
    
        {
            AutoResource res(h.get_resource());
            res.get_deleter().setHelper(&h);
        }
        // h.release_resource() gets called as expected
    }
    

    It seems a little fragile (remembering to assign the helper instance each time), but it does work as expected.