0

Does the C++ standards define a utility for wrapping a function in a type (as distinct from wrapping it in a value)?

After Googling a bunch of names that seem related, I'm not finding anything, but then again, I know of a few things where I'd never guess the name.


Edit:

  • I already know how to get a type that can dynamically take on the value of a function (either std::function or a good old function pointers like int(*f)(int)) but that specifically excludes from the type the one thing I most want to include: the actual function to be called.
  • I already know how to get the type of a function from its name (decltype(fn)) which is not what I want, for the same reasons as listed above.

I find myself needing to make a type where its operator() exactly matches a function foo (in my case, a C function from a 3rd part library). Ideally, the type should inline away to nothing any time it's used. As a one off, this is easy:

struct foo_t {
  ret_t operator()(SomeType t, int i) {
    return foo(t, i);
  }
};

However, there is at least one cases where this is a thing that needs to be done to a bunch of different functions: deleters for std::unique_ptr<T,D> while interfacing with opaque handles (e.g. from C libraries):

std::unique_ptr<FILE*, fclose_t> f(fopen(n, "r"));
std::unique_ptr<obj*, free_t> o((obj*)malloc(sizeof(obj)));
...

Rather than define fclose_t, free_t, etc. myself, what I'd like to do is something like this:

std::unique_ptr<FILE*, type_fn<fclose>> f(fopen(n, "r"));
std::unique_ptr<obj*, type_fn<free>> o((obj*)malloc(sizeof(obj)));
...

And that isn't even that hard... if you are happy to define type_fn yourself:

template<auto *f> struct fn_type {
  template<class... A>
  auto operator() (A&&... a) {
    return f(std::forward<A>(a)...);
  }
};

Which brings me back to the opening question: does C++ define something like that?

BCS
  • 75,627
  • 68
  • 187
  • 294
  • 1
    Seems that you're looking for `decltype` keyword. – van con Nguyen Dec 03 '22 at 03:31
  • @vanconNguyen IIRC `decltype` throws away everything but the calling signature (which is only incidentally of interest here). How can you then go from a just a calling signature to the one correct function (among many) that that signature came from? You can't call a calling signature, you can only call something that *has* a calling signature. – BCS Dec 03 '22 at 04:37
  • *'that isn't even that hard...'* -- Actually, *[Can I take the address of a function defined in standard library?](https://stackoverflow.com/questions/55687044/can-i-take-the-address-of-a-function-defined-in-standard-library)* – Ranoiaetep Dec 03 '22 at 06:26
  • @Ranoiaetep FWIW, the provoking case (and I expect the majority of potential application) invokes 3rd party functions. I re-wrote them using `std::` functions to avoid having to describe details that aren't relevant. -- That said, lambdas do solve the issue with overloaded functions not working... at the expense of loosing compatibility with C++17 and back. – BCS Dec 03 '22 at 18:41

1 Answers1

0

that isn't even that hard... if you are happy to define type_fn yourself...

Actually, you are not supposed to take the address of a function from the standard library(with some exceptions), since the library might make changes to a function that are compatible with a normal use of a function, but not with taking its address (for instance, adding a parameter with a default value, or adding an overload to an overload set).

In fact, taking the address of a function from the standard library is explicitly forbidden since C++20. More can be found here: Can I take the address of a function defined in standard library?

Which mean you can't write something like type_fn<fclose>.


A solution for you is to use a lambda, since the type of each lambdas are guaranteed to be unique:

auto deleter = [](auto *p){ std::fclose(p); };

Now, you can use decltype(deleter) anywhere you would need to specify a type for it:

auto f = std::uniqe_ptr<std::FILE, decltype(deleter)>(std::fopen("file", "r"));

If you only need this deleter once, you can also define the lambda just in the template parameter:

std::uniqe_ptr<std::FILE, decltype(
    [](auto *p){ std::fclose(p); }
)>(std::fopen("file", "r"));
Ranoiaetep
  • 5,872
  • 1
  • 14
  • 39
  • That last form is illegal from C++11 to C++17: `A lambda-expression shall not appear in an unevaluated operand (Clause 8), in a template-argument, in an alias-declaration, in a typedef declaration, [...]` N4659 8.1.5 (2). Even the first form doesn't work prior to C++20 `The closure type associated with a lambda-expression has no default constructor` N4659 8.1.5.1 (11) -- C++20 seems to remove both restriction. Forcing >=C++20 is undesierable. – BCS Dec 03 '22 at 18:35