The function boost::spirit::qi::parse()
expects two iterators to define the input range. This works well if I try to parse from std::string
or std::istream
. Now I want implement a more generic interface for my parser. One approach was to use boost::any_range
to define the input. Here is my test code it compiles but throws an exception: "string iterator not dereferencable"
.
Second question. How can I combine boost::any_range
together with boost::spirit::classic::position_iterator
to detected a possible error position?
#include <boost/range/any_range.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_multi_pass.hpp>
namespace qi = boost::spirit::qi;
typedef boost::any_range<
char,
boost::forward_traversal_tag,
char,
std::ptrdiff_t
> input_type;
template < typename _Iterator >
struct decode
: qi::grammar< _Iterator >
{
decode( ) : decode::base_type( m_rule )
{
m_rule = qi::int_;
BOOST_SPIRIT_DEBUG_NODES( ( m_rule ) )
}
qi::rule< _Iterator > m_rule;
};
bool parse( const input_type& in, int& out )
{
// We use a stream iterator to access the given stream:
typedef boost::spirit::multi_pass<
input_type::const_iterator
> stream_iterator;
// Create begin iterator for given stream:
stream_iterator sBegin = boost::spirit::make_default_multi_pass( input_type::const_iterator( in.begin( ) ) );
stream_iterator sEnd = boost::spirit::make_default_multi_pass( input_type::const_iterator( ) );
// Create an instance of the used grammar:
decode<
stream_iterator
> gr;
// Try to decode the data stored within the stream according the grammar and store the result in the out variable:
bool r = boost::spirit::qi::parse( sBegin,
sEnd,
gr,
out );
return r && sBegin == sEnd;
}
void main( )
{
std::string in = "12345"; int out;
parse( in, out );
}
Update
1.) I agree that there is a mistake in the default constructed sEnd
iterator. Therefore I simplified my example and I think I misunderstood how to use the multi_pass
iterator. In this case c0
is false
(expected) and c1
is true
(not expected). So what is the right way to use the multi_pass
iterator?
#include <boost/range/any_range.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_multi_pass.hpp>
namespace qi = boost::spirit::qi;
typedef boost::any_range<
char,
boost::forward_traversal_tag,
char,
std::ptrdiff_t
> input_type;
bool parse( const input_type& in, int& out )
{
//for( input_type::iterator i = in.begin( ); i != in.end( ); ++i )
//{
// std::cout << *i;
//}
// We use a stream iterator to access the given stream:
typedef boost::spirit::multi_pass<
input_type::const_iterator,
boost::spirit::iterator_policies::default_policy< // Defaults:
boost::spirit::iterator_policies::ref_counted, // OwnershipPolicy: ref_counted
boost::spirit::iterator_policies::buf_id_check, // CheckingPolicy : buf_id_check
boost::spirit::iterator_policies::buffering_input_iterator, // InputPolicy : buffering_input_iterator
boost::spirit::iterator_policies::split_std_deque // StoragePolicy : split_std_deque
>
> stream_iterator;
bool c0 = in.begin( ) == in.end( );
// Create begin iterator for given stream:
stream_iterator sBegin( in.begin( ) );
stream_iterator sEnd( in.end( ) );
bool c1 = sBegin == sEnd;
//for( stream_iterator i = sBegin; i != sEnd; ++i )
//{
// std::cout << *i;
//}
return false;
}
void main( )
{
std::string in = "12345"; int out;
parse( in, out );
}
2.) Yes I can compile a new grammar instance for each type of input iterator. My idea was to hide the implementation detail (=boost::spirit
) from the user and to give him a generic interface. Therefore I would like to avoid a template function.
3.) Yes I forgot to expose the attribute. It was only a quick & dirty example. Thanks for the hint.