GabiMe
GabiMe

Reputation: 18503

C++: Wrapping vector<char> with istream

I want to wrap a vector<char> with std::istream (so reading the vector would be done through the istream interface)

What's the way to do it?

Upvotes: 31

Views: 17463

Answers (6)

maxschlepzig
maxschlepzig

Reputation: 39155

If you are fine with swapping your vector<char> you can use Boost Interprocess' vectorstream. Example:

#include <boost/interprocess/streams/vectorstream.hpp>

#include <vector>
#include <string>
#include <iostream>


using namespace std;

int main(int argc, char **argv)
{
  static const char inp[] = "123 45 666"; 
  vector<char> v(inp, inp + sizeof inp  - 1);
  using ivectorstream =
    boost::interprocess::basic_ivectorstream<std::vector<char>>;
  ivectorstream is;
  is.swap_vector(v);
  while (!is.eof()) {
    int i = 0;
    is >> i;
    cout << i << '\n';
  }
  is.swap_vector(v);
  cout << string(v.begin(), v.end()) << '\n';
  return 0;
}

Alternatively, if you can't or don't want to mutate your vector<char>, you can use Boost Interprocess' bufferstream. With bufferstream, you don't have to swap your vector into it. Example:

#include <boost/interprocess/streams/bufferstream.hpp>

#include <vector>
#include <string>
#include <iostream>


using namespace std;

int main(int argc, char **argv)
{
  static const char inp[] = "123 45 666";
  vector<char> v(inp, inp + sizeof inp  - 1);
  boost::interprocess::ibufferstream is(v.data(), v.size());
  while (!is.eof()) {
    int i = 0;
    is >> i;
    cout << i << '\n';
  }
  return 0;
}

Upvotes: 0

Simon Richter
Simon Richter

Reputation: 29618

You'd define a streambuf subclass wrapping the vector, and pass an instance of that to the istream constructor.

If the data does not change after construction, it is sufficient to set up the data pointers using streambuf::setg(); the default implementation for the other members does the right thing:

template<typename CharT, typename TraitsT = std::char_traits<CharT> >
class vectorwrapbuf : public std::basic_streambuf<CharT, TraitsT> {
public:
    vectorwrapbuf(std::vector<CharT> &vec) {
        setg(vec.data(), vec.data(), vec.data() + vec.size());
    }
};

std::vector<char> data;
// ...
vectorwrapbuf<char> databuf(data)
std::istream is(&databuf);

If you need anything more advanced than that, override the streambuf::underflow method.

Upvotes: 38

ZunTzu
ZunTzu

Reputation: 7772

using Boost:

#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/array.hpp>
using namespace boost::iostreams;

basic_array_source<char> input_source(&my_vector[0], my_vector.size());
stream<basic_array_source<char> > input_stream(input_source);

or even simpler:

#include <boost/interprocess/streams/bufferstream.hpp>
using namespace boost::interprocess;

bufferstream input_stream(&my_vector[0], my_vector.size());

Upvotes: 16

Liam M
Liam M

Reputation: 5432

You'd could get away with simply building a class that implements the >> operator like a stream, something like this:

template<class _ITy>
class RangeStreamLite
{
private:

    _ITy Begin;
    _ITy End;
    _ITy Next;

public:

    RangeStreamLite(_ITy begin, _ITy end) :
        Begin(begin),
        End(end),
        Next(begin)
    {
        // Do nothing.
    }

    template<class _OTy>
    RangeStreamLite& operator>>(_OTy& out)
    {
        out = *Next;
        ++Next;
        return *this;
    }


    void reset()
    {
        Next = Begin;
    }
};

This is a 'quick and dirty' solution, a 'stream lite', it isn't really a stream in the proper sense but it works when all you require is a superficial stream-like device. To properly create a custom stream is a little more complicated, and would require you to inherit from std::streambuf and implement the necessary features. Here are a few links worth a look:

Upvotes: 3

Michael Anderson
Michael Anderson

Reputation: 73570

Adapting the answer from Get an istream from a char* and assuming this is what you're trying to do:

// Forward declarations
std::vector<char> my_create_buffer();
void my_consume_buffer( std::istream & is );

// What you want to be able to write
std::vector<char> buffer = my_create_buffer();
my_consume_buffer( wrap_vector_as_istream(buffer) );

You can then create the wrap_vector_as_istream like this (untested though) :

#include <iostream>
#include <istream>
#include <streambuf>
#include <string>

struct wrap_vector_as_istream : std::streambuf
{
    wrap_vector_as_istream(std::vector<char> & vec ) {
        this->setg(&vec[0], &vec[0], &vec[0]+vec.size() );
    }
};

One thing to be aware of though. The object you've created contains pointers into the vectors memory. So if you add or remove values to the vector while having this wrapper floating around, then you're heading for a crash.

(Oh and if you up vote me, please up vote the post I've adapted this from.)

Upvotes: 5

Bj&#246;rn Pollex
Bj&#246;rn Pollex

Reputation: 76856

You will have to write a custom stream-implementation that inherits from istream. This can easily be done using Boost.Iostreams - all you'd have to do is implement a simple Source.

Upvotes: -1

Related Questions