I need a function call(f, args...)
which calls the "function" f
with the arguments args...
. It should be compatible with the following "functions":
- Normal function pointers functors, lambdas, ... (= "callables"), call syntax:
f(args...)
- Member function pointers, call syntax:
(object.*f)(args...)
and(ptr->*f)(args...)
- Member data pointers, call syntax:
(object.*f)
and(ptr->*f)
In the latter two cases, the object
is the first of the arguments given to call
and the args...
the remaining arguments given to call
. Also, they should work with objects, pointers and smart pointers (for which I assume the smart pointer class implements operator*
returning a T&
, so I don't use the syntax ptr->*f
but rather (*ptr).*f
).
As I thought there is no such thing in the standard library (but I remember they have something similar, e.g. they have mem_fun
as a wrapper which could be useful here), I wrote it by myself.
Currently, the member versions (2. + 3.) are only needed for const
objects/pointers and const
member function/data pointers.
// 1.
// Normal function call (also supports functors / lambdas)
template <class Fn, class ...Args>
auto call(Fn &&f, Args && ...args) -> decltype(f(std::forward<Args>(args)...)) {
return f(std::forward<Args>(args)...);
}
// 2.
// Member function call on instance
template <class T, class Ret, class ...Args, class ...ProvidedArgs>
Ret call(Ret (T::*memFn)(Args...)const, const T &x, ProvidedArgs && ...args) {
return (x.*memFn)(std::forward<ProvidedArgs>(args)...);
}
// Member function call on dereferenced raw pointer
template <class T, class Ret, class ...Args, class ...ProvidedArgs>
Ret call(Ret (T::*memFn)(Args...)const, const T *x, ProvidedArgs && ...args) {
return ((*x).*memFn)(std::forward<ProvidedArgs>(args)...); // or ->*
}
// Member function call on dereferenced smart pointer
template <class T, class Ret, class SmartPtr, class ...Args, class ...ProvidedArgs>
Ret call(Ret (T::*memFn)(Args...)const, const SmartPtr &x, ProvidedArgs && ...args) {
return ((*x).*memFn)(std::forward<ProvidedArgs>(args)...);
}
// 3.
// Member variable on instance
template <class T, class Ret>
Ret call(Ret (T::*memVar), const T &x) {
return (x.*memVar);
}
// Member variable on dereferenced raw pointer
template <class T, class Ret>
Ret call(Ret (T::*memVar), const T *x) {
return ((*x).*memVar); // or ->*
}
// Member variable on dereferenced smart pointer
template <class T, class Ret, class SmartPtr>
Ret call(Ret (T::*memVar), const SmartPtr &x) {
return ((*x).*memVar);
}
Is there something to be improved (performance-wise, usability-wise, ...)? Can you think of a different case of "functions" which can be useful to be supported by my call
function?
My open questions about the current implementation are:
- Is my use of universal references and
std::forward
correct to achieve "perfect forwarding" to the target function? In case 2., I'm not sure if I should putArgs
orProvidedArgs
in the template parameter ofstd::forward
. I need two packs to be deduced separately for the function type and the parameters for the case they don't match (but can be converted). - Should the smart pointer parameter in cases 2. + 3. be a universal reference
&&
orconst&
? Currently I useconst&
. - Should the function parameter in case 1. be a universal reference
&&
orconst&
? Currently I use&&
.
&&
orconst &
makes no difference here because all you do with the pointer is calling it, so having a temporary or not does not matter. \$\endgroup\$const
or not. For overload 1 with objects you are right, it would not always work withconst &
. \$\endgroup\$