C ++ (без C ++ 11) обратный вызов функции получения класса через std :: map

0

Я пытаюсь создать механизм синтаксического анализа для получения данных из библиотеки буфера протокола Google (GPB) в C++. Я разбираю объект из сериализованных данных и получаю объект из класса Config. Я хотел бы получить данные из этого объекта config который содержит массивы и несколько вложенных объектов и ссылок (помните GPB). Вместо жесткого кодирования:

if command == "config.wireless.0.ap.0.essid":
    return config.wireless(0).ap(0).essid();
else if command == "config.ip"
    return config.ip();

Я хочу создать карту для каждого уровня дерева:

std::map<std::string, Callback> map;
map["ip"] = config.ip;

Затем оценивает каждый маркер (. Разделители) с соответствующей картой для выборки обратного вызова.

Проблема /TRDL ;: Возникает проблема с отображением функций getter из классов в обратные вызовы.

Мой код:

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

class AP
{
public:
    AP() : _key(0), _essid("my ssid!") {}
    int key() { return _key; }
    std::string essid() { return _essid; }
private:
    std::string _essid;
    int _key;
};

class Wireless
{
public:
    Wireless() : _key(0)
    {
        AP ap;
        _aps.push_back(ap);
    }
    int key() { return _key; }
    AP &ap(int index) { return _aps[index]; }
private:
    std::vector<AP> _aps;
    int _key;
};

class Config
{
public:
    typedef std::string *(*FnPtr)(void);

    Config() : _ip("10.0.0.1")
    {
        Wireless wireless;
        _wireless.push_back(wireless);
    }

    Wireless &wireless(int index) { return _wireless[index]; }
    std::string ip() { return _ip; }
private:
    std::vector<Wireless> _wireless;
    std::string _ip;
};



void compute(const std::string &cmd, Config &config)
{
    std::map<std::string, Config::FnPtr> fn_map;
    fn_map["ip"] = config.ip;

    std::cout << fn_map[cmd]() << std::endl;
}

int main(int argc, char* argv[])
{
    Config config;
    std::string cmd = "ip";

    compute(cmd, config);

    return 0;
}

Без использования C++ 11 std::function/bind (к сожалению), как я могу заставить это поведение работать? Кто-то упомянул, что C++ не позволяет отражать (что я пытаюсь сделать), поэтому, надеюсь, есть способ обойти это.

Кроме того, я использую синтаксис, похожий на SNMP, если кто знает, как SNMP переводит строки mywireless.0.interface.1.ip в соответствующую функцию, чтобы захватить значение, которое мне было бы интересно.

  • 0
    Ваш typedef выглядит неправильно. Я думаю, что вы хотите typedef std::string (*FnPtr)(void);
Теги:
callback
reflection

2 ответа

1
Лучший ответ

function и bind были первоначально добавлены как часть TR1, поэтому, даже если вы не можете использовать С++ 11, возможно, включите <tr1/functional> вариант?

Если так

#include <tr1/functional>

// ...

void compute(const std::string &cmd, Config &config)
{
    std::map<std::string, std::tr1::function<std::string()> > fn_map;
    fn_map["ip"] = std::tr1::bind(&Config::ip, config);

    std::cout << fn_map[cmd]() << std::endl;
}

Демо-версия

  • 0
    Это прекрасно работает, однако, возвращение каждой функции-члена для разных классов будет различным, поэтому строка: std::map<std::string, std::tr1::function<std::string()> > fn_map; терпит неудачу, потому что ожидает возврата std :: string. Проблема в том, что я не могу контролировать тип возвращаемых объектов класса. Есть ли способ обойти это?
  • 0
    После просмотра этого я не вижу никакого возможного способа сделать это, возможно, в C11, но не в 03. У меня есть проблема, возвращаемое значение этих функций может быть чем-либо из сторонней ссылки на класс, указателя или любых примитивов. Использование шаблонов не было бы способом сделать это, поскольку я не нашел способ, чтобы это работало. Есть ли хороший способ решить имеющееся у меня решение? Пути строки SNMP для разрешения глубины класса. "ip.0.wireless.0.ssid" -> ip(0).wireless(0).ssid();
Показать ещё 1 комментарий
2

Твоя проблема:

std::map<std::string, Config::FnPtr> fn_map;
    fn_map["ip"] = config.ip;

Config::FnPtr:

typedef std::string *(*FnPtr)(void);

Описанный на английском языке, это "указатель на функцию, которая не принимает аргументов и возвращает указатель на std::string ".

"config.ip" - это "указатель на метод-член класса Config, который не принимает аргументов и возвращает std::string ".

Эти два варианта не совпадают, следовательно, ваша ошибка.

Дело не только в том, что вы, вероятно, означали, что ваш typedef будет "typedef std :: string (* FnPtr) (void)". Даже если вы исправите это, это все равно не сработает. Это потому, что у вас будет:

"Функция, которая не принимает аргументов и возвращает указатель на std::string ".

а также

"Указатель на метод члена класса Config, который не принимает аргументов и возвращает std::string ".

Эти два варианта все еще не совпадают, и нельзя назначить другому.

Так что ты можешь сделать? Ну, это зависит от ваших точных намерений. Я собираюсь предположить, что вы, вероятно, хотите что-то вроде этого:

Во-первых, измените свой typedef:

typedef std::string (Config::*FnPtr)(void);

Это объявляет FnPtr как "указатель на метод-член класса Config, который не принимает никаких аргументов и возвращает std::string.

Затем перепишите свой метод compute():

void compute(const std::string &cmd, Config &config)
{
    std::map<std::string, Config::FnPtr> fn_map;
    fn_map["ip"] = &Config::ip;

    std::cout << (config.*fn_map[cmd])() << std::endl;
}

Описание на английском языке &Config::ip является "указателем на член конфигурации классов с именем ip ". Этот член оказывается "Метод, который не принимает аргументов и возвращает строку std ::".

Выражение (config.*fn_map[cmd])() может быть описано на английском языке как: доступ к члену экземпляра класса с именем config который указан выражением "fn_map [cmd]".

Основываясь на описаниях, которые я вам дал, вы должны сами выяснить, почему это работает.

  • 0
    Это не решает проблему ОП. Он хочет иметь возможность хранить функции-члены разных классов (хотя и с одной и той же сигнатурой) на map , например Config::ip() и AP::essid() . Вот почему вам нужно какое-то решение по стиранию типов, например std::function .
  • 0
    std :: function не поможет. Когда у вас есть нестатическая функция-член класса, в отличие от простой функции или статической функции-члена класса, по сути, сам класс является частью сигнатуры функции с определенной точки зрения. Таким образом, никакие две функции-члена из разных классов никогда не будут иметь одинаковую подпись.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню