passing member-function as argument to function-template
Asked Answered
K

2

6

Consider three ways to implement a routine in c++: through functors, member functions, and non-member functions. For example,

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

class FOO
{
public:
  void operator() (string word)         // first: functor
  {
    cout << word << endl;
  }

  void m_function(string word)          // second: member-function
  {
    cout << word << endl;
  }
} FUNCTOR;


void function(string word)              // third: non-member function
{
  cout << word << endl;
}

Now consider a template-function to call the three functions above:

template<class T>
void eval(T fun)
{
  fun("Using an external function");
}

What is the proper way to call FOO::m_function through eval? I tried:

FUNCTOR("Normal call");               // OK: call to ‘void FOO::operator()(string)‘
eval(FUNCTOR);                        // OK: instantiation of ‘void eval(T) [with T = FOO]’

function("Normal call");              // OK: call to ‘void function(string)’
eval(function);                       // OK: instantiation of ‘void eval(T) [with T = void (*)(string)]’

FUNCTOR.m_function("Normal call");    // OK: call to member-function ‘FOO::m_function(string)’
eval(FUNCTOR.m_function);             // ERROR: cannot convert ‘FOO::m_function’ from type
                                      //        ‘void (FOO::)(std::string) {aka void (FOO::)(string)}’
                                      //        to type ‘void (FOO::*)(std::basic_string<char>)’
                                      // In instantiation of ‘void eval(T) [with T = void (FOO::*)(string)]’:
                                      // ERROR: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘fun (...)’, e.g. ‘(... ->* fun) (...)’
Kliman answered 3/6, 2015 at 1:25 Comment(4)
ERROR: must use ‘.*’ or ‘->*’ to call pointer-to-member function Does that line in the error message give a hint?Petronia
I tried eval(T fun, O object){ object.*fun("...") } with no resultsKliman
Use (object.*fun)("...") insteadCollencollenchyma
Use eval(T fun, O&& object) to avoid copying your object each time you call eval.Brilliance
J
9

A pointer to member function and a pointer to function are two different beasts. The former takes an implicit first argument, the this pointer, or in other words, a pointer to the instance on which the member function is to be invoked on.

Typically, in order to be able to pass the member function as a callable object, you bind the instance on which it is to be invoked on, and then use placeholders to indicate arguments that will be passed to the callable later. In your case

eval(std::bind(&FOO::m_function, &FUNCTOR, std::placeholders::_1));

The first argument to bind above is the pointer to member function that you want to invoke, and the second is a pointer to the FOO instance on which you want to invoke m_function. The last one is a placeholder that indicates the first argument passed to the callable created by bind should be used when calling the member function.

Another way to do this is to pass a lambda expression to eval that takes a char const * (or std::string const&) argument and calls the member function.

eval([](char const *c) { FUNCTOR.m_function(c); });

Live demo

Jeramey answered 3/6, 2015 at 1:36 Comment(3)
What about declaring m_function as static? Then I can call it as eval(FUNCTOR.m_function). Should I avoid this?Kliman
@Collencollenchyma Oh, never mind. The problem is that we do want to bind something and mem_fn lacks the needed functionality.Fistic
@Kliman If m_function is guaranteed not to use the state of FOO, then it should be declared static, and the problem does go away. Always think twice before creating a class with no state: unless there's metaprogramming involved, perhaps you really want a namespace instead.Fistic
B
2

Inside eval, you cannot call a member function because you don't have any object;

you may go for this:

template<class T, class U>
void eval(T&& object, U fun)
{
    (object.*fun)("Using an external function");
}

and then

eval(FUNCTOR, &FOO::m_function);
Brilliance answered 3/6, 2015 at 1:35 Comment(1)
Good approach, but I have another function similar to eval which I cannot edit :(Kliman

© 2022 - 2024 — McMap. All rights reserved.