3
\$\begingroup\$

I need a function call(f, args...) which calls the "function" f with the arguments args.... It should be compatible with the following "functions":

  1. Normal function pointers functors, lambdas, ... (= "callables"), call syntax: f(args...)
  2. Member function pointers, call syntax: (object.*f)(args...) and (ptr->*f)(args...)
  3. 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 put Args or ProvidedArgs in the template parameter of std::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 && or const&? Currently I use const&.
  • Should the function parameter in case 1. be a universal reference && or const&? Currently I use &&.
\$\endgroup\$
11
  • \$\begingroup\$ A different class of functions that would be good to support are objects with overloaded operator () such as lambdas and std::function. \$\endgroup\$
    – nwp
    Commented Oct 15, 2014 at 18:56
  • \$\begingroup\$ @nwp Thanks, but that's already handled with case 1 (edited to clarify). \$\endgroup\$
    – leemes
    Commented Oct 15, 2014 at 18:58
  • \$\begingroup\$ I think this should compile but doesn't. The perfect forwarding works fine. && or const & makes no difference here because all you do with the pointer is calling it, so having a temporary or not does not matter. \$\endgroup\$
    – nwp
    Commented Oct 15, 2014 at 19:50
  • \$\begingroup\$ @nwp: It can. If you pass an object and hold a const reference to it you can not call a non const function on that object. \$\endgroup\$ Commented Oct 15, 2014 at 19:53
  • \$\begingroup\$ @LokiAstari Overloads 2 and 3 only get called for member function pointers. It does not matter if the member function pointer is const or not. For overload 1 with objects you are right, it would not always work with const &. \$\endgroup\$
    – nwp
    Commented Oct 15, 2014 at 20:04

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.