使用名称标记std :: function?

时间:2018-05-04 14:25:34

标签: c++ parsing c++11 std-function

我正在研究一个解析器组合库,我真的很喜欢我的解析器只是一个可调用的对象:

typedef std::function<parse_result(parse_stream)> parser;

这使得解析器组合器很好,例如:

parser operator &(parser a, parser b) { return both(a,b); }

但是我想要两个功能:

1)我希望自动将字符串文字提升为解析器,以便您可以执行以下操作:

parser option = "<" & regexp("[^+>]+");

2)我希望解析器有一个名称,我可以使用它来进行错误格式化。在上面的“both”解析器的情况下,我可以打印我预期的a.name()和b.name()例如。

到目前为止我尝试过的两个选项是

  1. 一个可调用的解析器类,这让我可以从字符串和std :: function实例构建,但一般的callable必须首先转换为std :: function并从那里转换为解析器,C ++不会进行两次隐式转换

  2. 从std :: function继承,所以我可以隐式转换函数,但是这似乎在将callables转换为解析器方面有很多问题。

  3. 有没有人对如何构建这个有什么想法?

1 个答案:

答案 0 :(得分:3)

你不需要std函数的原始typedef;你的解析器不仅仅是一个std函数。

struct parser: std::function<parse_result(parse_stream)>{
  using base = std::function<parse_result(parse_stream)>;
  using base::base;
};

这应该允许

parser p = []( parse_stream str ) { return parse_result(7); };

因为我们使用继承构造函数来展示std::function中的原始parser ctors。

虽然您可以覆盖:

parser operator&(parser a, parser b) { return both(a,b); }
通过将&放在parse_resultparse_stream的命名空间中,使用typedef版本

,我建议反对它;在standarizatoin中有一个聊天来限制这种模板参数ADL。使用裸parser类型,可以清楚地显示此类运算符重载的位置。

此外,某些类型不能在类之外重载,例如&=。使用struct,您可以在那里完成。

这些都没有修复

parser option = "<" & regexp("[^+>]+");

因为这里的问题是右手边不知道左手边做了什么(除非regexp是一个函数撤回解析器)。

首先这样做:

struct parser: std::function<parse_result(parse_stream)>{
  using base = std::function<parse_result(parse_stream)>;
  parser( char const* str ):
    base( [str=std::string(str)](parse_stream stream)->parse_result { /* do work */ } )
  {}
  parser( char c ):
    base( [c](parse_stream str)->parse_result { /* do work */ } )
  {}
  using base::base;
};

然后你可以添加

namespace parsing {
  // parser definition goes here
  inline namespace literals {
    inline parser operator""_p( char const* str ) { return str; }
  }
}

using namespace parsing::literals表示"hello"_p是一个尝试解析字符串"hello"的解析器。