Я пытаюсь создать механизм синтаксического анализа для получения данных из библиотеки буфера протокола 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
в соответствующую функцию, чтобы захватить значение, которое мне было бы интересно.
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;
}
std::map<std::string, std::tr1::function<std::string()> > fn_map;
терпит неудачу, потому что ожидает возврата std :: string. Проблема в том, что я не могу контролировать тип возвращаемых объектов класса. Есть ли способ обойти это?
"ip.0.wireless.0.ssid" -> ip(0).wireless(0).ssid();
Твоя проблема:
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]".
Основываясь на описаниях, которые я вам дал, вы должны сами выяснить, почему это работает.
map
, например Config::ip()
и AP::essid()
. Вот почему вам нужно какое-то решение по стиранию типов, например std::function
.
typedef
выглядит неправильно. Я думаю, что вы хотитеtypedef std::string (*FnPtr)(void);