简体   繁体   中英

C++ thread safe switching over string

I have a long list of string conditions. For low latency and code readability, I prefer using switch than "else if". Thanks to comments I edited this question and implement the switching by mapping string to callable :

#include <unordered_map>
#include <iostream>
#include <functional>
#include <thread>

struct Message{
    Message(const std::string field_name, const std::string data) : m_FieldName(field_name), m_Data(data) {}
    const std::string m_FieldName;
    const std::string m_Data;
};

class Handler{
    static const std::unordered_map<std::string, std::function<void(const Message &)>> m_FieldNameCallback;

public:
    bool operator()(Message message){
        bool res = true;
        auto it = m_FieldNameCallback.find(message.m_FieldName);
        if(it != m_FieldNameCallback.cend()){
            it->second(message);
        }
        else{
            std::cout << "Invalid message with field name: " << message.m_FieldName <<"\n";
            res = false;
        }
        return res;
    }
};

const std::unordered_map<std::string, std::function<void(const Message &)>> Handler::m_FieldNameCallback = {
    {"BusinessAction", [](const Message & message){std::cout<< 5 << ": " << message.m_Data << "\n";}},
    {"BusinessClass", [](const Message & message){std::cout<< 6 << ": " << message.m_Data << "\n";}},
    //...
    {"PriceVariation", [](const Message & message){std::cout<< 695 << ": " << message.m_Data << "\n";}},
    {"PairedVolume", [](const Message & message){std::cout<< 698 << ": " << message.m_Data << "\n";}}
};

int main(){
    Handler h1;
    std::thread t1(h1, Message("BusinessAction", "&&&##@@@"));
    std::thread t2(h1, Message("BusinessClass", "&##@"));
    std::thread t3(h1, Message("Business", "&##@"));
    t1.join();
    t2.join();
    t3.join();
}

Is Handler thread safe? using modern C++, are there any more idiomatic method for switching over string?

You definitly made good progress. Either make the map a non-static member of the handler function static. Now I think there is a strange mix. Here is some things I would do slightly different, if you have questions just let me know.

#include <unordered_map>
#include <iostream>
#include <functional>
#include <future>

struct Message
{
    Message(const std::string field_name, const std::string data) : m_FieldName(field_name), m_Data(data) {}

    const std::string m_FieldName;
    const std::string m_Data;
};

//---------------------------------------------------------------------------------------------------------------------

class Handler final // small tweak, no inheritance support (e.g. virtual methods/destructor)
{
    /*static*/ const std::unordered_map<std::string, std::function<void(const Message&)>> m_FieldNameCallback; 
    std::mutex m_output_mtx;

public:
    Handler() :
        m_FieldNameCallback
        {
            {"BusinessAction", [this](const Message& message) {show_message(5,message); }},
            {"BusinessClass", [this](const Message& message) {show_message(6,message); }},
            //...
            {"PriceVariation", [this](const Message& message) {show_message(695,message); }},
            {"PairedVolume", [this](const Message& message) {show_message(698,message); }}
        }
    {
    }

    bool operator()(const Message& message)  // pass by const ref, message should not change and ref will avoid copy
    {
        // I prefer exception based error handling, so an example of that  here.
        // also allows us to use at() instead of find
        try
        {
            // no lock needed since map is readonly
            auto callback = m_FieldNameCallback.at(message.m_FieldName);
            callback(message);
            return true;
        }
        catch (const std::out_of_range&)
        {
            show_invalid_message(message);
            return false;
        }
    }

private:
    void show_invalid_message(const Message& message)
    {
        std::unique_lock<std::mutex> lock(m_output_mtx);
        std::cout << "Invalid message with field name: " << message.m_FieldName << "\n";
    }

    void show_message(unsigned int number, const Message& message)
    {
        std::unique_lock<std::mutex> lock(m_output_mtx);
        std::cout << number << ": " << message.m_Data << "\n";
    }
};

int main()
{
    Handler handler;

    // async is a higher level abstraction then thread
    // it also allows you to get values calculated on other thread wiht future.get()
    // also any uncaught exceptions will be rethrown by get (allowing you to pass errors between threads)

    auto ft1 = std::async(std::launch::async, [&handler] { handler(Message{ "BusinessAction", "&&&##@@@" }); });
    auto ft2 = std::async(std::launch::async, [&handler] { handler(Message{ "BusinessClass", "&##@" }); });
    auto ft3 = std::async(std::launch::async, [&handler] { handler(Message{ "Business", "&##@" }); });

    ft1.get();
    ft2.get();
    ft3.get();

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM