c++smart-pointersunique-ptrfftw

Using unique_ptr and custom deleter with the fftw3 library


When using the fftw3 fast fourier transform (FFT) library, a plan variable is declared and initialised, it is then used to perform some FFTs and subsequently destroyed when the memory is to be freed:

#include <fftw3.h>

// Declare plan
fftw_plan myPlan;

// Initialise plan
myPlan = fftw_plan_dft_r2c(3, m, f, reinterpret_cast<fftw_complex*>(fk.get()), FFTW_PATIENT);

// Perform an FFT
fftw_execute(myPlan);

// Free the memory associated with the plan
fftw_destroy_plan(myPlan);

In the above: m, f and fk.get() are raw pointers to arrays and the code runs perfectly well.

However, I've been attempting to set up this process using a smart pointer that will automatically free the memory when the plan goes out of scope. This is what I have come up with:

std::unique_ptr<fftw_plan, decltype(&fftw_destroy_plan)> myPlan(
    fftw_plan_dft_r2c(3, m, f, reinterpret_cast<fftw_complex*>(fk.get()), FFTW_PATIENT),
    fftw_destroy_plan
);

Unfortunately, I get the following compiler error:

no instance of constructor "std::unique_ptr<_Tp, _Dp>::unique_ptr [with _Tp=fftw_plan, _Dp=void (*)(fftw_plan p)]" matches the argument listC/C++(289)
strFunc.cc(26, 9): argument types are: (fftw_plan, void (fftw_plan p))

I can see that the types of the second arguments don't match, but dereferencing doesn't seem to fix the issue (and I don't see why it would).

If anyone has some insights as to how to progress, I would greatly appreciate it!

Thanks!


Solution

  • If I understand the documentation correctly, then fftw_plan is an opaque pointer type.

    In that case you can do:

    struct deleter {
        // this alias is important for unique_ptr
        using pointer = fftw_plan;
        void operator()(pointer plan) const { fftw_destroy_plan(plan); } 
    };
    
    std::unique_ptr<void, deleter> myPlan(fftw_plan_dft_r2c(/*...*/));
    

    The important part here is that pointer must be a type that behaves like a pointer with respect to nullptr. That means there is an empty state that compares equal to nullptr (while others don't) and can be assigned/constructed from nullptr. unique_ptr needs this state for an empty or moved-from instance.