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?
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?
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.
&*vec.end()
can be replaced with vec.data() + vec.size()
. –
Capriccio std::istream
doesn't free the pointer. Better create the streambuffer on stack. –
Foreground istream
subclass that contains the streambuf
-- note that in this case, the (protected) no-argument istream
constructor and the init
method need to be used as base classes are constructed before data members. –
Capriccio vector
inside the vectorwrapbuf
. –
Capriccio 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());
bufferstream input_stream(my_vector.date(), my_vector.size());
–
Conjoined 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.)
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:
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;
}
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.
© 2022 - 2024 — McMap. All rights reserved.
&v[0]
and&v[0]+v.size()
as the arguments tomembuf
. – Contentment