2014-11-23 1 views
2

Я хочу, чтобы разобрать что-то вроде следующего:Ошибка компиляции пользовательского контейнера для повышающего духа

1;2 
=1200 
3;4 
5;6 

линии могут появляться в любом порядке. Линии, начинающиеся с знака =, могут быть более одного, и только последнее имеет значение; строки, содержащие a; представляют пару значений, которые я хочу сохранить на карте. Чтение ответа на this question Я придумал код, который должен быть достаточно хорошим (извините, но я все еще ноб с Духом) и должен делать то, что я пытаюсь достичь. Вот код:

#define BOOST_SPIRIT_USE_PHOENIX_V3 
#define DATAPAIR_PAIR 

#include <iostream> 
#include <boost/spirit/include/qi.hpp> 
#include <boost/fusion/adapted/std_pair.hpp> 
#include <boost/mpl/bool.hpp> 
#include <map> 
#if !defined(DATAPAIR_PAIR) 
#include <vector> 
#endif 


static const char g_data[] = "1;2\n=1200\n3;4\n5;6\n"; 
typedef std::string DataTypeFirst; 

#if defined(DATAPAIR_PAIR) 
typedef std::string DataTypeSecond; 
typedef std::pair<DataTypeFirst, DataTypeSecond> DataPair; 
typedef std::map<DataTypeFirst, DataTypeSecond> DataMap; 
#else 
typedef std::vector<DataTypeFirst> DataPair; 
typedef std::map<DataTypeFirst, DataTypeFirst> DataMap; 
#endif 

struct MyContainer { 
    DataMap data; 
    double number; 
}; 

namespace boost { namespace spirit { namespace traits { 
    template<> struct is_container<MyContainer> : boost::mpl::true_ {}; 

    template<> 
    struct container_value<MyContainer> { 
     typedef boost::variant<double, DataPair> type; 
    }; 

    template <> 
    struct push_back_container<MyContainer, double> { 
     static bool call (MyContainer& parContainer, double parValue) { 
      parContainer.number = parValue; 
      return true; 
     } 
    }; 

    template <> 
    struct push_back_container<MyContainer, DataPair> { 
     static bool call (MyContainer& parContainer, const DataPair& parValue) { 
#if defined(DATAPAIR_PAIR) 
      parContainer.data[parValue.first] = parValue.second; 
#else 
      parContainer.data[parValue[0]] = parValue[1]; 
#endif 
      return true; 
     } 
    }; 
} } } 

template <typename Iterator> 
struct TestGrammar : boost::spirit::qi::grammar<Iterator, MyContainer()> { 

    TestGrammar (void); 
    boost::spirit::qi::rule<Iterator, MyContainer()> start; 
    boost::spirit::qi::rule<Iterator, DataPair()> data; 
    boost::spirit::qi::rule<Iterator, double()> num; 
}; 

template <typename Iterator> 
TestGrammar<Iterator>::TestGrammar() : 
    TestGrammar::base_type(start) 
{ 
    using boost::spirit::qi::alnum; 
    using boost::spirit::qi::lit; 
    using boost::spirit::ascii::char_;; 
    using boost::spirit::qi::double_; 
    using boost::spirit::qi::eol; 
    using boost::spirit::qi::eoi; 

    start %= *((num | data) >> (eol | eoi)); 
    data = +alnum >> lit(";") >> +alnum; 
    num = '=' >> double_; 
} 

int main() { 
    std::cout << "Parsing data:\n" << g_data << "\n"; 

    TestGrammar<const char*> gramm; 
    MyContainer result; 
    boost::spirit::qi::parse(static_cast<const char*>(g_data), 
     g_data + sizeof(g_data)/sizeof(g_data[0]) - 1, 
     gramm, 
     result 
    ); 
    std::cout << "Parsed data:\n"; 
    std::cout << "Result: " << result.number << "\n"; 
    for (const auto& p : result.data) { 
     std::cout << p.first << " = " << p.second << '\n'; 
    } 

    return 0; 
} 

Я развиваю это на Gentoo Linux, используя Dev-LIBS/повышения-1.55.0-r2: 0/1.55.0 и GCC (Gentoo 4.8.3 P1.1, пирог -0,5,9) 4.8.3. Компиляция выше код я получаю сообщение об ошибке, как

/usr/include/boost/spirit/home/support/container.hpp:278:13: ошибка: «структура MyContainer» не имеет элемента с именем «всунуть»

как обходной путь, я придумал альтернативный код, который вы получите, комментируя строку #define DATAPAIR_PAIR. В этом случае код компилируется и работает, но я действительно хочу пару, где я могу, например, смешивать значения std :: string и int. Почему использование std :: pair в качестве атрибута для моего правила данных заставляет компилятор пропустить правильную специализацию push_back_container? Можно ли исправить код и заставить его работать, используя std :: pair или что-нибудь эквивалентное?

+0

[This] (http://coliru.stacked-crooked.com/a/81459094d3aed670) работает, но далек от хорош. Единственное добавление отмечено «// ADDED». К сожалению, я не знаю, почему это необходимо в вашем случае, а не в примере, связанном или обходном пути. – llonesmiz

+0

@cv_and_he какое время – sehe

+0

@cv_and_he это довольно странное требование (на самом деле это имеет смысл, но это странно в том смысле, что это не всегда требуется). Хорошая работа. Я не мог заставить его работать, поэтому обманул, посмотрев на ваше решение :) – sehe

ответ

1

Я бы упростил это путем/просто/не рассматривал такие вещи, как контейнер и не-контейнер в то же время. Таким образом, для данной конкретной ситуации я мог бы отойти от своей обычной мантры (avoid semantic actions) и использовать them¹:

Live On Coliru

template <typename It, typename Skipper = qi::blank_type> 
struct grammar : qi::grammar<It, MyContainer(), Skipper> { 
    grammar() : grammar::base_type(start) { 
     update_number = '=' > qi::double_ [ qi::_r1 = qi::_1 ]; 
     map_entry  = qi::int_ > ';' > qi::int_; 

     auto number = phx::bind(&MyContainer::number, qi::_val); 
     auto data = phx::bind(&MyContainer::data, qi::_val); 

     start   = *(
      ( update_number(number) 
       | map_entry [ phx::insert(data, phx::end(data), qi::_1) ] 
      ) 
      >> qi::eol); 
    } 

    private: 
    qi::rule<It, void(double&),  Skipper> update_number; 
    qi::rule<It, MyContainer::Pair(), Skipper> map_entry; 
    qi::rule<It, MyContainer(),  Skipper> start; 
}; 

Если вы можете позволить себе (0;0) записи в вашей карте, вы можете даже обойтись с грамматикой:

Live On Coliru

std::map<int, int> data; 
double number; 

bool ok = qi::phrase_parse(f, l, 
     *(
      (qi::omit['=' > qi::double_ [phx::ref(number)=qi::_1]] 
      | (qi::int_ > ';' > qi::int_) 
     ) >> qi::eol) 
     , qi::blank, data); 

Я могу попробовать сделать свой «продвинутый» дух подход работу тоже, но это может занять некоторое время :)


¹ я использую auto для удобства чтения, но, конечно, вы не» t нужно использовать это; просто повторите подвыражения в строке или используйте BOOST_AUTO. Обратите внимание, что это не является общим советом для выражений парсера с выраженным состоянием (см. BOOST_SPIRIT_AUTO)

+0

Это, вероятно, лучшее решение (saner?). – llonesmiz

+0

Либо это, либо я просто укушу пулю и проанализирую, что такое вход, а _then_ интерпретирует это, чтобы найти последнее значение поля 'number' – sehe

+0

Спасибо за ответы и комментарии.Поэтому, чтобы сделать мой вопрос более конкретным, я пытаюсь реализовать парсинг-анализатор как упражнение (как в .cue/.bin, см. «Cue sheet» в wikipedia). Этерогенная часть в этом случае - «FILE », где я храню имя/тип как строку/перечисление, но у меня также были проблемы со строкой/строкой case. В случае, например, в виде таблицы, какой подход лучше всего подходит? Семантические действия, предложенные вами или посетители, или двухпроходный синтаксический анализ, предложенный cv_and_he? –