chrisd
chrisd

Reputation: 893

Tables of C++ member functions

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

Answers (5)

Mike Seymour
Mike Seymour

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

migimunz
migimunz

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

Drew Dormann
Drew Dormann

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

Liam M
Liam M

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

Sarfaraz Nawaz
Sarfaraz Nawaz

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

Related Questions