Home > Software engineering >  How to pass c function/lambda as none-type parameter to a typedef/using template?
How to pass c function/lambda as none-type parameter to a typedef/using template?

Time:08-16

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));
...
  • Related