1

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!

3CEZVQ
43.7k11 gold badges93 silver badges103 bronze badges
asked Nov 28, 2024 at 5:27
5
  • 2
    The problem is that std::unique_ptr<T> wants to hold T*, not T; and pass T* to the deleter. It's difficult to make it work with a resource that's not a pointer. Commented Nov 28, 2024 at 5:31
  • What exactly is fftw_plan? Is it known to be a nullable type that supports assignment/comparison with nullptr? Commented Nov 28, 2024 at 5:32
  • @IgorTandetnik It is only a bit cumbersome if it isn't nullable yet. But pointer-like types can work easily. Commented Nov 28, 2024 at 5:32
  • 1
    Upon closer inspection, std::unique_ptr<T, Deleter> wants to hold Deleter::pointer if that is defined; otherwise T*. So to make this work, you would need a custom deleter that would, among other things, have a member typedef using pointer = fftw_plan;. Just a plain function pointer won't cut it as a deleter. Commented Nov 28, 2024 at 5:36
  • Thank you all for your helpful responses. I'm pleased to say that the issue is now resolved :-) Commented Nov 28, 2024 at 15:47

2 Answers 2

2

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.

answered Nov 28, 2024 at 5:38
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for taking the time to write and explain this. I now have it working within my class structure and it helped me gain deeper knowledge about implementing smart pointers along the way. Attempted to upvote but don't have the necessary reputation score yet :-(
1

A bit to add to this answer: fftw_plan is actually a pointer to fftw_plan_s. Here's a typedef from api:

typedef struct X(plan_s) *X(plan);

So you could keep the deleter and write something like

struct Deleter {
 using pointer = fftw_plan;
 void operator()(pointer p) { fftw_destroy_plan(p); }
};
std::unique_ptr<fftw_plan_s, Deleter> plan(/*create plan*/);
fftw_execute(plan.get());

or even write it without the deleter at all using pointer to function

std::unique_ptr<fftw_plan_s, decltype(&fftw_destroy_plan)>plan(/*create plan*/, fftw_destroy_plan);
fftw_execute(plan.get());

P.S. I'd better comment on the previous answer, but I don't have enough score to do so..

Edit: added option without deleter struct

answered Dec 2, 2024 at 9:14

1 Comment

With the function pointer as deleter, the unique_ptr variables are twice as large as with struct Deleter, because there must be room for the function pointer. Additionally, such unique_ptrs are (unnecessarily?) generic, because they could in theory hold a different function pointer---the spot in the variable is, well, variable, after all.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.