28

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?

Björn Pollex
  • 70,106
  • 28
  • 177
  • 265
GabiMe
  • 16,471
  • 25
  • 71
  • 106
  • Its not entirely clear what you want to be able to do. Can you give an example of the code that you would like to be able to write? – Michael Anderson Jan 11 '12 at 06:45
  • 1
    I use a library that expects an istream to consume. But I only have a vector at hand, hence I need to wrap it somehow – GabiMe Jan 11 '12 at 06:48
  • Does this suffice: http://stackoverflow.com/questions/4991697/wraps-a-vectorunsigned-char-inside-a-stream – Michael Anderson Jan 11 '12 at 06:53
  • It's still not clear what you want to do. Do you want to read a std::vector from a std::istream? – Gerald Jan 11 '12 at 06:54
  • Your other option if you dont want to copy is : http://stackoverflow.com/questions/7781898/get-an-istream-from-a-char, and use `&v[0]` and `&v[0]+v.size()` as the arguments to `membuf`. – Michael Anderson Jan 11 '12 at 06:55
  • Related: http://stackoverflow.com/questions/1448467/ http://stackoverflow.com/questions/1494182/ – Nemo Jan 11 '12 at 06:57

6 Answers6

34

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.

andrewdotn
  • 27,806
  • 6
  • 80
  • 110
Simon Richter
  • 26,160
  • 1
  • 38
  • 59
16

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());
ZunTzu
  • 5,778
  • 2
  • 26
  • 37
  • I like the second one with a slightly modification for more C++'ish code: `bufferstream input_stream(my_vector.date(), my_vector.size());` – stoycho Feb 07 '20 at 09:16
5

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.)

Community
  • 1
  • 1
Michael Anderson
  • 61,385
  • 7
  • 119
  • 164
3

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:

Community
  • 1
  • 1
Liam M
  • 5,028
  • 2
  • 37
  • 52
0

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;
}
maxschlepzig
  • 27,589
  • 9
  • 109
  • 146
-1

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.

Björn Pollex
  • 70,106
  • 28
  • 177
  • 265
  • 2
    Downvote because indirection/linking to solutions instead of providing them is against the concept of Stack Overflow. – Catskul Jun 21 '16 at 23:12