用名字标记 std::function?

Tagging std::function with a name?

我正在开发一个解析器组合器库,我真的希望我的解析器只是一些可调用对象:

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. 一个可调用的解析器class,这让我可以从字符串和std::function实例构建但是一个通用的可调用必须是首先转换为 std::function 然后从那里转换为解析器,C++ 不会进行两次隐式转换

  2. 从 std::function 继承,所以我可以隐式转换函数,但就仅将可调用对象转换为解析器而言,这似乎有很多陷阱。

有人对如何构建这个有任何想法吗?

您不需要标准函数的原始类型定义;你的解析器不仅仅是一个标准函数。

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); };

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

虽然您可以覆盖:

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

通过将 & 放在 parse_resultparse_stream 的命名空间中来使用 typedef 版本,我建议不要这样做; standarizatoin 中已经讨论过限制这种模板参数 ADL。使用裸 parser 类型,放置此类运算符重载的位置很清楚。

此外,某些类型无法在 class 之外重载,例如 &=。使用 struct 你可以在那里完成。

None 个修复

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".

的解析器