1
votes

I've written a code to pass a list of function pointer (by its name) as argument. But I have error. Can you explain why I have error while doing map

#include <functional>
#include <iostream>
#include <string>
#include <vector>
#include <map>

class Foo
{
public:
    void foo(int a, int b)
    {
        std::cout << a <<" "<< b<<'\n';
    }
};

class Bar
{
public:
    void bar(int a, int b)
    {
        std::cout << a<<" "<< b << '\n';
    }
};


int main()
{

    Foo foo;
    Bar bar;
    std::map<std::string,  void (*)(int,int)>myMap;
    myMap["bar"] = &Bar::bar;
    myMap["foo"] = &Foo::foo;

    std::vector<std::function<void (int )>> listofName;
    std::string s1("bar");
    std::string s2("foo");

    listofName.push_back(bind(myMap[s1],&bar,std::placeholders::_1,1));
    listofName.push_back(bind(myMap[s2],&foo,std::placeholders::_1,3));

    for (auto f : listofName) {
        f(2);
    }

 return 0;
}

Error:

34:18: error: cannot convert 'void (Bar::)(int, int)' to 'std::map, void (*)(int, int)>::mapped_type {aka void ()(int, int)}' in assignment

35:18: error: cannot convert 'void (Foo::)(int, int)' to 'std::map, void (*)(int, int)>::mapped_type {aka void ()(int, int)}' in assignment

41:70: error: no matching function for call to 'std::vector >::push_back(std::_Bind_helper&)(int, int), Bar, const std::_Placeholder<1>&, int>::type)'

3
void (*)(int,int) no. Bar::bar secretly also takes a Bar member. Why don't you just use std::function though?Hatted Rooster
You are trying to assign pointers to member functions to pointers to functions. It's not allowed. That's all.skypjack
Before putting &Bar::bar to a map and pulling it back to call it, try to do the call directly. &Bar::bar(2); Does it work? Do you expect it to work?n. 1.8e9-where's-my-share m.

3 Answers

4
votes

Member functions need to know what object they're a member of so that they can operate on the correct this.

Therefore you need to make sure the function you store in the map knows what object it's a member of in the future.

int main() {
  using namespace std::placeholders;
  Foo foo;
  Bar bar;
  std::map<std::string, std::function<void(int, int)>> myMap;
  myMap["bar"] = std::bind(&Bar::bar, &bar, _1, _2);
  myMap["foo"] = std::bind(&Foo::foo, &foo, _1, _2);

Later on it then already knows what object it's a member of and you don't need to tell it anymore:

  // ..
  listofName.push_back(std::bind(myMap[s1], _1, 1));
  listofName.push_back(std::bind(myMap[s2], _1, 3));
1
votes

You cannot store member function pointers in such map:

std::map<std::string,  void (*)(int,int)>myMap;

you would have to change it to:

std::map<std::string,  void (Foo::*)(int,int)>myMap;

but then you would be able to store only pointers to Foo class members. So the best choice is to use std::function here. Below is a working code:

Live

#include <functional>
#include <iostream>
#include <string>
#include <vector>
#include <map>

class Foo
{
public:
    void foo(int a, int b)
    {
        std::cout << a <<" "<< b<<'\n';
    }
};

class Bar
{
public:
    void bar(int a, int b)
    {
        std::cout << a<<" "<< b << '\n';
    }
};


int main()
{

    Foo foo;
    Bar bar;
    using namespace std::placeholders;
    std::map<std::string,  std::function<void (int,int)>>myMap;
    myMap["bar"] = std::bind(&Bar::bar, &bar, _1, _2);
    myMap["foo"] = std::bind(&Foo::foo, &foo, _1, _2);

    std::vector<std::function<void (int )>> listofName;
    std::string s1("bar");
    std::string s2("foo");

    listofName.push_back(bind(myMap[s1], std::placeholders::_1, 1));
    listofName.push_back(bind(myMap[s2], std::placeholders::_1, 3));

    for (auto f : listofName) {
        f(2);
    }

 return 0;
}
0
votes

A member function includes a hidden pointer to this. An old technic (inherited from C) is to wrap the member function in a static function taking a pointer to an object. What is nice here, is that as it is safe to cast any pointer to a void * and back again, you can tell the static wrapper that its first parameter is a void * and cast it to the correct object pointer to use it. Of course, if you pass a pointer to a different object you will get Undefined Behaviour. But it only require minimal changes to your original code:

#include <functional>
#include <iostream>
#include <string>
#include <vector>
#include <map>

class Foo
{
public:
    void foo(int a, int b)
    {
        std::cout << a <<" "<< b<<'\n';
    }
    static void doFoo(void *obj, int a, int b) { // the wrapper
        static_cast<Foo *>(obj)->foo(a, b);
    }
};

class Bar
{
public:
    void bar(int a, int b)
    {
        std::cout << a<<" "<< b << '\n';
    }
    static void doBar(void *obj, int a, int b) { // wrapper again
        static_cast<Bar *>(obj)->bar(a, b);
    }
};


using std::bind;

int main()
{

    Foo foo;
    Bar bar;
    // function will take an additional void *
    std::map<std::string,  void (*)(void*, int,int)>myMap;
    myMap["bar"] = &Bar::doBar;
    myMap["foo"] = &Foo::doFoo;

    std::vector<std::function<void (int )>> listofName;
    std::string s1("bar");
    std::string s2("foo");

    listofName.push_back(bind(myMap[s1],(void *)&bar,std::placeholders::_1,1));
    listofName.push_back(bind(myMap[s2],(void *)&foo,std::placeholders::_1,3));

    for (auto f : listofName) {
        f(2);
    }

 return 0;
}

That way it compiles fine (in C++11 mode) and gives as expected:

2 1
2 3