C++ 如何有效地识别字符串命令?

C++ 如何有效地识别字符串命令?,c++,string,performance,hash,C++,String,Performance,Hash,给定一系列命令和必须为每个命令运行的非常独特的代码: if(cmd == "cmd.setBoosterRocket") ... else if(cmd == "cmd.windSales") ... else if(cmd == "cmd.selfDustruct") ... else if(cmd == "cmd.unleashHounds") ... 如何对其进行优化?被放入s

给定一系列命令和必须为每个命令运行的非常独特的代码:

if(cmd == "cmd.setBoosterRocket")
    ...
else if(cmd == "cmd.windSales")
    ...
else if(cmd == "cmd.selfDustruct")
    ...
else if(cmd == "cmd.unleashHounds")
    ...
如何对其进行优化?被放入switch语句中,即

我考虑制作一个散列向量:

std::hash<std::string> hasher;
for(std::string command : m_commandList)
    m_mashes.push_back(hasher(command)
std::散列哈希器;
for(std::string命令:m_commandList)
m_mashes.push_back(hasher(命令)

但是向量不能作为switch case语句的一部分进行访问,因为它不是constexpr。字符串命令列表在编译时是已知的,我可以潜在地对哈希值进行硬编码……但这似乎不是一个好主意。

一种可能的方法是标记化:制作一个
枚举
类型和一个字典u利用开关(以一种比硬编码哈希更为程序员和编译器友好的方式),并且只具有对数复杂性

enum Command {SET_BOOSTER_ROCKET, WINDSALES, ETC};
const std::map<std::string, Command> commands = {
  {"cmd.setBoosterRocket", SET_BOOSTER_ROCKET},
  {"cmd.windsales", WINDSALES},
  {"othercommands", ETC},
};

如果有很多事情要开始,那么像这样标记命令可能会有点乏味,但这样做在可伸缩性和可读性之间有一个很好的平衡。

好吧,为您的用例编写一个简单的哈希函数:

static constexpr hasher = [](auto&& s){ return char(s.size() < xyz ? 0 : expr); };
是的,您不能使用(mis-?)故障排除功能。如果您确实希望其中一个故障排除功能,请使用
goto

现在您可以编写switch语句:

switch (hasher(cmd)) {
CASE("cmd.setBoosterRocket")
    ...
CASE("cmd.windSales")
    ...
CASE("cmd.selfDustruct")
    ...
CASE("cmd.unleashHounds")
    ...
...
}
大多数编译器都会提醒您注意重复的case语句,所以在获得廉价、唯一和紧密绑定的哈希之前,请不要乱动它


不要忘记
#undef CASE
来限制宏的作用域。

另一种方法是创建单独的函数来处理每个案例,然后创建一个映射作为“dispatcher”:

const static std::无序映射调度程序{
{“cmd.setBoosterRocket”、&setBoosterRocketHandler},
{“cmd.windSales”、&windSalesHandler},
{“cmd.selfDustruct”、&selfDustructHandler},
{“cmd.unreleasehounds”&sunleashHoundsHandler},
};
auto const it=dispatcher.find(cmd);
if(it==dispatcher.end()){/*处理默认情况*/}
else{(*it)(;}

使用C++20,可以使用
constepr
定义
索引
-lambda来提高效率

static constexpr auto commands = { "cmd.setBoosterRocket", "cmd.windSales", ... };

constexpr auto index = [&](std::string s) -> size_t { /*...*/ };

switch(quick_index(cmd))
{
    case index("cmd.setBoosterRocket"):
        // ...
        break;

    default:
        // ...
        break;
}
对于
quick_index
,它不必是
constexpr
,例如,您可以使用
unordered_map
查找O(1)中字符串的索引

下面是一个使用二进制搜索的完整示例(即
constexpr
,因此它既可以用于
索引
,也可以用于
快速索引

#包括
#包括
#包括
#包括
使用名称空间std;
模板
constexpr ForwardIt binary_find(ForwardIt first,ForwardIt last,const T&value,comp={})
{
first=std::下限(第一个、最后一个、值、comp);
返回first!=last&&!comp(值,*first)?first:last;
}
int main()
{
constexpr auto-sorted=[](数组a){sort(a.begin(),a.end());返回a;};
static constexpr auto commands=排序({“bar”、“foo”、“foobar”});
constexpr自动索引=[&](字符串视图)
{return binary_find(commands.begin(),commands.end(),s)-commands.begin();};
string_view cmd=“bar”;
开关(索引(cmd))
{
案例索引(“bar”):

无法将所有可能性都转换为将
std::string
映射到执行所需操作的。如果您不关心错误检查,则会将整个shebang简化为
mymap[cmd]()
。但是你真的应该关心错误检查。它不会添加太多额外的代码,并且使调试更容易。你可以用成对的排序数组实现编译时常量映射的非常好的近似值。然后你可以
std::binary_search
在该数组中搜索命令。@FrançoisAndrieux不幸的是,d不要做你想做的事情。这对C++来说太简单了!你想要的。我们有多少个字符串?“你是对的。每次我都能得到它,这不是我第一次犯那个错误。在宏中,<代码>断裂;< /Code >会在第一个代码>案例中引起麻烦:
。为什么?开关,不管它是可到达的还是死的。看起来你是对的。我不知道你可以启动一个<代码>开关>代码>除了一个案例。如果你想看到一些疯狂的东西,那么你可以用C和C++中的开关来做,看看哇。那太棒了。
switch (hasher(cmd)) {
CASE("cmd.setBoosterRocket")
    ...
CASE("cmd.windSales")
    ...
CASE("cmd.selfDustruct")
    ...
CASE("cmd.unleashHounds")
    ...
...
}
const static std::unordered_map<std::string, void(*)()> dispatcher{ 
   { "cmd.setBoosterRocket",  &setBoosterRocketHandler },
   { "cmd.windSales",  &windSalesHandler },
   { "cmd.selfDustruct",  &selfDustructHandler },
   { "cmd.unleashHounds",  &sunleashHoundsHandler },
};

auto const it = dispatcher.find(cmd);

if (it == dispatcher.end()) { /* handle default case */ }
else { (*it)(); }
static constexpr auto commands = { "cmd.setBoosterRocket", "cmd.windSales", ... };

constexpr auto index = [&](std::string s) -> size_t { /*...*/ };

switch(quick_index(cmd))
{
    case index("cmd.setBoosterRocket"):
        // ...
        break;

    default:
        // ...
        break;
}
#include <algorithm>
#include <array>
#include <iostream>
#include <string_view>

using namespace std;

template<class ForwardIt, class T, class Compare = std::less<>>
constexpr ForwardIt binary_find(ForwardIt first, ForwardIt last, const T& value, Compare comp = {})
{
   first = std::lower_bound(first, last, value, comp);
   return first != last && !comp(value, *first) ? first : last;
}

int main()
{
    constexpr auto sorted = [](array<string_view, 3> a) {sort(a.begin(), a.end()); return a;};
    
    static constexpr auto commands = sorted({ "bar", "foo", "foobar" });
    
    constexpr auto index = [&](string_view s)
        { return binary_find(commands.begin(), commands.end(), s)-commands.begin(); };

    string_view cmd = "bar";

    switch(index(cmd))
    {
        case index("bar"):
            cout << "bartender" << endl;
            break;

        case index("foo"):
            cout << "fighter" << endl;
            break;

        case index("foobar"):
            cout << "fighter bartender" << endl;
            break;
            
        default:
            cout << "moo" << endl;
            break;
    }
}