Appreciation in advance for the time of anyone who is willing to look at this.
I'd like to make a simple class that allows all stream interfaces, but only reads/writes to a simple std::vector stored in the class. It seems to me after trying to re-write everything myself, then trying to derive from basic_stream, that using boost::iostreams minimizes the amount of code I will need to re-write. For example: this is what I want to do, but I want my class to be used like os in there (thus why I try to derive from boost::iostreams::stream): http://theboostcpplibraries.com/boost.iostreams-devices
Here is a "first try", in which I try to inherit from stream and stream_buffer (don't know if necessary). All I want is for stream operators to all use the std::vector<char>
data as the container.
//File: memfile2.h
#pragma once
#include <algorithm> // copy, min
#include <iosfwd> // streamsize
#include <boost/iostreams/categories.hpp> // source_tag
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/stream_buffer.hpp>
//REV: use boost iostreams to let user write to a local vector of chars
//as a memory file.
//REV: Or just "get" one from the pointer, i.e. have a mem_ptr which "opens" a file.
struct mfile : public boost::iostreams::stream<boost::iostreams::array_source>, boost::iostreams::stream_buffer
{
std::vector<char> data;
mfile()
: boost::iostreams::stream<boost::iostreams::array_source>( data ),
boost::iostreams::stream_buffer()
{
}
void other_funct()
{
}
};
An example use program would be:
#include <memfile2.h>
int main()
{
mfile f;
f << "YOLO";
std::string fromf;
//f.seekg(0, BOOST_IOS::beg);
f >> fromf;
fprintf(stdout, "OUTPUT: [%s]\n", fromf.c_str());
f.other_funct();
}
Here are three takes:
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <iostream>
#include <iomanip>
#include <fstream>
template <typename CharT = char, typename CharTraits = std::char_traits<CharT>,
typename Buffer = std::vector<CharT>,
typename Base = boost::iostreams::stream<boost::iostreams::back_insert_device<Buffer> >
>
struct basic_fixed_stream : private Buffer, public Base {
basic_fixed_stream() : Buffer(), Base(*static_cast<Buffer*>(this)) {}
std::string to_string() const {
flush(*this);
return { Buffer::begin(), Buffer::end() };
}
};
using fixed_stream = basic_fixed_stream<char>;
int main()
{
fixed_stream f;
f << "YOLO " << std::showbase << std::hex << std::setfill('0') << 42;
std::string fromf = f.to_string();
fprintf(stdout, "OUTPUT: [%s]\n", fromf.c_str());
}
Prints:
OUTPUT: [YOLO 0x2a]
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <iostream>
#include <iomanip>
#include <fstream>
struct fixed_stream {
template <typename OS=std::ostream> friend fixed_stream& operator<<(fixed_stream& os, OS&(*manip)(OS&)) {
os._stream << manip;
return os;
}
template <typename T> friend fixed_stream& operator<<(fixed_stream& os, T const& v) {
os._stream << v;
return os;
}
std::string to_string() const {
flush(_stream);
return { _buffer.begin(), _buffer.end() };
}
operator std::ostream&() { return _stream; }
private:
using buffer_t = std::vector<char>;
buffer_t _buffer;
boost::iostreams::stream<boost::iostreams::back_insert_device<buffer_t> > _stream { _buffer };
};
int main()
{
fixed_stream f;
f << "YOLO " << std::showbase << std::hex << std::setfill('0') << 42;
std::string fromf = f.to_string();
fprintf(stdout, "OUTPUT: [%s]\n", fromf.c_str());
}
With the same output:
OUTPUT: [YOLO 0x2a]
Another take with istream functionality too. Note this fixes the capacity (for convenience):
Note, if you push more than capacity input, the stream state goes bad. You will want to handle errors and/or
clear()
the state.
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/array.hpp>
#include <iostream>
#include <iomanip>
#include <fstream>
template <typename CharT = char, typename CharTraits = std::char_traits<CharT>,
typename Buffer = std::vector<CharT>,
typename Base = boost::iostreams::stream<boost::iostreams::array>
>
struct basic_fixed_stream : private Buffer, public Base {
basic_fixed_stream(size_t capacity = 1024) : Buffer(capacity), Base(this->data(), this->size()) {}
using Base::clear;
std::string to_string() const {
flush(*this);
return { Buffer::begin(), Buffer::end() };
}
};
using fixed_stream = basic_fixed_stream<char>;
int main()
{
{
fixed_stream f;
f << "YOLO " << std::showbase << std::hex << std::setfill('0') << 42;
std::string fromf = f.to_string();
fprintf(stdout, "OUTPUT: [%s]\n", fromf.c_str());
}
{
fixed_stream f;
{
std::ifstream ifs("main.cpp");
f << ifs.rdbuf();
}
f.clear();
f.seekg(0);
std::string line;
while (getline(f, line))
fprintf(stdout, "OUTPUT: [%s]\n", line.c_str());
}
}
Output:
OUTPUT: [YOLO 0x2a]
OUTPUT: [#include <iostream>]
OUTPUT: [#include <boost/spirit/home/x3.hpp>]
OUTPUT: [#include <boost/fusion/adapted/std_tuple.hpp>]
OUTPUT: [#include <boost/spirit/home/x3/binary.hpp>]
OUTPUT: []
OUTPUT: [namespace x3 = boost::spirit::x3;]
OUTPUT: []
OUTPUT: [namespace hessian {]
OUTPUT: []
OUTPUT: [ typedef std::string string_t;]
OUTPUT: []
OUTPUT: [ namespace parser {]
OUTPUT: []
OUTPUT: [ struct bstring : x3::parser<bstring> {]
OUTPUT: [ using attribute_type = hessian::string_t;]
OUTPUT: []
OUTPUT: [ // string ::= s b1 b0 <utf8-data> string]
OUTPUT: [ // ::= S b1 b0 <utf8-data>]
OUTPUT: [ // ::= [x00-x1f] <utf8-data>]
OUTPUT: [ // NOTE: The length means number of UTF16 characters but the content is given in UTF8 characters!]
OUTPUT: [ template <typename It, typename Ctx, typename Attr>]
OUTPUT: [ bool parse(It& f, It const& l, Ctx&, x3::unused_type, Attr& attr) const {]
OUTPUT: [ auto saved = f;]
OUTPUT: [ char type;]
OUTPUT: [ size_t len;]
OUTPUT: [ auto tied = std::tie(type, len);]
OUTPUT: []
OUTPUT: [ while (x3::parse(f,l,x3::char_("sS") >> x3::big_word,tied)) {]
OUTPUT: [ ]
You'll notice that's the first kilobyte of the source code!