jmlopez
jmlopez

Reputation: 4953

setting the internal buffer for std::istringstream

I have the following piece of code that used to work in OS X before I upgraded to OS X 10.9.4 This is the code:

#include <iostream>
#include <sstream>

int main() {
    // Buffer to be read
    const char* buffer = "0 3";
    std::cout << "buffer: '" << buffer << "'\n";
    size_t len = 3;

    std::istringstream iss;
    iss.clear();
    iss.rdbuf()->pubsetbuf((char*)buffer, len);

    // Did iss got a hold of buffer?
    std::cout << "str: '" << iss.str() << "'\n";
}

The idea is simple, and it has been already asked. I want to be able to read from an existing string and read from it without having to copy its contents. As I already mentioned, this code does not give the correct output in OS X 10.9.4.

My guess as to why this is the case, is because as it was mentioned in this post the implementation of pubsetbuf does nothing.

My question is: Is there a way of achieving the desired results in every machine? What would I need to do in order to set the internal buffer correctly?

In case this is not clear, I expect the following output:

buffer: '0 3'
str: '0 3'

The current output I'm obtaining in my machine is

buffer: '0 3'
str: ''

Meaning that the internal buffer in the istringstream was not set.

Upvotes: 1

Views: 922

Answers (2)

David G
David G

Reputation: 96835

You can implement a stream buffer that takes a range of the characters you want to use as its buffer. Then wrap it with a convenience input stream:

#include <iostream>
#include <sstream>
#include <vector>

struct raw_char_buffer : std::streambuf
{
    explicit raw_char_buffer(const char* const begin,
                             const char* const end)
        : buffer(begin, end)
    {
        this->setg(buffer.data(), buffer.data(),
                   buffer.data() + buffer.size());
    }
private:
    std::vector<char> buffer;
};

struct raw_char_istream : virtual raw_char_buffer
                        , std::istream
{
    explicit raw_char_istream(const char* const begin,
                              const char* const end)
        : raw_char_buffer(begin, end)
        , std::istream(this)
    {
    }

    std::string str() const {
        return std::string(this->eback(), this->egptr());
    }
};

int main()
{
    const char* p = "Hello World";
    raw_char_istream os(p, p + 11);

    std::string s;
    os >> s;

    std::cout << "s = " << s << '\n';
    std::cout << "str() = " << os.str() << '\n';
}

Upvotes: 1

user2249683
user2249683

Reputation:

Instead of tampering with std::istringstream you might write your own:

#include <algorithm>
#include <ostream>

// BasicInputSequenceBuffer
// ============================================================================

template < typename Char, typename Traits = std::char_traits<Char> >
class BasicInputSequenceBuffer
:   public std::basic_streambuf<Char, Traits>
{
    // Types
    // =====

    private:
    typedef std::basic_streambuf<Char, Traits> Base;

    public:
    typedef typename Base::char_type char_type;
    typedef typename Base::int_type int_type;
    typedef typename Base::pos_type pos_type;
    typedef typename Base::off_type off_type;
    typedef typename Base::traits_type traits_type;
    typedef const char_type* pointer;
    typedef std::size_t size_type;

    // Construction
    // ============

    public:
    BasicInputSequenceBuffer(pointer data, size_type size) {
        // These casts are safe (no modification will take place):
        char* begin = const_cast<char_type*>(data);
        char* end = const_cast<char_type*>(data + size);
        this->setg(begin, begin, end);
    }

    // Stream Buffer Interface
    // =======================

    protected:
    virtual std::streamsize showmanyc();
    virtual std::streamsize xsgetn(char_type*, std::streamsize);
    virtual int_type pbackfail(int_type);

    // Utilities
    // =========

    protected:
    int_type eof() { return traits_type::eof(); }
    bool is_eof(int_type ch) { return ch == eof(); }
};


// Get Area
// ========

template <typename Char, typename Traits>
std::streamsize
BasicInputSequenceBuffer<Char, Traits>::showmanyc() {
    return this->egptr() - this->gptr();
}

template <typename Char, typename Traits>
std::streamsize
BasicInputSequenceBuffer<Char, Traits>::xsgetn(char_type* p, std::streamsize n) {
    std::streamsize result = std::min(n, this->egptr() - this->gptr());
    std::copy(this->gptr(), this->gptr() + result, p);
    this->gbump(result);
    return result;
}

template <typename Char, typename Traits>
typename BasicInputSequenceBuffer<Char, Traits>::int_type
BasicInputSequenceBuffer<Char, Traits>::pbackfail(int_type ch) {
    if(is_eof(ch)) {
        if(this->eback() != this->gptr()) {
            this->gbump(-1);
            return traits_type::to_int_type(*this->gptr());
        }
    }
    return eof();
}

typedef BasicInputSequenceBuffer<char> InputSequenceBuffer;


// BasicInputSequence
//=============================================================================

template < typename Char, typename Traits = std::char_traits<Char> >
class BasicInputSequence
:   public std::basic_istream<Char, Traits>
{
    // Types
    // =====

    private:
    typedef std::basic_istream<Char, Traits> Base;

    public:
    typedef typename Base::char_type char_type;
    typedef typename Base::int_type int_type;
    typedef typename Base::pos_type pos_type;
    typedef typename Base::off_type off_type;
    typedef typename Base::traits_type traits_type;

    private:
    typedef BasicInputSequenceBuffer<Char, Traits> buffer_type;

    public:
    typedef typename buffer_type::pointer pointer;
    typedef typename buffer_type::size_type size_type;

    // Construction
    // ============

    public:
    explicit BasicInputSequence(pointer data, size_type size)
    :   Base(&m_buf), m_buf(data, size)
    {}

    private:
    buffer_type m_buf;
};

typedef BasicInputSequence<char> InputSequence;

#include <iostream>

int main() {
    const char* buffer = "0 3";
    InputSequence stream(buffer, 3);
    std::string a;
    std::string b;
    stream >> a >> b;
    std::cout << "str: '" << a << ' ' << b << "'\n";
    return 0;
}

Upvotes: 2

Related Questions