boost :: spirit :: qi具有相同简单的自适应struct属性的规则会产生编译错误

时间:2013-08-10 23:49:44

标签: c++ boost boost-spirit boost-spirit-qi

我想重用一个规则,并在其前面添加一个关键字。所以我的规则看起来像这样:

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>

#include <string>

namespace qi = boost::spirit::qi;

struct A {
  int index;
  std::string name;
};
BOOST_FUSION_ADAPT_STRUCT(::A, (int, index)(std::string, name))

struct B {
  A data;
};
BOOST_FUSION_ADAPT_STRUCT(::B, (A, data))

int main() {
  typedef std::string::const_iterator iterator_type;

  qi::rule<iterator_type, A()> a_rule = qi::int_ > qi::lit(",") > *(qi::char_);
  qi::rule<iterator_type, B()> b_rule = qi::lit("(") > a_rule > qi::lit(")");
  qi::rule<iterator_type, B()> bad_rule = qi::lit("keyword") > b_rule;

  return 0;
}

这不编译,因为编译器想要从A创建B(指的是bad_rule的定义):

C:/tc/gcc_x64_4.8.1_win32_seh_rev1/mingw64/my/src/boost_1_54_0/boost/spirit/home/qi/detail/assign_to.hpp:152:18: error: no matching function for call to 'A::A(const B&)'
             attr = static_cast<Attribute>(val);
                  ^

但是,将struct B更改为不那么简单,可以使其正常工作:

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>

#include <string>

namespace qi = boost::spirit::qi;

struct A {
  int index;
  std::string name;
};
BOOST_FUSION_ADAPT_STRUCT(::A, (int, index)(std::string, name))

struct B {
  A data;
  int dummy;
};
BOOST_FUSION_ADAPT_STRUCT(::B, (A, data)(int, dummy))

int main() {
  typedef std::string::const_iterator iterator_type;

  qi::rule<iterator_type, A()> a_rule = qi::int_ > qi::lit(",") > *(qi::char_);
  qi::rule<iterator_type, B()> b_rule = qi::lit("(") > a_rule > qi::lit(")") > qi::attr(0);
  qi::rule<iterator_type, B()> bad_rule = qi::lit("keyword") > b_rule;

  return 0;
}

任何想法,如何在没有这种解决方法的情况下相处以及这里发生了什么?

2 个答案:

答案 0 :(得分:1)

这也是适应单个元素序列的问题。不幸的是,我不知道这种情况的解决方法。在这种情况下,您可以删除B的改编并专门化transform_attribute。有一个简单的示例here几乎完全符合您的要求。

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>

#include <string>

namespace qi = boost::spirit::qi;

struct A {
  int index;
  std::string name;
};
BOOST_FUSION_ADAPT_STRUCT(::A, (int, index)(std::string, name))

struct B {
  A data;
};

namespace boost{ namespace spirit{ namespace traits
{
    template <>
    struct transform_attribute<B,A,qi::domain>
    {
        typedef A& type;

        static type pre(B& val){ return val.data;}
        static void post(B&, A const&){}  
        static void fail(B&){} 
    };
}}}

int main() {
  typedef std::string::const_iterator iterator_type;

  qi::rule<iterator_type, A()> a_rule = qi::int_ >> qi::lit(",") >> *(qi::char_-qi::lit(")"));
  qi::rule<iterator_type, B()> b_rule = qi::lit("(") >> a_rule >> qi::lit(")");
  qi::rule<iterator_type, B()> bad_rule = qi::lit("keyword") >> b_rule;

  std::string test="keyword(1,one)";
  iterator_type iter=test.begin(), end=test.end();

  B b;

  bool result = qi::parse(iter,end,bad_rule,b);
  if(result && iter==end)
  {
    std::cout << "Success:" << std::endl;
    std::cout << "b.data.index=" << b.data.index << ", b.data.name=" << b.data.name << std::endl;
  }
  else
  {
    std::cout << "Failed. Unparsed: " << std::string(iter,end) << std::endl;
  }

  return 0;
}

答案 1 :(得分:1)

一个更简单的解决方法是放弃自动属性传播,而是将显式赋值写为一个简单的语义操作:

qi::rule<iterator_type, B()> bad_rule = "keyword" > b_rule[ qi::_val=qi::_1 ];

我同意这仍然是一个痛苦的解决方法。 Spirit V3承诺在这里简化机器,并使边缘的属性兼容性更加平滑。