Reputation: 383
I am trying to create a map that contains member function pointers of different classes. The member functions all have the same signature. In order to do this all my classes inherit an Object class which only has default constructor, virtual destructor and a virtual ToString() const method.
// The map looks like this
map<Object*, void (Object::*)()> mapOfMethodPointers;
// And here are the two classes that inherit Object
// Class A
class A : public Object
{
public:
void AFunc();
string ToString() const override;
};
void A::AFunc()
{
cout << "AFunc()" << endl;
}
string A::ToString() const
{
cout << "A" << endl;
}
// Class B
class B : public Object
{
public:
void BFunc();
string ToString() const override;
}
void B::BFunc()
{
cout << "BFunc()" << endl;
}
string B::ToString() const
{
cout << "B" << endl;
}
// Here is how add the member function pointers in the map
A a;
B b;
mapOfMethodPointers[*a] = &A::AFunc;
mapOfMethodPointers[*b] = &B::BFunc;
When I add both of the member function pointers in the map I get the following errors:
Regardless of the fact that both class A and class B are Objects, I can't make this convertions. How can I achieve such thing? I need something like polymorphism for member function pointers. The implementation I chose doesn't work. Any ideas?
Upvotes: 1
Views: 2583
Reputation:
You have to cast the derived member functions to base class member functions. The syntax for this is a bit clunky but perfectly logical. One restriction is that the base class can't be abstract.
Here is an example:
#include <iostream>
#include <map>
#include <string>
using namespace std;
class plant
{
public:
map<string, void(plant::*)(void)> _action;
void Action(string action);
};
void plant::Action(string action)
{
(this->*_action[action])();
}
class flower: public plant
{
public:
flower(void);
void look_at(void);
void smell(void);
void pick(void);
};
flower::flower(void)
{
_action["look at"] = (void(plant::*)(void))&flower::look_at;
_action["pick"] = (void(plant::*)(void))&flower::pick;
_action["smell"] = (void(plant::*)(void))&flower::smell;
}
void flower::look_at(void)
{
cout << "looking at flower" << endl;
}
void flower::smell(void)
{
cout << "smelling flower" << endl;
}
void flower::pick(void)
{
cout << "picking flower" << endl;
}
int main()
{
plant *myplant = new flower();
myplant->Action("look at");
myplant->Action("pick");
myplant->Action("smell");
delete myplant;
}
Upvotes: 0
Reputation: 27365
In order to do this all my classes inherit an Object class which only has default constructor, virtual destructor and a virtual ToString() const method.
This is a bad solution for storing polymorphic functions with similar signatures.
Here are two better solutions:
'1. Implement your function pointers as specializations of a base interface (Object
in your case). Then, in the client code store the interfaces themselves:
struct Object { virtual void Execute() = 0; }
/// old: map<Object*, void (Object::*)()> mapOfMethodPointers;
/// new:
std::vector<Object*> objects;
objects[10]->Execute(); // execution is agnostic of whichever instance you you use
In this solution, Execute would resolve to A::Execute
, as defined below:
class A : public Object
{
void AFunc();
public:
virtual void Execute() override { AFunc(); }
};
With this solution, you do not need a function map (because the virtual table of Object
is essentially a function map).
'2. Implement your function map in terms of generic functions, then fill it with lambdas:
Code:
/// old: map<Object*, void (Object::*)()> mapOfMethodPointers;
/// new:
map<Object*, std::function<void()>> mapOfMethodPointers;
// filling the map:
class A // not needed: public Object
{
public:
void AFunc(); // this is our interesting function
string ToString() const override;
};
A obj;
mapOfMethodPointers[&obj] = [&obj]() { obj.AFunc(); };
Upvotes: 5