合并 boost::spirit 和 boost::any_range?

Combine boost::spirit and boost::any_range?

函数boost::spirit::qi::parse()需要两个迭代器来定义输入范围。如果我尝试从 std::stringstd::istream 解析,这很有效。现在我想为我的解析器实现一个更通用的接口。一种方法是使用 boost::any_range 来定义输入。这是我编译但抛出异常的测试代码:"string iterator not dereferencable".

第二个问题。如何将 boost::any_rangeboost::spirit::classic::position_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;

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 );
}

更新

1.) 我同意默认构造的 sEnd 迭代器存在错误。因此我简化了我的例子,我想我误解了如何使用 multi_pass 迭代器。在这种情况下,c0false(预期),c1true(不是预期)。那么multi_pass迭代器的正确使用方法是什么?

#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.) 是的,我可以为每种类型的输入迭代器编译一个新的语法实例。我的想法是对用户隐藏实现细节 (=boost::spirit) 并给他一个通用接口。因此我想避免使用模板函数。

3.) 是的我忘了公开属性。这只是一个快速而肮脏的例子。谢谢提示。

默认构造的迭代器不等同于您的范围的结束迭代器。

输入迭代器通常只遵循该约定。

解析器继续读取。幸运的是,您正在使用某种 compiler/library 实现来检测结束后访问。


实际上,您不能为输入迭代器编译一个新语法 (decode<>) 实例吗?这就是 C++ 泛型编程的全部要点。


更新

这就是我要做的:

  • 请注意 do_parse(以及与 Spirit 或 Boost 相关的所有内容)可以隐藏在 cpp

Live On Coliru

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

namespace mylib {
    struct public_api {
        int parse(std::string const& input);
        int parse(std::istream& stream);
    };

    template<typename It>
    static int do_parse(It f, It l) {
        namespace qi = boost::spirit::qi;

        int result;
        if (qi::parse(f, l, qi::int_, result))
            return result;

        throw std::runtime_error("parse failure");
    }

    int public_api::parse(std::string const& input) {
        return do_parse(input.begin(), input.end());
    }

    int public_api::parse(std::istream& stream) {
        boost::spirit::istream_iterator f(stream >> std::noskipws), l;
        return do_parse(f, l);
    }
}

int main()
{
    std::istringstream iss("12345");
    std::string const s("23456");

    mylib::public_api api;
    std::cout << api.parse(s)   << "\n";
    std::cout << api.parse(iss) << "\n";
}

版画

23456
12345