代码之家  ›  专栏  ›  技术社区  ›  gct

用名称标记std::函数?

  •  5
  • gct  · 技术社区  · 7 年前

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

    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实例构建 但是 一般的可调用函数必须首先转换为std::函数,然后再从那里转换为解析器,而C++不会进行两次隐式转换

    2. 继承自std::function,因此我可以隐式地转换函数,但这似乎在仅将可调用函数转换为解析器方面存在很多问题。

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

    1 回复  |  直到 7 年前
        1
  •  3
  •   Yakk - Adam Nevraumont    7 年前

    您不需要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 中的CTOR parser

    虽然您可以覆盖:

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

    在typedef版本中 & 在的命名空间中 parse_result parse_stream ,我建议不要这样做;标准中有聊天限制这种模板参数ADL。赤裸的 解析器 类型时,放置此类运算符重载的位置是明确的。

    此外,某些类型不能在类之外重载,例如 &= .带有 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"