вызовите соответствующую функцию с аргументами, основанными на полученном ключе

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