【问题标题】:Error avalanche in Boost.Spirit.Qi usageBoost.Spirit.Qi 使用中的错误雪崩
【发布时间】:2010-02-07 19:59:30
【问题描述】:

我无法弄清楚我的代码有什么问题。 Boost 的模板让我发疯了!我无法从这一切中得出正面或反面,所以我只好问了。

这是怎么回事?

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

void parsePathTest(const std::string &path)
{
    namespace lambda = boost::lambda;
    using namespace boost::spirit;

    const std::string permitted = "._\\-#@a-zA-Z0-9";
    const std::string physicalPermitted = permitted + "/\\\\";
    const std::string archivedPermitted = permitted + ":{}";

    std::string physical,archived;

    // avoids non-const reference to rvalue
    std::string::const_iterator begin = path.begin(),end = path.end();

    // splits a string like "some/nice-path/while_checking:permitted#symbols.bin"
    // as physical = "some/nice-path/while_checking"
    // and archived = "permitted#symbols.bin" (if this portion exists)
    // I could barely find out the type for this expression
    auto expr
        =   ( +char_(physicalPermitted) ) [lambda::var(physical) = lambda::_1]
            >> -(
                    ':'
                    >> (
                           +char_(archivedPermitted) [lambda::var(archived) = lambda::_1]
                       )
                )
        ;

    // the error occurs in a template instantiated from here
    qi::parse(begin,end,expr);

    std::cout << physical << '\n' << archived << '\n';
}

错误的数量是巨大的;我会建议那些想在他们的 on 上帮助编译这个的人(相信我,在这里粘贴是不切实际的)。我正在使用最新的 TDM-GCC 版本 (GCC 4.4.1) 和 Boost 版本 1.39.00。

作为奖励,我想问另外两件事:C++0x 的新 static_assert 功能是否会在这个意义上帮助 Boost,以及我上面引用的实现是否是一个好主意,或者我是否应该使用 Boost 的字符串算法库。后者可能会提供更好的性能吗?

谢谢。

--编辑

以下非常小的示例首先失败,错误与上面的代码完全相同。

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

int main()
{
    using namespace boost::spirit;

    std::string str = "sample";
    std::string::const_iterator begin(str.begin()), end(str.end());

    auto expr
        =   ( +char_("a-zA-Z") )
        ;

    // the error occurs in a template instantiated from here
    if (qi::parse(begin,end,expr))
    {
        std::cout << "[+] Parsed!\n";
    }
    else
    {
        std::cout << "[-] Parsing failed.\n";
    }

    return 0;
}

-- 编辑 2

我仍然不知道为什么它在我的旧版本 Boost (1.39) 中不起作用,但升级到 Boost 1.42 解决了这个问题。以下代码在 Boost 1.42 下编译和运行完美:

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

int main()
{
    using namespace boost::spirit;

    std::string str = "sample";
    std::string::const_iterator begin(str.begin()), end(str.end());

    auto expr
        =   ( +qi::char_("a-zA-Z") ) // notice this line; char_ is not part of 
                                     // boost::spirit anymore (or maybe I didn't 
                                     // include the right headers, but, regardless, 
                                     // khaiser said I should use qi::char_, so here 
                                     // it goes)
        ;

    // the error occurs in a template instantiated from here
    if (qi::parse(begin,end,expr))
    {
        std::cout << "[+] Parsed!\n";
    }
    else
    {
        std::cout << "[-] Parsing failed.\n";
    }

    return 0;
}

感谢 hkaiser 的提示。

【问题讨论】:

  • 出现前几个错误仍然会有所帮助。此外,更喜欢使用预先存在的库。它们附带免费的错误修复,并且已经过测试。
  • GMan:你认为 boost 库是由 n2liquid 以某种方式自制的吗?也许你应该自己看看 boost.org!
  • @Pontus:什么?也许我误解了,但我不知道你是如何从我的评论中得到的。我非常清楚 Boost 是什么。
  • @GMan 那么用什么预先存在的库替换什么是什么意思?我也真的不明白。
  • "以及我上面引用的实现是否是个好主意,或者我是否应该使用 Boost 的字符串算法库。"也许我不明白,但据我所知,您是在问是否使用 Bosot.String 库或您自己手工制作的东西。这是不正确的吗?

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


【解决方案1】:

几点说明: a) 不要使用随 Boost V1.39 和 V1.40 分发的 Spirit V2 beta 版本。至少使用 Spirit V2.1(与 Boost V1.41 一起发布),因为它包含 很多 错误修复和性能增强(编译时间和运行时性能)。如果您无法切换 Boost 版本,请阅读 here 了解如何继续。 b) 尽量避免在 Spirit V2.x 中使用 boost::lambda 或 boost::bind。是的,我知道,文档说它有效,但你必须知道你在做什么。请改用 boost::phoenix 表达式。 Spirit“了解”Phoenix,这使得编写语义动作更容易。如果您使用 Phoenix,您的代码将如下所示:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

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

std::string physical, archived;  
auto expr 
    =   ( +char_(physicalPermitted) ) [phoenix::ref(physical) = qi::_1] 
    >> -( 
            ':' 
            >> ( +char_(archivedPermitted) )[phoenix::ref(archived) = qi::_1] 
        ) 
    ; 

但是如果您使用 Spirit 的内置属性传播规则,您的整体解析器会变得更加简单:

std::string physical;
boost::optional<std::string> archived;  

qi::parse(begin, end, 
    +qi::char_(physicalPermitted) >> -(':' >> +qi::char_(archivedPermitted)),
    physical, archived);

即根本不需要语义动作。如果您需要更多关于属性处理的信息,请参阅 Spirit 网站上关于属性魔法的系列文章。

编辑:

关于您的 static_assert 问题:是的 static_assert,可以改善错误消息,因为它可以用于尽早触发编译器错误。事实上,Spirit 已经广泛使用了这种技术。但是不可能在所有情况下都保护用户免受那些巨大的错误消息,而只是针对程序员确实期望的那些用户错误。只有概念(很遗憾没有纳入新的 C++ 标准)通常可以用来减少错误消息的大小。

关于您的 Boost 的字符串算法问题:当然可以将该库用于像您一样的简单任务。使用 Boost.Tokenizer 甚至可能会更好(如果您只需要在 ':' 处拆分输入字符串)。 Spirit 的性能应该可以与字符串算法的相应性能相媲美,但这当然取决于您将编写的代码。如果您假设使用的字符串算法需要对输入字符串数据进行一次传递,那么 Spirit 不会更快(因为它也是一次传递)。

Boost String 算法和 Boost Tokenizer 都不能为您提供匹配字符的验证。您的 Spirit 语法仅匹配您在字符类中指定的字符。因此,如果您需要这种匹配/验证,您应该使用 Spirit 或 Boost Regex。

【讨论】:

  • 啊,我稍后会尝试更新我的 Boost 副本。它在我的备份分区中至少重新安装了 3 次,为了方便起见,我一直在使用它。不过,我决定在这个实现中使用 Boost String Algorithms,因为我可以更好地“看到”正在做的事情。 Spirit 对我来说似乎仍然过于矫枉过正,我发布了更多内容以了解为什么它在有一天我需要它的情况下不起作用。但问题真的不在于行动,而在于运营商。我已经尝试过 Kleene Star 和 Plus 运算符,但都因类似的错误而失败。
  • 好吧,我想您的问题之一是第二个 +char_ 周围的括号放错了(有关正确代码,请参见上面的示例)。这实际上应该让你的代码编译,至少它应该分割你看到的那些模板错误的一大块。
  • 我已经更新了我的第一篇文章。即使是最简单的代码也会引发大量错误。
  • 您添加到问题中的代码使用 Spirit V2.2(来自 Boost 1.42)编译得很好。好吧,它编译得差不多了,你需要将 char_ 限定为 qi::char_。正如我所说,从 Boost 1.41 中提取的 Spirit 效果同样好,但请不要使用旧版本。
  • 我已经尝试过 Boost 1.42 并且成功了。关于 Spirit 的替代方案,是的,我很可能无法像 Spirit 那样一次性完成所有事情,但对于这样一个微不足道的问题,它仍然是一个很大的库,所以这次我将传递 Spirit。不过,一旦我有更大的理由将其作为新的依赖项,我将立即更改此实现以使用 Spirit。这是一个强大的库,我相信它迟早会派上用场。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多