#c
Вопрос:
В своем приложении я вызываю различные функции на основе ключа сообщения, полученного по сети. Но моя реализация не настолько эффективна, поскольку она состоит из исчерпывающего поиска ключа в std::vector<std::string>
. Я подумываю об использовании std::unordered_map<std::string, <fun>>
, но не знаю, как сопоставить его с функцией, а также вызвать эту функцию, передав необходимые аргументы. Это моя текущая реализация:
#include <iostream>
#include <string>
#include <vector>
int main() {
std::vector<std::string> msg_key_list;
msg_key_list.emplace_back("key1");
msg_key_list.emplace_back("key2");
msg_key_list.emplace_back("key3");
msg_key_list.emplace_back("key4");
msg_key_list.emplace_back("key5");
char buff[2048];
ssize_t data_len;
while (true) {
// receive data from network
std::string msg(buff, data_len);
std::string::size_type pos = msg.find_first_of(",");
std::string key = msg.substr(0, pos - 1);
if (key == msg_key_list.at(0)) {
// validate the message received
bool valid;
if (!valid) {
std::cerr << "Invalid data: ";
std::cerr << __FILE__ << ", " << __PRETTY_FUNCTION__ << ", " << __LINE__ << "n";
continue;
}
std::string val(buff pos, msg.size() - pos);
// fun1(val);
} else if (key == msg_key_list.at(1)) {
// validate the message received
bool valid;
if (!valid) {
std::cerr << "Invalid data: ";
std::cerr << __FILE__ << ", " << __PRETTY_FUNCTION__ << ", " << __LINE__ << "n";
continue;
}
std::string val(buff pos, msg.size() - pos);
// fun2(val);
} else if (key == msg_key_list.at(2)) {
// validate the message received
bool valid;
if (!valid) {
std::cerr << "Invalid data: ";
std::cerr << __FILE__ << ", " << __PRETTY_FUNCTION__ << ", " << __LINE__ << "n";
continue;
}
std::string val(buff pos, msg.size() - pos);
// fun3(val);
} else if (key == msg_key_list.at(3)) {
// validate the message received
bool valid;
if (!valid) {
std::cerr << "Invalid data: ";
std::cerr << __FILE__ << ", " << __PRETTY_FUNCTION__ << ", " << __LINE__ << "n";
continue;
}
std::string val(buff pos, msg.size() - pos);
// fun4(val);
} else if (key == msg_key_list.at(4)) {
// validate the message received
bool valid;
if (!valid) {
std::cerr << "Invalid data: ";
std::cerr << __FILE__ << ", " << __PRETTY_FUNCTION__ << ", " << __LINE__ << "n";
continue;
}
std::string val(buff pos, msg.size() - pos);
// fun5(val);
}
}
}
Комментарии:
1. Я бы использовал других потребителей… Таким образом, в зависимости от ключа вы создаете сообщение для определенной очереди, а затем потребители ищут сообщения в назначенных им очередях…
2. <O/T> <O/T>
bool valid; if(!valid)...
— это неопределенное поведение,valid
которое никогда не инициализируется3.
std::map<std::string, std::function<void()>>
?4. или
std::map<std::string, std::unique_ptr<SomeInterface>>
вам нужно использовать классы , хотя5. @Harry вы можете ввести-стереть данные в одну подпись, возможно
std::any
, либо это делается непосредственно при обратном вызове, либо вы переносите их при регистрации. и использоватьstd::map<std::string, std::function<void(std::any)>>
Ответ №1:
Я привел пример того, как я мог бы подойти к этому (если бы все обработчики использовали одну и ту же сигнатуру функции). Я также избегаю копирования строк в обработчике сообщений, потому что это замедлило бы выполнение ненужных действий.
#include <iostream>
#include <stdexcept>
#include <vector>
#include <map>
#include <string>
//-----------------------------------------------------------------------------
// function signature of your message handlers
using handler_t = void(*)(const std::string_viewamp;);
//-----------------------------------------------------------------------------
// handler functions
void handler_1(const std::string_viewamp; msg)
{
// validate data here
std::cout << "handler 1 : " << msg << std::endl;
};
void handler_2(const std::string_viewamp; msg)
{
std::cout << "handler 2 : " << msg << std::endl;
};
//-----------------------------------------------------------------------------
// handler selection
void handle_message(const std::stringamp; buffer)
{
static const std::map<std::string_view, handler_t> handler_map
{
{"key1",handler_1},
{"key2",handler_2}
};
try
{
// find location of first ',' (if any)
// also ensure the ',' is not too far back for splitting
auto index = buffer.find_first_of(',');
if ((index != std::string::npos) amp;amp; (index < (buffer.length() - 1)))
{
auto pos = buffer.begin() index;
// No need to copy data to seperate strings
// string_views will do.
std::string_view key{ buffer.begin(), pos };
std::string_view data{ pos 1, buffer.end() };
// lookup handler, not if no handler is found
// a std::out_of_range exception will be thrown
auto handler = handler_map.at(key);
// call the handler
handler(data);
}
}
catch (const std::out_of_rangeamp;)
{
// todo decide what you want to do with incorrect messages here
}
}
//-----------------------------------------------------------------------------
// and handle a test message in main
int main()
{
const std::string data{ "key1,Hello" };
handle_message(data);
}