-3

Consider the following hypothetical example:

template<typename T, typename R, typename... Ps>
R call(T& t, R (T::*method)(Ps...), Ps... ps){
    return (t.*method)(ps...);
}

struct A{
    int f(int i) const {return i;}
};
A a;

Then call(a, &A::f, 3) wont compile, because f is const. Can I make call work without providing the following overload:

template<typename T, typename R, typename... Ps>
R call(T& t, R (T::*method)(Ps...) const, Ps... ps){
    return (t.*method)(ps...);
}
Bubaya
  • 615
  • 3
  • 13
  • 1
    It will compile just fine. I don't understand the question. – user17732522 Oct 08 '22 at 00:02
  • 1
    [Works fine for me](https://godbolt.org/z/GYbzxcqn8). Did you mean to ask about one where `A::f` is not `const`, or where `const` is not in the signature of `call`? – Nathan Pierson Oct 08 '22 at 00:04
  • `template decltype(auto) call(T&& t, F&& f, Args&&...args) {return (t.*std::forward(f))(std::forward(args)...); }` ??? – fabian Oct 08 '22 at 00:10
  • @user17732522 One const too much, edit. Now it won't compile. But I don't want to write the entire function twice for the overload by constness. – Bubaya Oct 08 '22 at 00:12
  • @Bubaya Is there any reason to enforce this structure of the function pointer at all? Why not simply accept any type for `method` (i.e. a template parameter directly)? What is the purpose of this function? There is already `std::invoke` in the standard library which does what you want your `call` to do and much more. – user17732522 Oct 08 '22 at 00:29
  • @user17732522 The acual `call` is a wrapper that does more. How would a more generic version look like? – Bubaya Oct 08 '22 at 00:31
  • @Bubaya `template decltype(auto) call(F&& f, Args&&... args) { /* do whatever here */ return std::invoke(std::forward(f), std::forward(args)...); }`. Split out the first `args` if you need the object pointer specifically. You can also add a `static_assert` on `std::is_member_function_pointer` if you need to diagnose whether a member function pointer was passed. – user17732522 Oct 08 '22 at 00:33
  • @user17732522 This works with functions, but not methods? Where would you put the object? – Bubaya Oct 08 '22 at 00:35
  • @Bubaya It works with everything that is callable. The object pointer or reference is passed as the second argument to `invoke` (so the arguments are in reverse to your code). – user17732522 Oct 08 '22 at 00:36
  • @user17732522 I see. Feel free to turn this into an answer. – Bubaya Oct 08 '22 at 00:54

1 Answers1

0

There is already a standard library function that allows calling any callable, including member function pointers. You can simply use that for your wrapper function and it will automatically allow using any kind of callable as well:

With C++20:

decltype(auto) call(auto&& f, auto&&... args) {
    /* do whatever you want here */
    return std::invoke(decltype(f)(f), decltype(args)(args)...);
}

This would be passed the member function pointer as first argument, the class object as second argument and the member function arguments after that.

If you need access to the class object you can split this out and use the member function pointer call syntax specifically:

decltype(auto) call(auto&& f, auto&& t, auto&&... args)
{
    /* do whatever you want with t here */
    return (decltype(f)(f).*decltype(t)(t))(decltype(args)(args)...);
}
user17732522
  • 53,019
  • 2
  • 56
  • 105