I've got a need to write RAII wrapper functions/classes to some C library. I know we can use a smart pointer and pass a deleter
function, like this:
FILE* pf = fopen("NoSuchFile", "r");
shared_ptr<FILE> p1{pf, fclose}; // OK.
But, for more complex scenarios other than fopen()
/fclose()
, I don't wish to write code to pass deleter
functions each time I declare such a wrapper. Especially if we decide to update/replace the deleter
function, a lot of code has to be changed.
What I wish to have, is something like this (to make code cleaner):
template<typename T, fn ???? > // how to specify this "fn" parameter?
using sp_with_deleter = shared_ptr<T, fn>;
Then in client code, I can do this:
using smartFp = sp_with_deleter<FILE*, fclose>;
...
FILE* f1 = fopen(xxx);
FILE* f2 = fopen(yyy);
smartFp sf1(f1); // no need to pass deleter function.
smartFp sf2(f2);
...
Is there a way to achieve this sp_with_deleter
type?
CodePudding user response:
What you propose will NOT work for std::shared_ptr
, simply because the deleter
is not a template parameter of std::shared_ptr
. So, you can't specify a deleter
in a typedef
/using
statement when aliasing a std::shared_ptr
type. It can only be specified in the std:::shared_ptr
's constructor.
If you don't want to specify a deleter
on every std::shared_ptr
variable declaration, then you will need to write a wrapper function instead, eg:
auto make_smartFp(FILE* f) {
return std::shared_ptr<FILE>{f, &fclose};
// alternatively:
auto deleter = [](FILE* f){ fclose(f); };
return std::shared_ptr<FILE>{f, deleter};
}
...
auto sf1 = make_smartFp(fopen(xxx)); // no need to pass deleter function.
auto sf2 = make_smartFp(fopen(yyy));
...
You can do something similar for std::unique_ptr
, too. Even though the type of the deleter
is a template argument, when using a function or lambda as the deleter
then the actual deleter
must be passed in the constructor, eg:
auto make_smartFp(FILE* f) {
return std::unique_ptr<FILE, decltype(&fclose)>{f, &fclose};
// alternatively:
auto deleter = [](FILE *f){ fclose(f); };
return std::unique_ptr<FILE, decltype(deleter)>{t, deleter};
}
...
smartFp sf1(fopen(xxx)); // no need to pass deleter function.
smartFp sf2(fopen(yyy));
...
Otherwise, you can instead make the deleter
be a default-constructable class type, then you don't need to pass it in a parameter to std::unique_ptr
's constructor, eg:
struct FILE_deleter {
void operator()(FILE *f) { fclose(f); }
};
using smartFp = std::unique_ptr<FILE, FILE_deleter>;
...
smartFp sf1(fopen(xxx)); // no need to pass deleter function.
smartFp sf2(fopen(yyy));
...