Search code examples
c++boost-spiritboost-spirit-qi

strange error with boost::spirit::position_iterator2


So what I am trying to do is to parse a list of strings:

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

std::string TEST = "aa\nbbbb\nccc\n";

std::istringstream INPUT (TEST);
std::noskipws(INPUT);

typedef std::istreambuf_iterator<char> base_iterator;
typedef boost::spirit::multi_pass<base_iterator>  multi_pass_iter;
typedef boost::spirit::classic::position_iterator2<multi_pass_iter> pos_iterator;

base_iterator base_begin(INPUT);

multi_pass_iter first =  boost::spirit::make_default_multi_pass(base_begin);
multi_pass_iter last;

pos_iterator pfirst(first,last,std::string("DD"));
pos_iterator plast;

using qi::lexeme;
using ascii::alpha;

std::vector<std::string> DDD;
bool res = qi::phrase_parse(pfirst,plast,* lexeme[+alpha],ascii::space,DDD);

for (const auto & d : DDD) std::cout << d << " (" << d.size() << ")" << std::endl;

What i get in DDD are 3 strings of the correct size, but all of whitespaces.

If instead i use

bool res = qi::phrase_parse(first,last,* lexeme[+alpha],ascii::space,DDD);

everything works as expected. I used position_iterator2 in the past without any problem, so I don't believe it is a bug. Am I missing something?


Solution

  • There is an example here that doesn't work either. Using Visual Studio 2012 both give a warning:

    boost/iterator/iterator_adaptor.hpp(306): warning C4172: returning address of local variable or temporary
    boost_1_52_0\boost/iterator/iterator_adaptor.hpp(306) : while compiling class template member function 'const char &boost::iterator_adaptor<Derived,Base,Value,Traversal>::dereference(void) const'
             with
             [
                Derived=boost::spirit::classic::position_iterator2<forward_iterator_type>,
                Base=forward_iterator_type,
                Value=const char,
                Traversal=boost::forward_traversal_tag
             ]
    

    A quick google search of "iterator_adaptor dereference temporary" leads to this that recommends that the Reference parameter of iterator_adaptor be a non-reference type.

    In order to accomplish that you need to change the file "boost/spirit/home/classic/iterator/impl/position_iterator.ipp". Specifically you'd need to change:

    typedef boost::iterator_adaptor<
        main_iter_t,
        ForwardIterT,
        const_value_type,
        boost::forward_traversal_tag
    > type;
    

    to:

    typedef boost::iterator_adaptor<
        main_iter_t,
        ForwardIterT,
        const_value_type,
        boost::forward_traversal_tag,
        const_value_type
    > type;
    

    this leads to a new error in both g++ and vc11:

    boost_1_52_0\boost/concept_check.hpp(212): error C2440: 'initializing' : cannot convert from 'boost::detail::iterator_category_with_traversal<Category,Traversal>' to 'std::forward_iterator_tag'
              with
              [
                  Category=std::input_iterator_tag,
                  Traversal=boost::forward_traversal_tag
              ]
              No constructor could take the source type, or constructor overload resolution was ambiguous
    

    That can be avoided if you change the iterator_adaptor typedef to:

    typedef boost::iterator_adaptor<
        main_iter_t,
        ForwardIterT,
        const_value_type,
        std::forward_iterator_tag,
        const_value_type
    > type;
    

    This makes both the program below (based on your code) and the example from boost-spirit.com work, but I'm not sure that it won't break in other cases, so use it at your discretion.

    #include <vector>
    #include <istream>
    #include <sstream>
    #include <iostream>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/support_multi_pass.hpp>
    #include <boost/spirit/include/classic_position_iterator.hpp>
    
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    
    int main()
    {
    
        std::string TEST = "aa\nbbbb\nccc\n";
    
        std::istringstream INPUT (TEST);
        std::noskipws(INPUT);
    
        typedef std::istreambuf_iterator<char> base_iterator;
        typedef boost::spirit::multi_pass<base_iterator>  multi_pass_iter;
        typedef boost::spirit::classic::position_iterator2<multi_pass_iter> pos_iterator;
    
        base_iterator base_begin(INPUT);
    
        multi_pass_iter first =  boost::spirit::make_default_multi_pass(base_begin);
        multi_pass_iter last;
    
        pos_iterator pfirst(first,last,std::string("DD"));
        pos_iterator plast;
    
        using qi::lexeme;
        using ascii::alpha;
    
        std::vector<std::string> DDD;
        bool res = qi::phrase_parse(pfirst,plast,* lexeme[+alpha],ascii::space,DDD);
    
        if(res && pfirst==plast)
        {
            for (const auto & d : DDD) 
                std::cout << d << " (" << d.size() << ")" << std::endl;
        }
        else
        {
            std::cout << "Parsing error." << std::endl;
        }
    
        return 0;
    }