为C++的switch添加case的字符串匹配

Add case string matching to C++ switch

C++标准中的switch是不能够实现字符串的case匹配的,但是往往我们也有这个需求,来实现一下。

我们需要实现的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
switch("123"){
case "123":{
// ...
break;
}
case "456":{
// ...
break;
}
// ...
default:{
// ...
break;
}
}

直接匹配字符串是不行的,C++中case只可以匹配a constant expression of the same type as the type of condition after conversions and integral promotions,所以在这里我需要把字符串转换为一个字面值整数从而进行case匹配。

将字符串转换为数字可以使用HASH(Wikipedia - hash function)方式来计算,在这里我使用的Hash算法是Chromium中计算string字符串hash值的算法(Chromium - string_piece.h)。

Hash计算部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Hashing ---------------------------------------------------------------------

// We provide appropriate hash functions so StringPiece and StringPiece16 can
// be used as keys in hash sets and maps.

// This hash function is copied from base/strings/string16.h. We don't use the
// ones already defined for string and string16 directly because it would
// require the string constructors to be called, which we don't want.
#define HASH_STRING_PIECE(StringPieceType, string_piece) \
std::size_t result = 0; \
for (StringPieceType::const_iterator i = string_piece.begin(); \
i != string_piece.end(); ++i) \
result = (result * 131) + *i; \
return result;

struct StringPieceHash {
std::size_t operator()(const StringPiece& sp) const {
HASH_STRING_PIECE(StringPiece, sp);
}
};
struct StringPiece16Hash {
std::size_t operator()(const StringPiece16& sp16) const {
HASH_STRING_PIECE(StringPiece16, sp16);
}
};

我们需要用到的也就是HASH_STRING_PIECE这个宏定义代码。

在C++11特性中,我们可以使用constexpr(具体内容参照文章末尾链接)来定义一个constexpr函数,switch的case标签处调用这个constexpr函数。

这里我写了一个递归来实现上面的hash计算算法:

1
2
3
constexpr size_t HASH_STRING_PIECE(const char *string_piece,size_t hashNum=0){
return *string_piece?HASH_STRING_PIECE(string_piece+1,(hashNum*131)+*string_piece):hashNum;
}

此时在switch中就可以这么写:

1
2
3
4
5
6
7
8
9
10
switch(HASH_STRING_PIECE("123")){
case HASH_STRING_PIECE("123"):{
cout<<"123"<<endl;
break;
}
case HASH_STRING_PIECE("456"):{
cout<<"456"<<endl;
break;
}
}

为了更方便我们可以重载一个""操作符来方便我们使用,而不是每次都直接调用HASH_STRING_PIECE这个函数,也是为了更符合case的使用规范。

1
2
3
constexpr size_t operator "" _HASH(const char *string_pice,size_t){
return HASH_STRING_PIECE(string_pice);
}

然后就可以这么用了:

1
2
3
4
5
6
7
8
9
10
switch(HASH_STRING_PIECE("123")){
case "123"_HASH:{
cout<<"123"<<endl;
break;
}
case "456"_HASH:{
cout<<"456"<<endl;
break;
}
}

直接使用标准库string调用的话可以再写这么一个函数:

1
2
3
4
size_t CALC_STRING_HASH(const string& str){
// 获取string对象得字符串值并传递给HAHS_STRING_PIECE计算,获取得返回值为该字符串HASH值
return HASH_STRING_PIECE(str.c_str());
}

然后直接传入string对象的话可以调用CALC_STRING_HASH函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int main(int argc,char* argv[])
{
cout<<"switch list in \"123\"/\"456\"/\"789\",Please input:";
string input;
cin>>input;
switch(CALC_STRING_HASH(input)){
case "123"_HASH:{
cout<<"the case is 123"<<endl;
break;
}
case "456"_HASH:{
cout<<"the case is 456"<<endl;
break;
}
case "789"_HASH:{
cout<<"the case is 789"<<endl;
break;
}
default:
cout<<"Not found"<<endl;
break;

}
return 0;
}

cppreference - constexpr
C++11FAQ - constexpr

全文完,若有不足之处请评论指正。

微信扫描二维码,关注我的公众号。

本文标题:为C++的switch添加case的字符串匹配
文章作者:查利鹏
发布时间:2016/09/24 22:43
本文字数:702 字
原始链接:https://imzlp.com/posts/1494/
许可协议: CC BY-NC-SA 4.0
文章禁止全文转载,摘要转发请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!