Search code examples
c++qtc++11unique-ptrmupdf

How do you create a custom deleter for a unique_ptr class member that wraps a c function which requires 2 arguments?


I am trying to use mupdf to create a program (in Qt) that will allow me to list the objects of the document as a list and allow me to select which objects to render / not render. Since Qt is c++, and I am more comfortable with it, I am trying to wrap structures defined in mupdf in C++ classes.

Right now my problem is this - one of the first things you do in mupdf is create a global context, that is passed around to everything, including functions that clean up and delete structures.

I am familiar with creating an object that has an overloaded operator(), much like:

struct ContextDeleter
{
    inline void operator()(fz_context* ctx)
    {
        fz_drop_context(ctx);
    }
};

which I can then hand to unique_ptr -

std::unique_ptr<fz_context, ContextDeleter> ctxPtr;

What I can't figure out is how to do the same thing with a function like:

fz_drop_page(ctx, page);

ie:

struct PageDeleter
{
     inline void operator()(fz_context* ctx, fz_page* pg)
     {
          fz_drop_page(ctx, pg);
     }
}

This is obviously incorrect, but is what I am trying to achieve.

How can I create a deleter for unique_ptr that includes 2 arguments (in this case the necessary context pointer)? Is there a way for me to make unique_ptr aware of the context pointer to delete the page (in this example)? Or (one thought I had) do I need to create something that wraps the unique_ptr so I can hand it the context for deletion later somehow (haven't fully thought it through yet).

I have seen the examples here:

How do I use a custom deleter with a std::unique_ptr member?

and

Wrapping of C-code with a unique_ptr and custom deleter

but I can't figure out how to make them work in my case.


Solution

  • Store the fz_context * in the deleter, and pass an instance of that deleter to the unique_ptr that holds the fz_page *

    struct PageDeleter
    {
        explicit PageDeleter(fz_context *ctx)
        : ctx(ctx)
        {}
        void operator()(fz_page* page) const
        {
            fz_drop_page(ctx, page);
        }
        fz_context *ctx;
    };
    

    Construct the unique_ptr as

    fz_context *ctx = // get the fz_context
    fz_page *page = // get the fz_page
    
    PageDeleter page_del(ctx);
    std::unique_ptr<fz_page, PageDeleter> pagePtr(page, page_del);
    

    You could wrap all of this in a make_unique_fz_page function for convenience.