“碳复制”一个c ++ istream? [英] "carbon-copy" a c++ istream?

查看:106
本文介绍了“碳复制”一个c ++ istream?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于我自己的小解析器框架,我试图定义(类似于)以下函数:

For my very own little parser framework, I am trying to define (something like) the following function:

template <class T>
// with operator>>( std::istream&, T& )
void tryParse( std::istream& is, T& tgt )
{
    is >> tgt /* , *BUT* store every character that is consumed by this operation
    in some string. If afterwards, is.fail() (which should indicate a parsing
    error for now), put all the characters read back into the 'is' stream so that
    we can try a different parser. */
}

然后我可以这样写: )

Then I could write something like this: (maybe not the best example)

/* grammar: MyData     = <IntTriple> | <DoublePair>
            DoublePair = <double> <double>
            IntTriple  = <int> <int> <int> */
class MyData
{ public:
    union { DoublePair dp; IntTriple it; } data;
    bool isDoublePair;
};

istream& operator>>( istream& is, MyData& md )
{
    /* If I used just "is >> md.data.it" here instead, the
       operator>>( ..., IntTriple ) might consume two ints, then hit an
       unexpected character, and fail, making it impossible to read these two
       numbers as doubles in the "else" branch below. */
    tryParse( is, md.data.it );
    if ( !is.fail() )
        md.isDoublePair = false;
    else
    {
        md.isDoublePair = true;
        is.clear();
        is >> md.data.dp;
    }
    return is;
}

任何帮助都非常感激。

推荐答案

不幸的是,流只有非常少的基本的后退支持。

Unfortunately, streams have only very minimal and rudimentary putback support.

最后一次我需要这个,我写了我自己的阅读器类,它包装一个流,但有一个缓冲区,把东西放回来,该缓冲区为空。这些方法可以从中获取状态,并且可以提交状态或回滚到更早的状态。

状态类析构函数中的默认操作是回滚,以便您可以在不给出很多人认为错误处理,因为异常只是回滚解析器的状态,直到一个不同的语法规则被尝试的​​点。 (我认为这被称为backtracking。)这是一个草图:

The last times I needed this, I wrote my own reader classes which wrapped a stream, but had a buffer to put things back into, and read from the stream only when that buffer is empty. These had ways to get a state from, and you could commit a state or rollback to an earlier state.
The default action in the state class' destructor was to rollback, so that you could parse ahead without giving much thought to error handling, because an exception would simply rollback the parser's state up to a point where a different grammar rule was tried. (I think this is called backtracking.) Here's a sketch:

class parse_buffer {
    friend class parse_state;
public:
    typedef std::string::size_type index_type;

    parse_buffer(std::istream& str);

    index_type get_current_index() const;
    void set_current_index(index_type) const;

    std::string get_next_string(bool skip_ws = true) const;
    char get_next_char(bool skip_ws = true);
    char peek_next_char(bool skip_ws = true); 

    std::string get_error_string() const; // returns string starting at error idx
    index_type get_error_index() const;
    void set_error_index(index_type);

    bool eof() const;

    // ...
};

class parse_state {
public:
    parse_state(parse_buffer&);
    ~parse_state();

    void commit();
    void rollback();

    // ...
};

这应该给你一个想法。它没有实现,但是这是直接的,应该很容易重做。此外,真正的代码有许多方便的函数,如阅读函数读取分隔的字符串,如果它是几个给定的关键字之一,消耗一个字符串,读取一个字符串,并将其转换为给每个模板参数的类型,这样的东西。

This should give you an idea. It has none of the implementation, but that was straightforward and should be easy to redo. Also, the real code had many convenient functions like reading functions that read a delimited string, consumed a string if it was one of several given keywords, read a string and converted it to a type given per template parameter, and stuff like this.

这个想法是,函数会将错误索引设置为其起始位置,保存解析状态,并尝试解析,直到它成功或进入死区。在后一种情况下,它只会抛出异常。这将破坏堆栈上的 parse_state 对象,将状态回滚到可以捕获异常并尝试其他方式的函数,或输出错误(这是 get_error_string())。

The idea was that a function would set the error index to its starting position, save the parse state, and try to parse until it either succeeded or ran into a dead end. In the latter case, it would just throw an exception. This would destroy the parse_state objects on the stack, rolling back the state up to a function which could catch the exception and either try something else, or output an error (which is where get_error_string() comes in.)

如果你想要一个快速解析器,这个策略可能是错误的,那么流也经常会变慢。 OTOH,最后一次我使用这样的东西,我做了一个XPath解析器,它运行在一个专有的DOM,用于表示3D渲染器中的场景。而且并非 XPath解析器获得了那些想要获得更高帧率的人的热量。 :)

If you want a really fast parser, this strategy might be wrong, but then streams are often to slow, too. OTOH, the last time I used something like this, I made an XPath parser that operates on a proprietary DOM, which is used to represent scenes in a 3D renderer. And it was not the XPath parser that got all the heat from the guys trying to get higher frame rates. :)

这篇关于“碳复制”一个c ++ istream?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆