如何使用boost :: spirit将文本解析为结构?

时间:2012-05-05 00:03:23

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

我正在学习boost::spirit,我正在尝试阅读并解析一些文本到结构中。

例如,"2: 4.6"在我的2下解析为int 4.6和double TestStruct

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/fusion/include/std_pair.hpp>
namespace qi = boost::spirit::qi;

struct TestStruct {
  int myint;
  double mydouble;
  TestStruct() {}
  TestStruct(std::pair<int,double> p) : myint(p.first), mydouble(p.second) {}
};

template <typename Iterator, typename Skipper>
struct MyGrammar : qi::grammar<Iterator, TestStruct(), Skipper> {
  MyGrammar() : MyGrammar::base_type(mystruct) {
    mystruct0 = qi::int_ >> ":" >> qi::double_;
    mystruct = mystruct0;
  }
  qi::rule<Iterator, std::pair<int,double>(), Skipper> mystruct0;
  qi::rule<Iterator, TestStruct(), Skipper> mystruct;
};

int main() {
  typedef boost::spirit::istream_iterator It;
  std::cin.unsetf(std::ios::skipws);
  It it(std::cin), end; // input example: "2: 3.4"                                                                              

  MyGrammar<It, qi::space_type> gr;
  TestStruct ts;
  if (qi::phrase_parse(it, end, gr, qi::space, ts) && it == end)
    std::cout << ts.myint << ", " << ts.mydouble << std::endl;
  return 0;
}

它很好用,但我想知道如何简化这段代码?

例如,我想摆脱mystruct0语法规则,该规则仅用于标记类型std::pair<int,double>,然后用于自动构造TestStruct对象来自mystruct规则。

如果可能的话,我也希望能够从TestStruct摆脱std::pair构造函数。

那么,以下代码可以以某种方式编译吗?这将是一个更好的解决方案:

struct TestStruct {
  int myint;
  double mydouble;
  TestStruct() {}
  TestStruct(int i, double d) : myint(i), mydouble(d) {}
};

template <typename Iterator, typename Skipper>
struct MyGrammar : qi::grammar<Iterator, TestStruct(), Skipper> {
  MyGrammar() : MyGrammar::base_type(mystruct) {
    mystruct = qi::int_ >> ":" >> qi::double_;
  }
  qi::rule<Iterator, TestStruct(), Skipper> mystruct;
};

int main() {
  typedef boost::spirit::istream_iterator It;
  std::cin.unsetf(std::ios::skipws);
  It it(std::cin), end; // input example: "2: 3.4"                                                                              

  MyGrammar<It, qi::space_type> gr;
  TestStruct ts;
  if (qi::phrase_parse(it, end, gr, qi::space, ts) && it == end)
    std::cout << ts.myint << ", " << ts.mydouble << std::endl;
  return 0;
}

不幸的是,编译器说:

boost_1_49_0/include/boost/spirit/home/qi/detail/assign_to.hpp:123: 
error: no matching function for call to ‘TestStruct::TestStruct(const int&)’

2 个答案:

答案 0 :(得分:3)

为了能够“顺序”地将值解析为结构,您需要将其转换为fusion元组,如here所述。

在您的情况下,这意味着您需要

  1. 包含必要的标题

    #include <boost/fusion/adapted/struct/adapt_struct.hpp>
    
  2. 使用fusion-adapt struct宏。声明TestStruct 之后的最佳位置:

    BOOST_FUSION_ADAPT_STRUCT(
            TestStruct,
            (int,myint)
            (double,mydouble)
         )
    
  3. 通过这两项更改,您的简化版本可以编译并生成所需的结果。不确定它现在是否真的更简单 - 但如果你打算在你的结构中添加更多成员,这是一个很好的起点,因为它可能有助于简化未来的事情。

    我没有看到任何其他重大更改可以使程序更简单。

答案 1 :(得分:2)

是的,可以编译代码。实际上,您可以不使用构造函数:默认(编译器生成的)构造函数很好。

您需要做的就是将结构调整为融合序列。 (作为奖励,这也适用于业力。)
这正是让std::pair首先发挥作用的神奇之处。 < / p>

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/struct.hpp>
namespace qi = boost::spirit::qi;

struct TestStruct {
    int myint;
    double mydouble;
};

BOOST_FUSION_ADAPT_STRUCT(TestStruct, (int, myint)(double, mydouble));

template <typename Iterator, typename Skipper>
struct MyGrammar : qi::grammar<Iterator, TestStruct(), Skipper> {
    MyGrammar() : MyGrammar::base_type(mystruct) {
        mystruct = qi::int_ >> ":" >> qi::double_;
    }
    qi::rule<Iterator, TestStruct(), Skipper> mystruct;
};

int main() {
    typedef std::string::const_iterator It;
    const std::string input("2: 3.4");
    It it(input.begin()), end(input.end());

    MyGrammar<It, qi::space_type> gr;
    TestStruct ts;

    if (qi::phrase_parse(it, end, gr, qi::space, ts) && it == end)
        std::cout << ts.myint << ", " << ts.mydouble << std::endl;

    return 0;
}