1

下面是一个完全独立的示例。问题似乎是第 84-89 行 - 如果这些行被注释掉,则示例编译。我要解析的是文件的每一行,有五个用冒号分隔的项目,最后三个项目是可选的。单个函数接受 a boost::filesystem::file,将其吸入 usingboost.interprocess并解析它。

我想要解析的示例:

a:1
a:2:c
a:3::d
a:4:::e
a:4:c:d:e

结果应该存储在 , 中vector<file_line>,并且file_line是一个有五个成员的结构,最后三个是可选的。这是代码和错误:

代码

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma warning(disable : 4512) // assignment operator could not be generated
# pragma warning(disable : 4127) // conditional expression is constant
# pragma warning(disable : 4244) // 'initializing' : conversion from 'int' to 'char', possible loss of data
#endif

#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/qi.hpp>
#include <boost/spirit/home/qi/string.hpp>
#include <boost/spirit/home/karma.hpp>
#include <boost/spirit/home/karma/binary.hpp>
#include <boost/spirit/home/phoenix.hpp>
#include <boost/spirit/home/phoenix/bind.hpp>
#include <boost/spirit/home/phoenix/core.hpp>
#include <boost/spirit/home/phoenix/operator.hpp>
#include <boost/spirit/home/phoenix/statement/sequence.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/filesystem/operations.hpp>

#include <string>

// This struct and fusion adapter is for parsing file servers in colon-newline format. 
struct file_line
{
  std::string a;
  unsigned short b;
  boost::optional<std::string> c;
  boost::optional<std::string> d;
  boost::optional<std::string> e;
};
BOOST_FUSION_ADAPT_STRUCT(
  file_line,
  (std::string, a)
  (unsigned short, b)
  (boost::optional<std::string>, c)
  (boost::optional<std::string>, d)
  (boost::optional<std::string>, e)
)

void
import_proxies_colon_newline(const boost::filesystem::path& file)
{
  using namespace boost::spirit;
  using qi::parse;
  using qi::char_;
  using qi::eol;
  using qi::eoi;
  using qi::lit;
  using qi::ushort_;

  // <word>:<ushort>:[word]:[word]:[word]
  if(boost::filesystem::exists(file) && 0 != boost::filesystem::file_size(file))
  {
    // Use Boost.Interprocess for fast sucking in of the file. It works great, and provides the bidirectional
    // iterators that we need for spirit.
    boost::interprocess::file_mapping mapping(file.file_string().c_str(), boost::interprocess::read_only);
    boost::interprocess::mapped_region mapped_rgn(mapping, boost::interprocess::read_only);

    const char*       beg = reinterpret_cast<char*>(mapped_rgn.get_address());
    char const* const end = beg + mapped_rgn.get_size();

    // And parse the data, putting the results into a vector of pairs of strings.
    std::vector<file_line> output;

    parse(beg, end,

          // Begin grammar
          (
            *(
                *eol
              >> +(char_ - (':' | eol) 
              >> ':' >> ushort_         
              >> -(':'
                    >> *(char_ - (':' | eol)) 
                    >> (eol | 
                          -(':'
                              >> *(char_ - (':' | eol)) 

                              // This doesn't work. Uncomment it, won't compile. No idea why. It's the same
                              // as above.
                              >> (eol |
                                    -(':'
                                        >>
                                        +(char_ - eol) 
                                      )
                                )
                          )
                        )
                  )
              >> *eol
            )
          )
          // End grammar, begin output data

          ,output
          );
  }
}

来自 MSVC 10 的错误消息

由于问题限制为 30,000 个字符,因此我将仅在此处显示前几个。该示例应该尝试在您的机器上编译和生成相同的东西。

1>C:\devel\dependencies\boost\boost-1_44\include\boost/spirit/home/support/container.hpp(101): error C2955: 'boost::Container' : use of class template requires template argument list
1>          C:\devel\dependencies\boost\boost-1_44\include\boost/concept_check.hpp(602) : see declaration of 'boost::Container'
1>          C:\devel\dependencies\boost\boost-1_44\include\boost/spirit/home/qi/operator/kleene.hpp(65) : see reference to class template instantiation 'boost::spirit::traits::container_value<Container>' being compiled
1>          with
1>          [
1>              Container=char
1>          ]
1>          C:\devel\dependencies\boost\boost-1_44\include\boost/spirit/home/qi/detail/fail_function.hpp(38) : see reference to function template instantiation 'bool boost::spirit::qi::kleene<Subject>::parse<Iterator,Context,Skipper,Attribute>(Iterator &,const Iterator &,Context &,const Skipper &,Attribute &) const' being compiled
1>          with
1>          [
1>              Subject=boost::spirit::qi::difference<boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::char_,boost::spirit::char_encoding::standard>>,boost::spirit::qi::alternative<boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::standard,true,false>,boost::fusion::cons<boost::spirit::qi::eol_parser,boost::fusion::nil>>>>,
1>              Iterator=const char *,
1>              Context=const boost::fusion::unused_type,
1>              Skipper=boost::fusion::unused_type,
1>              Attribute=char
1>          ]

……剪……

1>C:\devel\dependencies\boost\boost-1_44\include\boost/spirit/home/support/container.hpp(102): fatal error C1903: unable to recover from previous error(s); stopping compilation
4

1 回答 1

2

我已经在 Spirit 邮件列表上回答了,但为了完整起见,我也将其发布在这里。


你的例子远非最小。我看不出你为什么在代码中留下进程间、文件系统或 Karma 引用。这只会使每个愿意提供帮助的人的诊断变得更加困难。此外,您在某处有一个不匹配的括号。我假设您错过了关闭+(char_ - (':' | eol).

好的,让我们仔细看看。这是您的(简化的)语法。它不再做任何有用的事情,但在属性方面它应该与原来的行为相同:

*(+char_ >> -(*char_ >> (eol | -(*char_ >> (eol | -(':' >> +char_))))))

该语法的暴露(传播属性)是:

vector<
  tuple<
    std::vector<char>,
    optional<
      tuple<
        std::vector<char>,
        variant<
          char,
          optional<
            tuple<
              std::vector<char>,
              variant<
                char,
                optional<
                  std::vector<char>
                >
              >
            >
          >
        >
      >
    >
  >
>

属性兼容性规则可以做很多事情,但它们不能variant<char, vector<char> >肯定地将 std::string 映射到 a 上。而且,我相信你自己已经不明白你的语法了,为什么你指望Spirit能在这种情况下做对呢?

我的建议是,您首先要通过将事物分解为规则来简化语法。这不仅使它更容易理解,而且可以让你告诉 Spirit 你希望从语法的哪个子部分得到什么属性。例如:

rule<char const*, std::string()> e1 = +~char_(":\r\n");
rule<char const*, std::string()> e2 = *~char_(":\r\n");
rule<char const*, std::string()> e3 = +~char_("\r\n");
rule<char const*, ushort()> u = ':' >> ushort_;
rule<char const*, file_line()> fline = 
    *eol >> e1 >> u
         >> -(':' >> e2 >> (eol | -(':' >> e2 >> (eol | -(':' >> e3))))) >> *eol;

这使得整体语法更具可读性:

*fline

漂亮吧?

如果你进一步思考它,你会意识到,写作

foo >> (eol | -bar) >> *eol

相当于:

foo >> -bar >> *eol

这更加简化了它:

rule<char const*, file_line()> f = 
    *eol >> e1 >> u >> -(':' >> e2 >> -(':' >> e2 >> -(':' >> e3) ) ) >> *eol;

您现在可以看到,您的语法至少产生 5 个子属性,而您的 file_list 只有四个成员。您需要相应地调整您的 file_list 结构。

上面的内容现在可以编译(Boost SVN trunk),但它无法产生正确的结果。如果我用 喂它"a:4:c:d:e",我会得到结果:output[0].a == "a"output[0].b == 4output[0].c == "cde"。让我们分析一下为什么会这样。

同样,属性兼容性规则只能完成部分工作。在这种情况下file_list::a,被映射到e1file_list::bu,而file_list::c被映射到表达式的整个其余部分。实际上,这正是您所期望的,因为可选项将序列分成 3 个元素。您的属性是“扁平化”的,而语法不是。

有两种解决方案:a)更改您的属性以匹配语法结构:

struct file_line
{
  std::string a;
  unsigned short b;
  boost::optional<
    fusion::vector<
      std::string, 
      boost::optional<
        fusion::vector<std::string, boost::optional<std::string> >
      >
    >
  > c;
};

或 b)使用语义操作来设置属性的元素(这是我会做的)。

于 2011-02-14T17:50:33.163 回答