我有一个很长的字符串条件列表。为了低延迟和代码可读性,我更喜欢使用switch而不是& else if"。感谢评论,我编辑了这个问题,并通过将字符串映射到可调用对象来实现切换:
#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();
}
处理程序线程安全吗?使用现代c++,还有什么更习惯的方法来切换字符串吗?
你确实取得了很大的进步。将映射设置为处理程序函数的非静态成员。现在我认为这是一种奇怪的混合。以下是我会做的一些稍微不同的事情,如果你有问题请告诉我。
#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;
}