Ошибка лавины в использовании Boost.Spirit.Qi - PullRequest
3 голосов
/ 07 февраля 2010

Я не могу понять, что не так с моим кодом. Шаблоны 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';
}

Количество ошибок огромно; Я хотел бы предложить людям, которые хотят помочь составить это на их (поверьте мне, вставка здесь непрактично). Я использую последнюю версию 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.

1 Ответ

7 голосов
/ 10 февраля 2010

Несколько замечаний: а) не используйте бета-версию Spirit V2, поставляемую с Boost V1.39 и V1.40. Вместо этого используйте как минимум Spirit V2.1 (как выпущено с Boost V1.41), поскольку он содержит lot исправлений ошибок и улучшений производительности (как времени компиляции, так и времени выполнения). Если вы не можете переключать версии Boost, прочитайте здесь , чтобы узнать, как продолжить. б) Старайтесь избегать использования boost :: lambda или boost :: bind с Spirit V2.x. Да, я знаю, документы говорят, что это работает, но вы должны знать, что вы делаете. Вместо этого используйте выражения boost :: 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, либо Boost Regex.

...