3

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.

Community
  • 1
  • 1
tenspd137
  • 367
  • 1
  • 12

3 Answers3

6

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.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Unfortunetally, C++ has no concept of destructor arguments: if it did, you could reduce the state of your unique ptrs a touch. It would be a strange, difficult language feature to add methinks. – Yakk - Adam Nevraumont Dec 29 '15 at 22:32
  • The OP is looking for a `PageDeleter` implementation, but your example implemented `ContextDeleter` instead, which the OP already has a working implementation for. I updated your answer to swap the values around. – Remy Lebeau Dec 29 '15 at 23:00
1

Using the std::unique_ptr constructor with deleter directly is a bit of an annoyance with lambdas, because you cannot name the type. But you can infer the type using templates like this:

template <typename T, typename D, typename ...Args>
std::unique_ptr<T,D> make_unique_with_deleter( D deleter, Args&&...args )
{
    return std::unique_ptr<T,D>(
        new T( std::forward<Args>(args)... ), 
        std::move( deleter ) );
}

This way, you create a unique_ptr with lambda deleter directly in one place. Here's an example:

int main()
{
    const auto ctx = make_unique_with_deleter<fz_context>(
        []( fz_context * ctx ){ fz_drop_context( ctx ); }, 
        nullptr, nullptr, FZ_STORE_UNLIMITED );
    const auto page = make_unique_with_deleter<fz_page>( 
        [ctx]( fz_page * pg ){ fz_drop_page( ctx.get(), pg ); }, 
        ctx.get(), 1000000 );
    // and so forth ...
}
Ralph Tandetzky
  • 22,780
  • 11
  • 73
  • 120
0

Try using a lambda that captures the fz_context* value, instead of storing it in a custom deleter:

fz_context *ctx = ...;
fz_page *page = ...;

std::unique_ptr<fz_page, std::function<void(fz_page*)>> pagePtr(page, [ctx](fz_page *page){ fz_drop_page(ctx, page); });

Or:

fz_context *ctx = ...;
fz_page *page = ...;

auto deleter = [ctx](fz_page *page){ fz_drop_page(ctx, page); };
std::unique_ptr<fz_page, decltype(deleter)> pagePtr(page, deleter);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770