Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass a function pointer as a template value argument without creating a function object in C++?

I've seen many variants of this question asked here, but I still feel my specific case is different.

My goal is wrapping a C API that looks like this:

TF_Buffer* buf = TF_AllocateBuffer();
// ...
TF_DeleteBuffer(buf);

Since I have many of these objects, I'd love to create a generic type named handle that could hold a given pointer and call the appropriate deallocator upon destruction. My imagined use case would be

class buffer : public handle<TF_Buffer, TF_DeleteBuffer> {
public:
  buffer(TF_Buffer* b): handle(b) {}
}

unfortunately I'm unable to get this to work since TF_DeleteBuffer is a simple function (of type void TF_DeleteBuffer(TF_Buffer*)). I did manage to work around the issue with creating a function object for the function, so the following does work

template<typename Obj, typename Deleter>
class handle {
public:
  Obj* obj;

  handle(Obj* o): obj(o) {};
  ~handle() { if (obj) Deleter()(obj); }
};

struct buffer_deleter {
  void operator()(TF_Buffer* b) { TF_DeleteBuffer(b); }
};

class buffer : public handle<TF_Buffer, buffer_deleter> {
public:
  buffer(TF_Buffer* b): handle(b) {}
}

but it feels dirty having to define the buffer_deleter class just for this purpose. I'd imagine something like this ought to work (with or without the std::function)

template<typename Obj, std::function<void(Obj*)> Deleter>
class handle {
  // ...
}

but I can't find a way to make the compiler happy. From what I understand, this is somewhat similar to std::unique_ptr which accepts a deleter type object, vs std::shared_ptr which accepts a deleter function pointer and stores it in the shared object. I don't mind storing the pointer explicitly (and using extra memory), but at the same time, given I'll be creating lots of these types, I'd like to have some way of making it syntactically nice. I really do not want to pass the deleter pointer to each instance of the object being created, which is why I'm trying to hide it in the template.

like image 892
Jakub Arnold Avatar asked Mar 04 '23 16:03

Jakub Arnold


1 Answers

You can define a non-type template parameter as function pointer.

template<typename Obj, void(*Deleter)(Obj*)>
class handle {
public:
  Obj* obj;

  handle(Obj* o): obj(o) {};
  ~handle() { if (obj) Deleter(obj); }
};

And use it like

class buffer : public handle<TF_Buffer, &TF_DeleteBuffer> {
  ...
};
like image 161
songyuanyao Avatar answered Mar 15 '23 22:03

songyuanyao