【问题标题】:Combine boost::spirit and boost::any_range?结合 boost::spirit 和 boost::any_range?
【发布时间】:2015-09-13 16:44:36
【问题描述】:

函数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.) 是的,我忘了公开属性。这只是一个快速而肮脏的例子。感谢您的提示。

【问题讨论】:

  • 我将忽略“第二个问题”。一次 1 个问题。两者本身都很复杂。

标签: c++ boost boost-spirit boost-range


【解决方案1】:

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

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

解析器继续读取。幸运的是,您正在使用某种编译器/库实现来检测过去的访问。


实际上,你不能为输入迭代器编译一个新的语法 (decode&lt;&gt;) 实例吗?这就是 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

【讨论】:

  • 根据你的回答更新了我的问题。
  • 您似乎想强制 multi_pass 使用“常规”迭代器。好。你可以如果你write your own input policy。个人认为这很疯狂。它只会让一切变得低效。您还知道哪些其他库可以使流表现为范围?
  • 在我的回答中添加了我将在公共 API 中执行的操作的草图 (Live too)
  • 认为你是对的。我的假设是我可以将流迭代器包装在 boost::any_range 中。但这是不可能的。因此,您提出的界面对我来说似乎没问题。为了更通用,我会将 parse(std::string) 替换为 parse(boost::any_type)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多