Reputation: 893
I need a table that maps codes to C++ member functions. Suppose we have this class:
class foo
{
bool one() const;
bool two() const;
bool call(char*) const;
};
What I want is a table like this:
{
{ “somestring”, one },
{ ”otherstring”, two }
};
So that if I have a foo
object f
, f.call(”somestring”)
would look up “somestring” in the table, call the one()
member function, and return the result.
All of the called functions have identical prototypes, i.e., they are const, take no parameters, and return bool.
Is this possible? How?
Upvotes: 1
Views: 9129
Reputation: 254461
Since you only need to store members of the same class, with the same arguments and return types, you can use pointer-to-member-functions:
bool foo::call(char const * name) const {
static std::map<std::string, bool (foo::*)() const> table
{
{"one", &foo::one},
{"two", &foo::two}
};
auto entry = table.find(name);
if (entry != table.end()) {
return (this->*(entry->second))();
} else {
return false;
}
}
That uses the new initialisation syntax of C++11. If your compiler doesn't support it, there are various other options. You could initialise the map with a static function:
typedef std::map<std::string, bool (foo::*)() const> table_type;
static table_type table = make_table();
static table_type make_table() {
table_type table;
table["one"] = &foo::one;
table["two"] = &foo::two;
return table;
}
or you could use Boost.Assignment:
static std::map<std::string, bool (foo::*)() const> table =
boost::assign::map_list_of
("one", &foo::one)
("two", &foo::two);
or you could use an array, and find the entry with std::find_if
(or a simple for
loop if your library doesn't have that yet), or std::binary_search
if you make sure the array is sorted.
Upvotes: 3
Reputation: 1038
I would go with boost::function
with std::map
. Concretely, something like this :
typedef boost::function<bool()> MyFunc;
typedef std::map<std::string, MyFunc> MyFuncMap;
Then, given an instance of MyFuncMap, you could just do map["something"]()
. Then you could wrap that in a class that overloads operator()
. You could use function pointers/references, but I prefer using boost::function
because it allows me to bind pointers to member functions (using boost::bind) or use other function objects. You can also test boost::function
in conditionals as you would with regular function pointers.
Here is the relevant documentation :
Good luck!
Edit: Regarding your question about the const member and boost::function
, here's an example :
#include <boost/function.hpp>
#include <boost/bind.hpp>
typedef boost::function<bool ()> FuncPtr;
struct Test
{
bool test() const
{
std::cout << "yay" << std::endl;
}
};
int main(int argc, char **argv)
{
Test t;
FuncPtr ptr = boost::bind(&Test::test, &t);
ptr();
}
Upvotes: 2
Reputation: 63775
Yes, it's possible, using pointer to member syntax.
Using the prototypes you supplied, the map would be.
std::map< std::string, bool( foo::*)() const>
It would be called with this syntax
this->*my_map["somestring"]();
That odd-looking ->*
operator is for pointer to member functions, which can have some odd considerations, due to inheritance. (It's not just a raw address, as ->
would expect)
Upvotes: 6
Reputation: 5432
I'd just like to add that a pointer to a member function is meaningless without having an instance of a class on which to call it. The situation you've described accounts for this (and I think you know this), however in other situations, it may be necessary to encapsulate the function pointer with a pointer or reference to the instance to which it corresponds in some sort of functor construct.
Upvotes: 1
Reputation: 361402
Yes.
struct foo_method
{
std::string name;
bool (foo::*pfun)() const;
};
foo_method methodTable[] =
{
{ “somestring”, &foo::one },
{ ”otherstring”, &foo::one }
};
void foo::call(const char* name) const
{
size_t size = sizeof(methodTable)/sizeof(*methodTable);
for(size_t i = 0 ; i < size ; ++i)
{
if ( methodTable[i].name == name )
{
bool (foo::*pfun)() const = methodTable[i].pfun;
(this->*pfun)(); //invoke
}
}
}
Upvotes: 3