9
\$\begingroup\$

I want an istream that you can safely peek arbitrarily many characters from. This works as far as I can tell, but I am unsure if this is really "the right way" to do it since the iostream library is pretty cryptic to me.

class peekbuf : public std::streambuf
{
    std::streambuf* sbuf_;
    std::vector<char> data_;
    char ch; 
public:
    peekbuf(std::streambuf* s)
    : sbuf_(s)
    { } 

    std::vector<char> const& peek(int N) {
        data_.clear();
        data_.reserve(N);
        for (int i = 0; i < N; ++i) {
            int next = sbuf_->sbumpc();
            if (next == traits_type::eof()) {
                break;
            }
            data_.push_back(next);
        }

        if (!data_.empty()) {
            setg(data_.data(), data_.data(), data_.data() + data_.size());
        }

        return data_;
    }   

protected:
    int_type underflow() override {
        ch = sbuf_->sbumpc();
        setg(&ch, &ch, &ch + 1); 
        return ch; 
    }   
};

Example usage:

int main() {
    peekbuf buf{std::cin.rdbuf()};
    std::cin.rdbuf(&buf);

    auto first5 = buf.peek(5);

    std::string s;
    std::cin >> s;
    std::cout << "Got " << s << '\n';

    std::cout << "Peeked: ";
    for (char c : first5) {
        std::cout << c;
    }
    std::cout << '\n';
}

Where:

$ echo 123456789 | ./a.out

yields:

Got 123456789
Peeked: 12345
\$\endgroup\$
  • \$\begingroup\$ I don;t have time write now (this weekend). But something feels wrong. When you set the underlying buffer with setg(data_.data(), data_.data(), data_.data() + data_.size()); I would expect you to save the state of the current buffer so when you have finished reading your peeked data you can go back to the original buffer. \$\endgroup\$ – Martin York May 5 '16 at 17:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.