更疯狂的精神-解析器类型(规则vs int_parser <>)和元编程技术


80

问题在底部以粗体显示,该问题在结尾处也用蒸馏代码片段加以概括。

我正在尝试将我的类型系统(类型系统从类型到字符串,从类型到字符串)统一为一个组件(由Lakos定义)。我正在使用boost::arrayboost::variantboost::mpl,以实现此目的。我想将我的类型的解析器和生成器规则统一在一个变量中。有一个未定义的类型,一个int4(见下文)类型和一个int8类型。变体读取为variant<undefined, int4,int8>

int4特性:

struct rbl_int4_parser_rule_definition
{
  typedef boost::spirit::qi::rule<std::string::iterator, rbl_int4()> rule_type;

  boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

  rule_type rule;

  rbl_int4_parser_rule_definition()
  {
    rule.name("rbl int4 rule");
    rule = parser_int32_t;  
  }
};

template<>
struct rbl_type_parser_rule<rbl_int4>
{
  typedef rbl_int4_parser_rule_definition string_parser;
};

上面的变体以未定义开始,然后初始化规则。我遇到了一个问题,该问题导致了50页的错误,我终于设法找到了它,operator=在分配期间使用了Variant ,并且boost::spirit::qi::int_parser<>不能将a分配给另一个(operator =)。

相比之下,我的未定义类型没有问题:

struct rbl_undefined_parser_rule_definition
{
  typedef boost::spirit::qi::rule<std::string::iterator, void()> rule_type;
  rule_type rule;

  rbl_undefined_parser_rule_definition()
  {
    rule.name("undefined parse rule");
    rule = boost::spirit::qi::eps;
  }
};

template<>
struct rbl_type_parser_rule<rbl_undefined>
{
  typedef rbl_undefined_parser_rule_definition string_parser;
};

问题的提炼:

#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <boost/cstdint.hpp>

typedef boost::spirit::qi::rule<std::string::iterator,void()> r1;
typedef boost::spirit::qi::rule<std::string::iterator,int()> r2;

typedef boost::variant<r1,r2> v;

int main()
{
  /*
  problematic
  boost::spirit::qi::int_parser<int32_t> t2;
  boost::spirit::qi::int_parser<int32_t> t1;


  t1 = t2;
  */

  //unproblematic
  r1 r1_;
  r2 r2_;
  r1_ = r2_;

  v v_;
  // THIS is what I need to do.
  v_ = r2();
}

具体的解析器和规则之间存在语义鸿沟。此刻我的大脑在抽烟,所以我不会考虑实用主义,我的问题是,如何解决这个问题? 我可以想到三种解决问题的方法。

一:静态函数成员:

struct rbl_int4_parser_rule_definition
{
  typedef boost::spirit::qi::rule<std::string::iterator, rbl_int4()> rule_type;

  //boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

  rule_type rule;

  rbl_int4_parser_rule_definition()
  {
    static boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

    rule.name("rbl int4 rule");
    rule = parser_int32_t;  
  }
};

我猜想一种方法可以防止线程安全代码??

二:整数解析器包装在shared_ptr中。我为打字系统而烦恼的原因有两个:1效率,2将关注点集中到组件中。使用指针失败了第一个原因。

三: operator =定义为无操作。变体保证lhs在分配之前默认构造。

编辑: 我认为选项3最有意义(operator =是no-op)。一旦创建了规则容器,它就不会更改,而我只是分配以强制将类型的规则特征插入其偏移量。


1
仅在以下parser_int32_t情况下选项1才是线程不安全的:具有状态获取引用。如果是无状态的或进行了复制,则它是安全的。从语义上讲,我会说是一个副本。
Matthieu M.

这是一个令人困惑的问题,我不能确定解析器对象没有状态。此外,规则机制还具有引用语义和具体语义-即,一个规则可以保存对其他规则的引用,但它们本身也可以是具体解析器(我认为),我不知道这些语义如何应用于具体解析器。
哈桑·赛义德

@MatthieuM:对,除非.alias()使用,否则进行复制。
ildjarn

@ildjarn,但规则不是具体的解析器:D规则的内容是表达式,等同于解析树。
哈桑·赛义德

1
我无法评估#1是否是线程安全的,但是我可以给出一些容易忘记的建议。静态赋值仅由编译器评估一次。想象一下检查一下代码(如果(!evaluated_yet)validate()否则为noop())。第一次在任何地方调用任何rbl_int4_parser_rule_definition的相关成员对象时,它将被构造一次。它几乎绝对等同于使用全局单例。您可以使用该类型的全局单例来解决同一问题吗?(忽略inti。order等),如果这样,则应该是线程安全的。
std''OrgnlDave 2012年

Answers:


11

我不确定我是否能完整了解问题,但这里有一些提示

  • 该行注释对// THIS is what I need to do.我来说编译正常(问题已解决?我猜您实际上是在分配解析器,而不是规则?)

  • static在最新标准(C ++ 11)中,局部函数的初始化已定义为线程安全的。检查您的编译器对C ++ 0x线程的支持。(顺便说一下,如果初始化程序抛出,则初始化语句的传递将尝试再次初始化)。

  • 规则 alias()

    http://boost-spirit.com/home/articles/doc-addendum/faq/#aliases中所述

    您可以创建规则的“逻辑副本”,而不必实际复制原型表达式的值。如常见问题解答所述,这主要是为了允许延迟绑定

  • Nabialek伎俩可能正是你所需要的,基本上它懒洋洋地选择后续解析解析器

    one = id;
    two = id >> ',' >> id;
    
    keyword.add
        ("one", &one)
        ("two", &two)
        ;
    
    start = *(keyword[_a = _1] >> lazy(*_a));
    

    在您的上下文中,我可以看到keyword定义为

    qi::symbols<char, qi::rule<Iterator>*> keyword;
    

    使用语义动作的属性来完成所有工作。或者,

    qi::symbols<char, qi::rule<Iterator, std::variant<std::string,int>() >*> keyword;
    
  • 将规则置于相同的类型下(基本上如上一行所示)

    这是让我感到困惑的部分:您说您想统一类型系统。可能不需要强类型化的解析器(不同的属性签名)。

    typedef boost::variant<std::string,int> unified_type;
    typedef qi::rule<std::string::iterator, unified_type() > unified_rule;
    
    unified_rule rstring = +(qi::char_ - '.');
    unified_rule rint    = qi::int_;
    
    unified_rule combine = rstring | rint;
    
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.