【发布时间】:2013-05-24 05:09:51
【问题描述】:
我正在使用 boost 的正则表达式库,我发现确定是否找到命名匹配然后使用该信息有点烦人。要检测命名匹配,我想这样做:
typedef boost::match_result<string::const_iterator> matches_t;
typedef matches_t::const_reference match_t;
boost::regex re("(?:(?<type1>aaaa)|(?<type2>bbbb)" /*...*/ "|(?<typeN>abcdefg)");
string str(SOME_STRING);
matches_t what;
boost::match_flag_type flags = boost::match_default;
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
if((match_t type1 = what["type1"]).matched)
{
// do stuff with type1
}
else if((match_t type2 = what["type2"]).matched)
{
// do stuff with type2
}
// ...
else if((match_t typeN = what["typeN"]).matched)
{
// do stuff with typeN
}
}
如果那会奏效,那就太好了。范围将被限制在 if 的主体中,内存可以被有效地使用并且看起来相当干净。遗憾的是,它不起作用,因为您无法在列表中定义变量。 :(
这有可能:
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
match_t found = what["type1"];
if(found.matched)
{
// do stuff with type1
}
else if((found = what["type2"]).matched)
{
// do stuff with type2
}
// ...
else if((found = what["typeN"]).matched)
{
// do stuff with typeN
}
}
但是 match_t 是一个 const 引用,所以它是不可赋值的。 (tl;dr 另外我不知道底层类型是什么,通常我也不想知道,因为我更喜欢一种更通用的解决方案,我可以在这个正则表达式示例之外使用它。甚至std::move() 用于围绕什么 [...] 它变得更加冗长,并且文档没有说它对 sub_match 使用移动语义。由于第一个给出的原因,所有这些当然都是没有意义的本段的句子。)
另一种选择是这样做:
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
match_t type1 = what["type1"];
if(type1.matched)
{
// do stuff with type1
}
else {
match_t type2 = what["type2"];
if(type2.matched)
{
// do stuff with type2
}
// ...
else {
match_t typeN = what["typeN"];
if((match_t typeN = what["typeN"]).matched)
{
// do stuff with typeN
}
}
// ...
}
}
}
由于大括号嵌套太深,我不喜欢。
可能会在每个 if 正文的末尾滥用带有 breaks 的循环结构,如下所示:
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
do{
{
match_t type1 = what["type1"];
if(type1.matched)
{
// do stuff with type1
break;
}
}
{
match_t type2 = what["type2"];
if(type2.matched)
{
// do stuff with type2
break;
}
}
// ...
{
match_t typeN = what["typeN"];
if(typeN.matched)
{
// do stuff with typeN
break;
}
}
} while(0);
}
哪个更好,但仍然不是很好。使用宏,可以隐藏大部分噪音。喜欢:
#define IF(declare, cond) do{{declare;if(cond){
#define ELSE_IF(declare, cond) break;}}{declare; if(cond){
#define ELSE break;}}{{
#define END_IF break;}}}while(0);
if(regex_search(str.cbegin(), str.cend(), what, re, flags))
{
IF(match_t type1 = what["type1"], type1.matched)
{
// do stuff with type1
}
ELSE_IF(match_t type2 = what["type2"], type2.matched)
{
// do stuff with type2
}
// ...
ELSE_IF(match_t typeN = what["typeN"], typeN.matched)
{
// do stuff with typeN
}
END_IF
}
大括号实际上是由宏所隐含的,但通过重述它们可以使阅读更清晰。
我能想到的另一个选择是进入 boost::sub_match 类并添加一个转换函数将该类型转换为返回值将是 matched 成员的布尔值。然后我可以在 if 表达式中声明一个 match_t 变量,它会被 if 自动转换为布尔值。我不确定我是否还在那里,它不是通用的。
在风格上,我建议的那些是好是坏(只有最后 3 个实际上有效,所以我可能会将 cmets 限制在它们身上)。
另外,有没有人有更好的建议?请说明您认为它们更好的原因。
【问题讨论】:
-
请注意,
if(foo = bar)与 C++ 中的if(foo == bar)不同,代码可能不会执行您想要的操作。此外,我强烈建议不要像上一个示例那样使用宏来“删除垃圾”。它不会删除“垃圾”,但会使代码难以理解。 -
@Damon:是的,我知道 = 与 == 不同。该代码完全符合我的要求,以限制范围、减少噪音和重用堆栈空间(如果编译器对其进行了优化)。我有点同意你的观点:宏,但只要你知道宏的用途,它确实可以减少多余的噪音,这可以让事情变得更清晰。但是YMMV。所以我认为你更喜欢带有
do { } while(0);没有宏的第二个? -
不确定,我可能会编写第一个版本(在外部
if范围内声明变量,因此它可以编译),因为在我看来,这是最不容易混淆的版本。但归根结底,这取决于个人品味。而且,我会在if内的分配周围加上双括号,以明确表明这些不是偶然的(启用适当的警告,这也是 GCC 建议做的)。 -
@Damon:第一个?还是5个中的第3个?我问这个是因为第一个不起作用,并且不可能预先声明变量而不给它们赋值,因为类型是 const 引用。
-
在
if(regex_search(...))包围的块中声明(并分配,因为它们是引用)三个作为第一件事的障碍是什么?这应该可以正常工作,不是吗?在最坏的情况下,它会徒劳地初始化 2 个变量,但平均而言它会很好,而且它是可读的代码:)
标签: c++ coding-style