Проблема при попытке скомпилировать парсер Spirit.Qi
Ниже приведен полностью автономный пример. Кажется, что проблема в строках 84-89 - если эти строки закомментированы, пример компилируется. Я пытаюсь проанализировать каждую строку файла с пятью разделенными двоеточиями элементами, причем последние три элемента являются необязательными. Одна функция занимает boost::filesystem::file
отстой в использовании boost.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
Поскольку количество вопросов ограничено 30000 символов, я покажу только первые несколько здесь. Пример должен попытаться скомпилировать и произвести то же самое на вашем компьютере.
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
1 ответ
Я уже ответил в списке рассылки 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>
>
>
>
>
>
>
>
>
>
Правила совместимости атрибутов могут сделать совсем немного, но они не могут отобразить std::string на variant<char, vector<char> >
наверняка. Более того, я верю, что вы сами больше не понимаете свою грамматику, почему вы ожидаете, что Дух сделает это правильно в этом случае?
Я бы посоветовал вам начать с упрощения грамматики, превратив вещи в правила. Это не только облегчает понимание, но и позволяет вам сказать Духу, какой атрибут вы ожидаете получить от какой части вашей грамматики. Например:
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 == 4
, а также output[0].c == "cde"
, Давайте разберемся, почему это происходит.
Опять же, правила совместимости атрибутов могут выполнять только часть работы. В этом случае file_list::a
отображается на e1
, file_list::b
на u
, в то время как file_list::c
отображается на все остальное выражение. Это то, что вы ожидаете, на самом деле, так как необязательный разбивает последовательность на 3 элемента. Ваш атрибут "сплющен", а грамматика - нет.
Есть два решения: а) изменить свой атрибут в соответствии со структурой грамматики:
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;
};
или б) использовать семантические действия для установки элементов вашего атрибута (что я и сделал бы).