我晕啊 spirit

被 spirit 这种神奇的东西搞怕了 -,-b 虽然理论上说的很好,看别人给的例子也觉得不错,但是实在是太悲摧了。

第一点是,spirit 用了 phoenix,但是用的 ms 是低版本的 spirit(估计 2.x),然后 boost 1.49 现在自带的 phoenix 是 3.0。OK,那么 spirit 文档上说其实我们 2.5 版本(boost 1.47 开始)就支持使用 phoenix V3 了。不过不知道为啥,我试了一个例子就发现 v3 过不去的地方用 spirit 自带的就万事大吉(提交到 trac)。但是 phoenix 老版本似乎没有定义 argN 或者 _N 什么的(我只是好奇为什么不提供)。

//#define BOOST_SPIRIT_USE_PHOENIX_V3 1
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>

namespace spirit = boost::spirit ;
namespace qi = boost::spirit::qi ;
namespace ascii = boost::spirit::ascii ;
namespace phoenix = boost::phoenix ;

using qi::int_ ;
using qi::_1 ;
using phoenix::push_back ;
using phoenix::ref ;
using phoenix::if_ ;

int
main (int argc, char* argv[]) {
  std::string line ;
  std::getline (std::cin, line) ;

  std::vector<int> v ;
  bool r = spirit::qi::phrase_parse
    (line.begin (), line.end (),
     int_ [if_(_1 % 2 == 0)[push_back (ref(v), _1)]] % ',',
     ascii::space) ;

  if (r) {
    std::cout << "vector: " ;
    std::copy (v.begin(), v.end(),
               std::ostream_iterator<int> (std::cout, ", ")) ;
    std::cout << std::endl ;
  }
  return 0 ;
}

将第一行注释去掉看看你能看懂是啥错误么?不去掉是可以正常编译的。我一度以为是不是我 include 的头文件错误,就把 phoenix 相关的换成 boost/phoenix 里面的对应版本。问题依旧 -,-

第二点是,即便使用了号称出错信息更友好 clang++,出错还是比较让人一头雾水,非常简单的程序都能导致飙出来 N 行出错信息(我 iTerm 的 scroll 都找不到头的哈)。这导致我每次必须在 make 后面 2>&1 然后放到 less 里面去看出错信息。

第三点是,为什么下面这么简单的程序结果都是错的?

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>

namespace spirit = boost::spirit ;
namespace qi = boost::spirit::qi ;
namespace ascii = boost::spirit::ascii ;
namespace phoenix = boost::phoenix ;

using qi::double_ ;
using qi::_1 ;
using phoenix::push_back ;
using phoenix::ref ;

int
main (int argc, char* argv[]) {
  spirit::istream_iterator iter (std::cin), end ;
  std::vector<double> v ;
  bool r = spirit::qi::phrase_parse
    (iter, end,
     double_ [push_back (ref(v), _1)] % ',',
     ascii::space) ;

  if (r) {
    std::cout << "vector: " ;
    std::copy (v.begin(), v.end(),
               std::ostream_iterator<double> (std::cout, ", ")) ;
    std::cout << std::endl ;
  }
  return 0 ;
}

从标准输入给的数据是“1,2,3,4” 输出为“vector: 1, 2, 3, 44,”,发指的是输入为“1,2,3,4,”结果就对了。就算不用简写,而是写 double_ >> *(‘,’ >> double_) 结果仍然有问题。最后发现 spirit 提供的 istream_iterator 可能有问题(见 trac),

int
main (int argc, char* argv[]) {
  std::string line ;
  std::getline (std::cin, line) ;

  std::vector<double> v ;
  bool r = spirit::qi::phrase_parse
    (line.begin (), line.end (),
     (double_ [push_back (ref(v), _1)] % ','),
     ascii::space) ;

  if (r) {
    std::cout << "vector: " ;
    std::copy (v.begin(), v.end(),
               std::ostream_iterator<double> (std::cout, ", ")) ;
    std::cout << std::endl ;
  }
  return 0 ;
}

这个结果就正确了,sigh…. 实在是让人怀疑很多 spirit 的例子都是精心挑选过,像弄得复杂点,几乎不能想出什么办法进行调试。

第四,有些问题看起来这样那样,但是就是一些细小的差别会导致编译失败,记得上面编译通过的例子,如果我们稍微修改一下:

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>

namespace spirit = boost::spirit ;
namespace qi = boost::spirit::qi ;
namespace ascii = boost::spirit::ascii ;
namespace phoenix = boost::phoenix ;

using qi::lexeme ;
using qi::int_ ;
using ascii::char_ ;
using namespace qi::labels ;

typedef boost::variant<int, std::string> value_type ;

template <class Iter>
struct kv_grammar
  : qi::grammar<Iter, value_type (), ascii::space_type> {

  kv_grammar () : kv_grammar::base_type (start) {

    quoted_string %=
         lexeme ['"' >> +(char_ - '"') >> '"']
      ;

    start %= int_ | quoted_string ;
  }

  qi::rule<Iter, value_type (), ascii::space_type> start ;
  qi::rule<Iter, std::string (), ascii::space_type> quoted_string ;
} ;

int
main (int, char**) {
  std::string line ;
  value_type v ;
  kv_grammar<std::string::const_iterator> p ;
  std::getline (std::cin, line) ;
  //std::string::const_iterator iter = line.begin (), end = line.end () ;

  bool r = qi::phrase_parse
    (line.begin (), line.end (), p, ascii::space, v) ;

  if (r)
    std::cout << v << std::endl ;

  return 0 ;
}

你必须去掉注释的那行,然后把 iter 和 end 放在现在直接调用 begin() 和 end() 的那里。你要是能从上面的出错信息里面找到一点点跟 const 有关系的东西的话,那么我服了你了…

第五,你得对周围的 lib 非常熟悉,比如 phoenix、fusion。下面的代码没有注释掉的那行 tuple 的是会出错的,本质上 fusion 其实没支持多少东西,必须引入一些宏或者什么 code(boilerplate?)才能实现它所号称支持的东西,

#include <iostream>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
#include <boost/variant.hpp>
#include <boost/tuple/tuple.hpp>

namespace spirit = boost::spirit ;
namespace qi = boost::spirit::qi ;
namespace ascii = boost::spirit::ascii ;
namespace phoenix = boost::phoenix ;

using qi::lexeme ;
using qi::int_ ;
using qi::_val ;
using qi::_1 ;
using ascii::char_ ;
using namespace qi::labels ;
using phoenix::at_c ;

struct keys_ : qi::symbols<char, int> {
  keys_ () {
    add
      ("height", 0)
      ("name",   1)
      ;
  }
} ;

typedef boost::variant<int, std::string> value_type ;

template <class Iter>
struct kv_grammar
  : qi::grammar<Iter, boost::tuple<int, value_type> (), ascii::space_type> {

  kv_grammar () : kv_grammar::base_type (start) {

    quoted_string %=
         lexeme ['"' >> +(char_ - '"') >> '"']
      ;

    start =
         keys [at_c<0>(_val) = _1]
      >> '='
      >> (int_ [at_c<1>(_val) = _1] | quoted_string [at_c<1>(_val) = _1])
      ;
  }

  qi::rule<Iter, boost::tuple<int, value_type> (), ascii::space_type> start ;
  qi::rule<Iter, std::string (), ascii::space_type> quoted_string ;
  keys_ keys ;
} ;

int
main (int, char**) {
  std::string line ;
  value_type v ;
  boost::tuple<int, value_type> u ;
  int a ;
  std::string b ;
  kv_grammar<std::string::const_iterator> p ;
  std::getline (std::cin, line) ;
  std::string::const_iterator iter = line.begin (), end = line.end () ;

  bool r = qi::phrase_parse
    (iter, end, p, ascii::space, u) ;

  if (r)
    std::cout << boost::get<0>(u) << ": " << boost::get<1>(u) << std::endl ;
  std::cout << line << std::endl ;

  return 0 ;
}

——————-
And God said to Abraham, You shall keep my covenant therefore, you, and your seed after you in their generations.

Advertisements
我晕啊 spirit

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s