3

Running the following code results in a crash. Why?

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_multi_pass.hpp>

using namespace boost::spirit;
typedef multi_pass<
            std::string::const_iterator,
            iterator_policies::default_policy<
                iterator_policies::first_owner,
                iterator_policies::no_check,
                iterator_policies::buffering_input_iterator,
                iterator_policies::split_std_deque>>
        string_mp_iterator;

int main() {
    std::string input = "234";
    string_mp_iterator input_begin(input.begin()),
            input_end((string_mp_iterator()));
    qi::rule<string_mp_iterator, boost::variant<int, double>()> r =
            &qi::lit('1') >> qi::int_ | qi::double_;
    qi::parse(input_begin, input_end, r);
    return 0;
}

To reproduce the crash I seem to need to have both a predicate and subsequent alternative, to be using a multi_pass iterator, and for the input to not satisfy the predicate.

I get the feeling that I'm somehow using multi_pass incorrectly here, but I don't see what the problem is exactly.

  • 2
    Are you trying to parse doubles vs. ints reliably? Search for https://stackoverflow.com/search?q=strict_real_policies – sehe Dec 31 '16 at 09:48
  • No, this is just an example where I'm trying to keep things a simple as possible. This will be nonetheless useful elsewhere. Thanks! – Jan Ladislav Dussek Jan 01 '17 at 02:37
  • That's the nature of Undefined Behaviour: you can't reason about it because the results are not defined – sehe Jan 01 '17 at 13:05

2 Answers2

2

It seems you can not wrap std::string with a multi_pass iterator, at least, not with iterator_policies::buffering_input_iterator std::string has a pointer based end, not a null. That is why the iterator comes up incompatible. If you are going to just parse a std::string, use the iterators directly as they meet the requirement of multi_pass. If you plan on changing to a stream (code sorta from here):

typedef std::istreambuf_iterator<char> base_iterator_type;
typedef boost::spirit::multi_pass<base_iterator_type> forward_iterator_type;

main( )
{
    std::istringstream input( "234" );

    base_iterator_type in_begin(input);
    base_iterator_type in_end;
    forward_iterator_type fwd_begin = boost::spirit::make_default_multi_pass(in_begin);
    forward_iterator_type fwd_end   = boost::spirit::make_default_multi_pass(in_end);

    qi::rule<forward_iterator_type, boost::variant<int, double>()> r =
        &qi::lit('1') >> qi::int_ | qi::double_;
    qi::parse(fwd_begin, fwd_end, r);
    return 0;
}
Community
  • 1
  • 1
lakeweb
  • 1,859
  • 2
  • 16
  • 21
2

Simply fix the initializer for the end iterator.

string_mp_iterator input_end(input.end());

Since it's not an input iterator, you cannot use a default constructed iterator legally.

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_multi_pass.hpp>

using namespace boost::spirit;
typedef multi_pass<
    std::string::const_iterator,
    iterator_policies::default_policy<
        iterator_policies::first_owner, iterator_policies::no_check,
        iterator_policies::buffering_input_iterator,
        iterator_policies::split_std_deque>>
    string_mp_iterator;

int main() {
    std::string input = "234";
    string_mp_iterator input_begin(input.begin()),
                       input_end(input.end());
    qi::rule<string_mp_iterator, boost::variant<int, double>()> r = &qi::lit('1') >> qi::int_ | qi::double_;
    qi::parse(input_begin, input_end, r);
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • This clearly fixes the code, but it's still a mystery to me why the predicate and alternative combination is necessary to cause a crash. In my real use case I am not wrapping a string iterator into a `multi_pass`. This does nonetheless confirm my suspicion that my use of `multi_pass` is not correct. – Jan Ladislav Dussek Jan 01 '17 at 02:37