madf: (Default)
[personal profile] madf

Припустимо, є такий код:
    std::string str("1,2,3,4,5");                                               
                                                                                
    std::vector<int> v;                                                         
    ListGrammar<std::string::const_iterator> grammar;                           
    if (qi::parse(str.begin(), str.end(), grammar, v)) {
        // do something
    }

З першого погляду тут одна помилка: qi::parse приймає посилання на ітератори, а не значення. Так, принаймні, написано у документації: Iterator Based Parser API. Це зроблено для того щоб після розбору отримати позицію останнього розібраного символу. Та іноді позиція нам не потрібна, тому розробники Boost.Spirit додали overloaded функцію, що приймає обидва ітератора по константному посиланню (уся її суть у тому що там локально створюється копія ітератора і віддається звичайній функції). Виходить, помилок немає?
Компілятори так не вважають. Найкращий вивід у шланга (хоча gcc і ekopath теж нормально показують, просто кольором не виділяють і стрілки не малюють):
...
/usr/include/boost/spirit/home/qi/reference.hpp:43:30: error: no matching member function for call to 'parse'
            return ref.get().parse(first, last, context, skipper, attr);
                   ~~~~~~~~~~^~~~~
...
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:247:14: note: candidate function [with Context = const
      boost::spirit::unused_type, Skipper = boost::spirit::unused_type, Attribute = std::vector<int,
      std::allocator<int> >] not viable: no known conversion from '__gnu_cxx::__normal_iterator<char *,
      std::basic_string<char> >' to '__gnu_cxx::__normal_iterator<const char *, std::basic_string<char> > &' for 1st
      argument
        bool parse(Iterator& first, Iterator const& last
             ^
/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:293:14: note: candidate function template not viable: requires 6
      arguments, but 5 were provided
        bool parse(Iterator& first, Iterator const& last

Дивимось у rule.hpp - overloaded метод parse: на 5 і 6 параметрів. Ну, той що на 6 нас не цікавить, а от чому не вийшло з тим що на п’ять?
Сигнатура:
        template <typename Context, typename Skipper, typename Attribute>       
        bool parse(Iterator& first, Iterator const& last                        
          , Context& /*context*/, Skipper const& skipper                        
          , Attribute& attr) const

Дивимось уважно на те чому він не підійшов: "no known conversion from '__gnu_cxx::__normal_iterator<char *, std::basic_string<char> >' to '__gnu_cxx::__normal_iterator<const char *, std::basic_string<char> > &' for 1st argument". Що? Немає методу для перетворення value у referense? Так у нас тут уже referense в усіх полях!
Години 3 у мене пішло на те щоб побачити де помилка. Саме смішне, що у цьому повідомленні вона чітко описана :)
А все тому що я спочатку не знав про overloaded версію qi::parse, виніс два ітератори і передав їх по посиланню:
    std::string str("1,2,3,4,5");                                               
    std::string::const_iterator from(str.begin());                               
    std::string::const_iterator to(str.end());                                   
                                                                                
    std::vector<int> v;                                                         
    ListGrammar<std::string::const_iterator> grammar;                           
    if (qi::parse(from, to, grammar, v)) {
        // do something
    }

Та хоч воно і зібралось після цього, помилка не давала мені спокою.
Все просто. Дивимось початковий код. Яким типом інстанційовано граматику? Правильно, std::string::const_iterator. Власне, це const char *, як правильно нам показує компілятор ekopath. А що ми передаємо у qi::parse? Правильно, begin() і end() які мають дві версії: з isterator і з const_itaretor. Яка версія буде вибрана? Дивимось на сигнатуру:
      iterator begin();
const_iterator begin() const;

У нас str константна? Ні. Які тоді питання? Туди передається звичайний ітератор. Про що чітко пише компілятор: "no known conversion from '__gnu_cxx::__normal_iterator<char *, std::basic_string<char> >' to '__gnu_cxx::__normal_iterator<const char *, std::basic_string<char> > &' for 1st argument". Та хто ж його читає...
Найкраще, мабуть, робити як самі бустівці: писати шаблонну функцію-обгортку над qi::parse, параметризовану типом ітератора. Якось так:
template                                                     
bool parse_int(const Iterator & from, const Iterator & to, std::vector<int> & v)
{                                                                               
    return qi::parse(from, to, ListGrammar<Iterator>(), v);                     
}


В процесі битви з компіляторами я знайшов щонайменше 3 виходи із ситуації (константний std::string, ітератори окремо і через шаблонну функцію), але шило в жопі не давало покинути проблему не розібравши її до кінця :). Да, константний std::string - останнє рішення, яке просто підтвердило гіпотезу про проблеми з константністю.

PS: обожнюю розставляти &lt; і &gt; замість < і > :(

PPS: а ще я сьогодні поборов скіпери, так що тепер з чистою совістю буду дивитись кіна під червоного вина :)
This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

madf: (Default)
madf

April 2018

S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 21st, 2025 10:53 pm
Powered by Dreamwidth Studios