I was trying to implement an std::unique_ptr
factory that I could use like this:
auto fd = my_make_unique<fclose>(fopen("filename", "r"));
I.e., pass the deleter function as a template argument.
My best attempt in C++11 was:
template<typename D, D deleter, typename P>
struct Deleter {
void operator()(P* ptr) {
deleter(ptr);
}
};
template<typename D, D deleter, typename P>
std::unique_ptr<P, Deleter<D, deleter, P>> my_make_unique(P* ptr)
{
return std::unique_ptr<P, Deleter<D, deleter, P>>(ptr);
}
In C++14 it is much cleaner:
template<typename D, D deleter, typename P>
auto my_make_unique(P* ptr)
{
struct Deleter {
void operator()(P* ptr) {
deleter(ptr);
}
};
return std::unique_ptr<P, Deleter>(ptr);
}
But both solutuions would require me to pass the type of &fclose
before fclose
itself as template argument:
auto fd = my_make_unique<decltype(&fclose), fclose>(fopen("filename", "r"));
Is it possible to get rid of decltype(&fclose)
template argument in C++11? What about in C++14?
EDIT: Why this question is not a duplicate of RAII and smart pointers in C++: referenced question is about general RAII techniques in C++, and one of the answers estates that std::unique_ptr
can be used for this purpose. I am already familiar with RAII pattern and how std::unique_ptr
is a solution, but I am concerned with present question on how to build an easier to use abstraction to this frequent case I encounter when interacting with C libraries.
Is it possible to get rid of
decltype(&fclose)
template argument in C++11? What about in C++14?
No, not until C++17 can you get rid of the type of that parameter. Template non-type parameters need a type, which you can't deduce - because it has to be a template non-type parameter. That's one problem.
Additionally, you have the problem that taking the address of functions in the standard library is unspecified. The standard library is always allowed to provide additional overloads, for instance, so &fclose
may be invalid. The only truly portable way of doing this is to provide a lambda or write your own wrapper function:
auto my_fclose_lam = [](std::FILE* f) { std::fclose(f); }
void my_fclose_fun(std::FILE* f) { std::fclose(f); }
And with either of those, with C++14 at best you can introduce a macro like:
#define DECL(v) decltype(v), v
auto fd = my_make_unique<DECL(my_fclose_lam)>(fopen("filename", "r"));
C++17 allows you to at least lift your custom function into a template parameter (though not yet a lambda) via template auto
:
template <auto deleter, typename P>
auto my_make_unique(P* ptr)
{
struct Deleter {
void operator()(P* ptr) {
deleter(ptr);
}
};
return std::unique_ptr<P, Deleter>(ptr);
}
my_make_unique<my_fclose_fun>(fopen(...));
C++20 will finally allow you to stick a lambda into there:
my_make_unique<[](std::FILE* f){ std::fclose(f); }>(fopen(...));
Old incorrect answer:
So the best you can do is introduce a macro like:
#define DECL(v) decltype(v), v
auto fd = my_make_unique<DECL(&fclose)>(fopen("filename", "r"));
Whether or not you think that's a good idea is probably up to your coworkers.
In C++17, with template auto
, you could just be able to write my_make_unique<fclose>
, which is great:
template <auto deleter, typename P>
auto my_make_unique(P* ptr)
{
struct Deleter {
void operator()(P* ptr) {
deleter(ptr);
}
};
return std::unique_ptr<P, Deleter>(ptr);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With