Таблицы функций-членов C ++ - PullRequest
1 голос
/ 26 января 2012

Мне нужна таблица, которая отображает коды для функций-членов C ++. Предположим, у нас есть этот класс:

class foo
{
  bool one() const;
  bool two() const;
  bool call(char*) const;
};

Я хочу вот такую ​​таблицу:

{
  { “somestring”,  one },
  { ”otherstring”, two }
};

Так что, если у меня есть foo объект f, f.call(”somestring”) будет искать «somestring» в таблице, вызывать функцию-член one() и возвращать результат.

Все вызываемые функции имеют идентичные прототипы, то есть они являются константными, не принимают параметров и возвращают bool.

Возможно ли это? Как?

Ответы [ 5 ]

6 голосов
/ 26 января 2012

Да, это возможно, используя указатель на член синтаксис.

Используя предоставленные вами прототипы, карта будет иметь вид.

std::map< std::string, bool( foo::*)() const>

Это будет вызвано с этим синтаксисом

this->*my_map["somestring"]();

Этот странно выглядящий оператор ->* предназначен для указателя на функции-члены, которые могут иметь некоторые странные соображения вследствие наследования. (Это не просто необработанный адрес, как ожидал бы ->)

3 голосов
/ 26 января 2012

Да.

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
       }
   }
}
2 голосов
/ 26 января 2012

Поскольку вам нужно хранить только члены одного класса с одинаковыми аргументами и типами возвращаемых данных, вы можете использовать функции указателя на член:

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;
    }
}

, использующий новый синтаксис инициализации:C ++ 11.Если ваш компилятор не поддерживает его, есть другие варианты.Вы можете инициализировать карту с помощью статической функции:

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;
}

, или вы можете использовать Boost.Assignment :

static std::map<std::string, bool (foo::*)() const> table = 
    boost::assign::map_list_of
        ("one", &foo::one)
        ("two", &foo::two);

, или вы можете использовать массив, инайдите запись с помощью std::find_if (или простого цикла for, если в вашей библиотеке его еще нет), или std::binary_search, если вы убедитесь, что массив отсортирован.

2 голосов
/ 26 января 2012

Я бы пошел с boost::function с std::map. Конкретно как то так:

typedef boost::function<bool()> MyFunc;
typedef std::map<std::string, MyFunc> MyFuncMap;

Затем, учитывая экземпляр MyFuncMap, вы можете просто сделать map["something"](). Тогда вы можете обернуть это в класс, который перегружает operator(). Вы можете использовать указатели / ссылки на функции, но я предпочитаю использовать boost::function, потому что это позволяет мне связывать указатели на функции-члены (используя boost :: bind) или использовать другие функциональные объекты. Вы также можете проверить boost::function в условных выражениях, как если бы вы использовали обычные указатели функций.

Вот соответствующая документация:

Удачи!

Редактировать: Относительно вашего вопроса о члене const и boost::function, вот пример:

#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();
}
1 голос
/ 26 января 2012

Я просто хотел бы добавить, что указатель на функцию-член не имеет смысла, если у него нет экземпляра класса для вызова. Ситуация, которую вы описали, объясняет это (и я думаю, что вы это знаете), однако в других ситуациях может потребоваться инкапсулировать указатель функции с указателем или ссылкой на экземпляр, которому он соответствует в некотором виде функтор конструкция.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...