8

我正在使用 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;博士另外我不知道底层类型是什么,通常我真的不想知道,因为我更喜欢一种更通用的解决方案,我可以在这个正则表达式示例之外使用它。甚至 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
            }
          }
    // ...
    }
  }
}

由于大括号嵌套很深,我不喜欢。

可能会像这样在每个主体break的末尾滥用带有 s 的循环结构:if

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 个实际上有效,所以我可能会限制对它们的评论)。

另外,有没有人有更好的建议?请说明您认为它们更好的原因。

4

3 回答 3

6

通常建议避免嵌套ifs - 它们使代码更难阅读。如果有嵌套的 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
  }
}

BEGS循环:

const char *types[] = {"type1", "type2", "typeN", 0};
for(const char **cur = types; *cur; cur++){
    found = what[*cur];
    if (found.matched){
         //initiate robot uprising
         break;
    }
}

您所有其他示例(IMO)都是一种糟糕的编码风格。我更喜欢保持循环和 ifs 简短。如果它不适合 20 行代码,那么最好做一些非常复杂的事情(这不是你的情况)。如果它没有做任何复杂的事情,则需要对其进行重组。

于 2013-05-24T15:28:11.413 回答
3

你可以做这样的事情(注意这个代码没有针对编译器进行测试)

// create a map between match types and actions
std::map<std::string, std::function<match_t>> actions;
actions["type1"] = [&](match_t type) {...};

// iterate through the actions map and check each match type
for(auto kvp : actions)
{
   match_t type = what[kvp.first];
   if(type.matched)
   {
      // do stuff with this result
      kvp.second(type);
   }
}
于 2013-05-24T05:30:00.690 回答
0

您可以编写一个match_t带有重载的包装operator bool

struct bmatch
{
    matches_t::const_reference ref;
    bmatch(matches_t::const_reference r)
    :ref(r)
    {}

    operator bool() const
    {
        return ref.matched;
    }
};

进而:

if (bmatch type1 = what["type1"])
{ //Use type2.ref
}
else if (bmatch type2 = what["type2"])
{ //Use type2.ref
}

对于加分,您还可以重载operator->

    matches_t::const_reference operator->() const
    {
        return ref;
    }
于 2013-05-24T15:09:36.923 回答