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

4
我正在学习boost::spirit,并尝试将一些文本读取并解析成结构体。
例如,"2: 4.6"在我的TestStruct中被解析为int类型的2和double类型的4.6
#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 ”,然后使用它自动从“mystruct”规则构造“TestStruct”对象。
如果可能的话,我还希望能够摆脱“std :: pair”中的“TestStruct”构造函数。
那么,以下代码是否可以被编译?这将是一个更好的解决方案:
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个回答

3
为了能将值“按顺序”解析到结构体中,您需要将其转换为一个“fusion”元组,具体请参见此处的说明。
在您的情况下,这意味着您需要:
  1. Include the necessary header

    #include <boost/fusion/adapted/struct/adapt_struct.hpp>
    
  2. Use the fusion-adapt struct macro. Best place it right after the declaration of TestStruct:

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

通过这两个更改,您的简化版本编译并产生了所需的结果。不确定现在是否真的更简单 - 但是如果您计划向struct添加进一步的成员,这是一个很好的起点,因为它可能有助于简化未来的事情。

我没有看到其他可以使程序更简单的主要更改。


2

这段代码是可以编译的。事实上,你甚至可以不需要构造函数:使用默认(由编译器生成的)构造函数就可以了。

你唯一需要做的就是将你的结构体适配为一个fusion序列。(作为奖励,这也适用于karma。)
这正是让std::pair最初能够工作的神奇之处。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接