使用内部向量成员创建 class 作为流访问 class 实例的容器
Create class using internal vector member as the container for stream access to class instances
先谢过愿意看的人的时间
我想做一个简单的 class 允许所有流接口,但只 reads/writes 到存储在 class 中的简单 std::vector。在我尝试自己重写所有内容,然后尝试从 basic_stream 派生之后,在我看来,使用 boost::iostreams 可以最大限度地减少我需要重写的代码量。例如:这是我想做的,但我希望我的 class 像 os 一样被使用(因此我尝试从 boost::iostreams::stream 派生):http://theboostcpplibraries.com/boost.iostreams-devices
这里有一个"first try",我尝试继承stream和stream_buffer(不知道有没有必要)。我想要的只是让流操作员都使用 std::vector<char>
数据作为容器。
//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()
{
}
};
一个示例使用程序是:
#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();
}
这是三个镜头:
继承,非常通用
#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());
}
打印:
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());
}
具有相同的输出:
OUTPUT: [YOLO 0x2a]
双向:
另一个也有 istream 功能。注意这个固定容量(为了方便):
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: [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: [ ]
您会注意到这是源代码的第一个 KB!
郑重声明,我最终做了一些更丑陋的事情,涉及大量复制。我保留自己的记忆,然后只在需要使用重载运算符(如流提取等)时将其部分转换为字符串流。 .
//File memfile3.h
#pragma once
#include <algorithm>
//#include <stringstream>
#include <cstdlib>
#include <cstdio>
#include <sstream>
#include <utility_functs.h>
#include <boost/mpi.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/filesystem.hpp>
#include <fstream>
#include <vector>
#include <memory>
//REV: this is same as std::stringstream, common impl.
//I want to ignore cases where it might have [=10=] in it if it is e.g. binary right? It won't break stringstream...?
//In case where I am accessing it with binary, I want access to the original stream...super wasteful copying.
//Problem is that I want all the user accesses on ssfile to modify ME, not it??! Haha yea, whatever though. Works.
//REV: WHat if I want to append at the end? Do I want to overwrite it (I assume so).
struct memfile
{
std::vector<char> filedata;
std::string filename;
size_t raccesses=0;
size_t waccesses=0;
//REV: Keep track if I'm being accessed? Make sure not more than one at a time for writing?
void waccess()
{
if(waccesses > 0)
{
fprintf(stderr, "Opening for simultaneous writes, big error!\n");
exit(1);
}
++waccesses;
}
void wclosed()
{
if(waccesses==0)
{
fprintf(stderr, "ERROR in closed for memfile, already 0 accesses references...\n");
exit(1);
}
--waccesses;
}
void raccess()
{
++raccesses;
}
void rclosed()
{
if(raccesses==0)
{
fprintf(stderr, "ERROR in closed for memfile, already 0 accesses references...\n");
exit(1);
}
--raccesses;
}
~memfile()
{
//Do all the natural stuff, delete the vector etc.? Need to do otherwise?
if( raccesses != 0 )
{
fprintf(stderr, "REV: Massive error, I'm in desctructor of memfile, but there is still a readaccess pointer to me...\n");
exit(1);
}
//Do all the natural stuff, delete the vector etc.? Need to do otherwise?
if( waccesses != 0 )
{
fprintf(stderr, "REV: Massive error, I'm in desctructor of memfile, but there is still a writeaccess pointer to me...\n");
exit(1);
}
//~filedata();
//~filename();
}
memfile()
: filename("ERRORFNAME")
{
}
//REV: This can't be right...? I want this on the PTR side I assume? Oh well.
//If I'm writing out, I can specify to overwrite or not?
memfile( const std::string& fname, const bool& fromfile=false )
{
filename = fname;
//Read data from file if I specify to do so.
if( fromfile )
{
std::streampos fileSize;
std::fstream file;
file = std::fstream(fname, std::fstream::in | std::ios::binary );
// get its size:
file.seekg(0, std::ios::end);
fileSize = file.tellg();
file.seekg(0, std::ios::beg);
// read the data:
filedata.resize(fileSize);
file.read(filedata.data(), fileSize);
//REV: what the heck, close it since I shouldn't need it anymore. I will synch/overwrite it later if I want.
file.close();
}
}
//For serialization, required to send across boost.
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & filename;
ar & filedata;
}
};
struct memfile_ptr
{
//Could use shared but not guaranteed that other side will also allocate with shared_ptr so no-go heh;
//Hope like hell other side will keep it open
memfile* mfile = NULL;
size_t readpos=0;
size_t writepos=0;
bool failstate=false; //stream had a problem due to user function, e.g. trying to read incorrect type or something? Or going past end of file.
bool badstate=false; //stream has a problem, memory deallocated or something?
bool eofstate=false; //EOF
bool goodstate=true;
bool fromfile=false; //was it originally read from a file (locally?). We might want to write it back to close it...?
void clear()
{
failstate=false;
badstate=false;
eofstate=false;
goodstate=true;
}
void reset()
{
clear();
readpos=0;
writepos=0;
}
memfile_ptr()
{
reset();
}
memfile_ptr( memfile& mf )
{
reset();
open( mf );
}
void open( memfile& mf )
{
mf.waccess();
mf.raccess();
mfile = &mf;
//Sets all flags
}
~memfile_ptr()
{
close();
}
void close()
{
mfile->wclosed();
mfile->rclosed();
//Close doesn't really do anything, just reset pointer to NULL.
//There should be no buffered changes.
mfile = NULL;
}
bool fail()
{
if( failstate )
{
return true;
}
return false;
}
bool bad()
{
if(badstate)
{
return true;
}
return false;
}
bool eof()
{
if( readpos == mfile->filedata.size() )
{
return true;
}
else
{
return false;
}
if(eofstate)
{
return true;
}
return false;
}
bool good()
{
if( failstate || eofstate || badstate )
{
return false;
}
return true;
//REV: No, check if some state is fine, based on if user has tried to use one of the stream guys but it failed heh.
//user hasn't gotten EOF yet maybe? They need to "check" it?
/*if( readptr < filedata.size() )
{
return true;
}
else
{
return false;
}*/
}
void tofile( const std::string& fname )
{
std::ofstream ofs;
//Is default to append, or to overwrite?
//REV: CHANGE TO OVERWRITE JUST IN CASE?!?!
open_ofstream( fname, ofs, std::ios::binary | std::ios::trunc ); //Will this write binary properly?
ofs.write( mfile->filedata.data(), mfile->filedata.size() );
close_ofstream( ofs );
return;
}
//REV: This returns everything. I want to get "from now" type thing?
//REV: This returns "from readpos"
std::string getdata()
{
//return std::string(mfile->filedata.begin()+readpos, mfile->filedata.end() );
return std::string(mfile->filedata.begin(), mfile->filedata.end() );
//return _ss.str();
}
std::string getnextdata()
{
//return std::string(mfile->filedata.begin()+readpos, mfile->filedata.end() );
return std::string(mfile->filedata.begin()+readpos, mfile->filedata.end() );
//return _ss.str();
}
template<typename T>
memfile_ptr& operator<<(const T& t)
{
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
size_t writesize = tmpstr.size();
//<= because e.g. if there is already [C], and writepos == 0, and writesize == 1, offset == 1,
//I will just overwrite it.
if( writepos + writesize <= offset )
{
//No need to resize
}
else
{
size_t newend = writepos + writesize;
mfile->filedata.resize( newend );
}
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+writepos );
writepos += writesize;
return *this;
/*
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
mfile->filedata.resize( mfile->filedata.size() + tmpstr.size() );
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+offset );
//mfile->filedata.push_back( tmpstr.begin(), tmpstr.end() );
return *this;*/
}
memfile_ptr& operator<<(std::ostream& (*t)(std::ostream&))
{
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
size_t writesize = tmpstr.size();
if( writepos + writesize <= offset )
{
//No need to resize
}
else
{
size_t newend = writepos + writesize;
mfile->filedata.resize( newend );
}
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+writepos );
writepos += writesize;
return *this;
/*
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
mfile->filedata.resize( mfile->filedata.size() + tmpstr.size() );
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+offset );
//mfile->filedata.push_back( tmpstr.begin(), tmpstr.end() );
return *this;
//_ss << t;
//return *this;
*/
}
memfile_ptr& operator<<(std::ios& (*t)(std::ios&))
{
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
size_t writesize = tmpstr.size();
if( writepos + writesize <= offset )
{
//No need to resize
}
else
{
size_t newend = writepos + writesize;
mfile->filedata.resize( newend );
}
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+writepos );
writepos += writesize;
return *this;
}
memfile_ptr& operator<<(std::ios_base& (*t)(std::ios_base&))
{
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
size_t writesize = tmpstr.size();
if( writepos + writesize <= offset )
{
//No need to resize
}
else
{
size_t newend = writepos + writesize;
mfile->filedata.resize( newend );
}
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+writepos );
writepos += writesize;
return *this;
}
//None of the other states are set?
template<typename T>
memfile_ptr& operator>>(T& t)
{
std::stringstream _ss( std::string( mfile->filedata.begin()+readpos, mfile->filedata.end() ) );
size_t p1 = _ss.tellg();
_ss >> t;
//Wrap .good .bad .eof .fail etc. so user can use them equivalently.
//Write READLINE etc. functions so user can use them appropriately.
//_ss.clear() should reset all bits?? So I know for each thing if it failed, e.g. if I preiously failed to get INT bc i read in DOUBLE,
//but now I want to try again.
if( _ss.fail() )
{
failstate=true;
badstate=true;
}
if( _ss.eof() ) //check if it is true
{
eofstate=true;
badstate=true;
readpos = mfile->filedata.size();
}
if( bad() )
{
return *this;
}
size_t p2 = _ss.tellg();
size_t mv = p2-p1;
readpos += mv;
return *this;
}
//REV: I want to also be able to use FPRINTF, etc. with it. To do this, derive the string, and then reset it. It is copies, so very slow heh.
//Also, open in binary mode will make it totally different. In binary mode, I will only use my personal write/read stuff.
//What about if user wants to do write/read stuff? sscanf etc. How do we know "how far" user has gone extracting stuff? Can user "restart? Things?
//Overload:
//WRITE/READ/GOOD/GET/TELL/etc....man that is nasty. User might want to freely use seek type commands on the file, in which case...?
//I should just use a local buffer? Overload streambuf? Nah just do it my way, easiest haha.
size_t compute_new_size( const size_t& writesize )
{
size_t offset = mfile->filedata.size();
if( writepos + writesize <= offset )
{
return offset;
//No need to resize
}
else
{
size_t newend = writepos + writesize;
return newend;
}
}
std::vector<char> read( const size_t& numbytes )
{
size_t endpt = readpos + numbytes;
if( endpt >= mfile->filedata.size() )
{
failstate=true;
badstate=true;
eofstate=true;
endpt = mfile->filedata.size();
}
std::vector<char> ret( mfile->filedata.begin()+readpos, mfile->filedata.begin()+endpt );
readpos += numbytes;
return ret;
}
void write( const std::vector<char>& towrite )
{
//Write string data? Or we don't care what type it is haha. It will always write to WRITEPOS...
size_t ws= compute_new_size( towrite.size() );
mfile->filedata.resize( ws );
std::copy( towrite.data(), towrite.data()+towrite.size(), mfile->filedata.begin()+writepos );
writepos += towrite.size(); //Actually written heh.
return;
}
//Where is the "readpos" and "writepos"
template<typename... Args>
void printf(const char* fmt, Args... args )
{
size_t SPRINTF_BUFF_SIZE=1e3;
std::vector<char> buffer( SPRINTF_BUFF_SIZE );
int written = std::snprintf( buffer.data(), buffer.size(), fmt, args... );
while( written >= buffer.size() )
{
buffer.resize( buffer.size()*2 );
int written = std::snprintf( buffer.data(), buffer.size(), fmt, args... );
}
if( written < 0 )
{
//REV: some error
}
else
{
//we need to push back written characters from buffer to our location.
buffer.resize( written );
write( buffer );
}
}
//REV: Do readline functs etc.?
//REV: Would like to write something to make sure I got an int, when I got an int, etc.
//In my stream case, I don't return the actual stream...so It's all messed up.
//E.g. if user tries to get it, but it doesn't get anything, set something to FALSE for them!! Yea I need to do that.
//E.g. if they try to do s >> myint1 >> myint2, but there is only 1 int, there should only be a true thing partway through?
//And they should be able to check state to see that it failed to get second int or something. OK.
//REV: Better way??
template <typename...Ts>
int scanf( const char* fmt, Ts&&...ts )
{
std::string fmtstr = std::string(fmt);
fmtstr+="%n";
int ncharswritten = -1;
std::string buffer = getnextdata();
int nargswritten = std::sscanf(buffer.c_str() ,
fmtstr.c_str() ,
std::forward<Ts>(ts)... ,
&ncharswritten );
if( ncharswritten < 0 )
{
//Huh, something is wrong. User should check how many he "should" have written heh. Oh well.
badstate = true;
failstate = true;
}
else
{
readpos += ncharswritten;
}
//REV: This won't work if it goes past EOF, so need to handle how many were written if conspos wasn't filled becuase
//it hit EOF partway through...
return (nargswritten-1);
}
//REV: What should happen if it fails to fill one of the guys, e.g. it tries to get a DOUBLE from a STR or something?
//REV: Scan from start, if it tries to go past, it returns number anyway?
template<typename... Args>
int scanf2_REV(const char* fmt, Args... args )
{
std::string fmtstr = std::string(fmt);
fmtstr+="%n";
int conspos=-1;
std::string buffer = getnextdata();
//args.push_back( & conspos );
int numargswritten = std::sscanf(buffer.c_str(), fmtstr.c_str(), args..., &conspos);
if( conspos < 0 )
{
//Huh, something is wrong. User should check how many he "should" have written heh. Oh well.
badstate = true;
failstate = true;
}
//fprintf( stdout, "I consumed [%d] chars\n", conspos );
readpos += conspos;
//REV: This won't work if it goes past EOF, so need to handle how many were written if conspos wasn't filled becuase
//it hit EOF partway through...
return (numargswritten-1);
}
};
一个示例程序如下(忽略危险的扫描到string.data哈哈...只是一个例子。):
#include <memfile3.h>
int main()
{
//ssfile sf;
memfile mf;
memfile_ptr sf( mf );
int myint=2;
double mydouble=32.3;
std::string mystr="YOLO";
sf << myint << " " << mydouble << " " << mystr << std::endl;
fprintf(stdout, "Should contain [%s]\n", sf.getdata().c_str() );
//Get data from it? Do I need to seek from beginning?
myint=0;
mydouble=0;
std::string doubletmpstr;
mystr="ASDF";
sf >> myint >> doubletmpstr >> mystr;
//fprintf(stdout, "Contains (no change?) [%s]\n", sf.getdata().c_str() );
fprintf(stdout, "Got out: int should be 2 [%d] double (as str) should be 32.3 [%s], str should be YOLO: [%s]\n", myint, doubletmpstr.c_str(), mystr.c_str() );
fprintf(stdout, "After extraction (should be empty?) [%s]. Now adding via printf\n", sf.getdata().c_str() );
//WRiting more using printf.
sf.printf("%d %f\n", 10, 15.1);
int i2;
float f2;
sf >> i2 >> f2;
fprintf(stdout, "Should have got 10 and 15.1: [%d] [%f]\n", i2, f2 );
//I should consume?
sf.printf("%ld %ld %s\n", 222, 233, "CHAR");
fprintf(stdout, "Current unprocessed portion of file: [%s]\n", sf.getnextdata().c_str() );
int i3, i4;
std::string s3("NOTA");
sf.scanf("%d %d %s\n", &i3, &i4, s3.data() );
fprintf(stdout, "Got (=222) [%d] (=233) [%d] (=CHAR) [%s]\n", i3, i4, s3.c_str() );
fprintf(stdout, "Current unprocessed portion of file: [%s]\n", sf.getnextdata().c_str() );
fprintf(stdout, "I should have got: 11 (or 12 if include newline)\n");
//No, it's not empty. Shit.
//Try to peek?
if( sf.eof() )
{
fprintf(stdout, "Correctly EOF!\n");
}
else
{
fprintf(stdout, "Incorrectly NOT EOF!\n");
}
int a;
sf >> a;
//Should be EOF?
if( sf.eof() )
{
fprintf(stdout, "Correctly EOF!\n");
}
else
{
fprintf(stdout, "Incorrectly NOT EOF!\n");
}
}
先谢过愿意看的人的时间
我想做一个简单的 class 允许所有流接口,但只 reads/writes 到存储在 class 中的简单 std::vector。在我尝试自己重写所有内容,然后尝试从 basic_stream 派生之后,在我看来,使用 boost::iostreams 可以最大限度地减少我需要重写的代码量。例如:这是我想做的,但我希望我的 class 像 os 一样被使用(因此我尝试从 boost::iostreams::stream 派生):http://theboostcpplibraries.com/boost.iostreams-devices
这里有一个"first try",我尝试继承stream和stream_buffer(不知道有没有必要)。我想要的只是让流操作员都使用 std::vector<char>
数据作为容器。
//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()
{
}
};
一个示例使用程序是:
#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();
}
这是三个镜头:
继承,非常通用
#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());
}
打印:
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());
}
具有相同的输出:
OUTPUT: [YOLO 0x2a]
双向:
另一个也有 istream 功能。注意这个固定容量(为了方便):
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: [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: [ ]
您会注意到这是源代码的第一个 KB!
郑重声明,我最终做了一些更丑陋的事情,涉及大量复制。我保留自己的记忆,然后只在需要使用重载运算符(如流提取等)时将其部分转换为字符串流。 .
//File memfile3.h
#pragma once
#include <algorithm>
//#include <stringstream>
#include <cstdlib>
#include <cstdio>
#include <sstream>
#include <utility_functs.h>
#include <boost/mpi.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/filesystem.hpp>
#include <fstream>
#include <vector>
#include <memory>
//REV: this is same as std::stringstream, common impl.
//I want to ignore cases where it might have [=10=] in it if it is e.g. binary right? It won't break stringstream...?
//In case where I am accessing it with binary, I want access to the original stream...super wasteful copying.
//Problem is that I want all the user accesses on ssfile to modify ME, not it??! Haha yea, whatever though. Works.
//REV: WHat if I want to append at the end? Do I want to overwrite it (I assume so).
struct memfile
{
std::vector<char> filedata;
std::string filename;
size_t raccesses=0;
size_t waccesses=0;
//REV: Keep track if I'm being accessed? Make sure not more than one at a time for writing?
void waccess()
{
if(waccesses > 0)
{
fprintf(stderr, "Opening for simultaneous writes, big error!\n");
exit(1);
}
++waccesses;
}
void wclosed()
{
if(waccesses==0)
{
fprintf(stderr, "ERROR in closed for memfile, already 0 accesses references...\n");
exit(1);
}
--waccesses;
}
void raccess()
{
++raccesses;
}
void rclosed()
{
if(raccesses==0)
{
fprintf(stderr, "ERROR in closed for memfile, already 0 accesses references...\n");
exit(1);
}
--raccesses;
}
~memfile()
{
//Do all the natural stuff, delete the vector etc.? Need to do otherwise?
if( raccesses != 0 )
{
fprintf(stderr, "REV: Massive error, I'm in desctructor of memfile, but there is still a readaccess pointer to me...\n");
exit(1);
}
//Do all the natural stuff, delete the vector etc.? Need to do otherwise?
if( waccesses != 0 )
{
fprintf(stderr, "REV: Massive error, I'm in desctructor of memfile, but there is still a writeaccess pointer to me...\n");
exit(1);
}
//~filedata();
//~filename();
}
memfile()
: filename("ERRORFNAME")
{
}
//REV: This can't be right...? I want this on the PTR side I assume? Oh well.
//If I'm writing out, I can specify to overwrite or not?
memfile( const std::string& fname, const bool& fromfile=false )
{
filename = fname;
//Read data from file if I specify to do so.
if( fromfile )
{
std::streampos fileSize;
std::fstream file;
file = std::fstream(fname, std::fstream::in | std::ios::binary );
// get its size:
file.seekg(0, std::ios::end);
fileSize = file.tellg();
file.seekg(0, std::ios::beg);
// read the data:
filedata.resize(fileSize);
file.read(filedata.data(), fileSize);
//REV: what the heck, close it since I shouldn't need it anymore. I will synch/overwrite it later if I want.
file.close();
}
}
//For serialization, required to send across boost.
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & filename;
ar & filedata;
}
};
struct memfile_ptr
{
//Could use shared but not guaranteed that other side will also allocate with shared_ptr so no-go heh;
//Hope like hell other side will keep it open
memfile* mfile = NULL;
size_t readpos=0;
size_t writepos=0;
bool failstate=false; //stream had a problem due to user function, e.g. trying to read incorrect type or something? Or going past end of file.
bool badstate=false; //stream has a problem, memory deallocated or something?
bool eofstate=false; //EOF
bool goodstate=true;
bool fromfile=false; //was it originally read from a file (locally?). We might want to write it back to close it...?
void clear()
{
failstate=false;
badstate=false;
eofstate=false;
goodstate=true;
}
void reset()
{
clear();
readpos=0;
writepos=0;
}
memfile_ptr()
{
reset();
}
memfile_ptr( memfile& mf )
{
reset();
open( mf );
}
void open( memfile& mf )
{
mf.waccess();
mf.raccess();
mfile = &mf;
//Sets all flags
}
~memfile_ptr()
{
close();
}
void close()
{
mfile->wclosed();
mfile->rclosed();
//Close doesn't really do anything, just reset pointer to NULL.
//There should be no buffered changes.
mfile = NULL;
}
bool fail()
{
if( failstate )
{
return true;
}
return false;
}
bool bad()
{
if(badstate)
{
return true;
}
return false;
}
bool eof()
{
if( readpos == mfile->filedata.size() )
{
return true;
}
else
{
return false;
}
if(eofstate)
{
return true;
}
return false;
}
bool good()
{
if( failstate || eofstate || badstate )
{
return false;
}
return true;
//REV: No, check if some state is fine, based on if user has tried to use one of the stream guys but it failed heh.
//user hasn't gotten EOF yet maybe? They need to "check" it?
/*if( readptr < filedata.size() )
{
return true;
}
else
{
return false;
}*/
}
void tofile( const std::string& fname )
{
std::ofstream ofs;
//Is default to append, or to overwrite?
//REV: CHANGE TO OVERWRITE JUST IN CASE?!?!
open_ofstream( fname, ofs, std::ios::binary | std::ios::trunc ); //Will this write binary properly?
ofs.write( mfile->filedata.data(), mfile->filedata.size() );
close_ofstream( ofs );
return;
}
//REV: This returns everything. I want to get "from now" type thing?
//REV: This returns "from readpos"
std::string getdata()
{
//return std::string(mfile->filedata.begin()+readpos, mfile->filedata.end() );
return std::string(mfile->filedata.begin(), mfile->filedata.end() );
//return _ss.str();
}
std::string getnextdata()
{
//return std::string(mfile->filedata.begin()+readpos, mfile->filedata.end() );
return std::string(mfile->filedata.begin()+readpos, mfile->filedata.end() );
//return _ss.str();
}
template<typename T>
memfile_ptr& operator<<(const T& t)
{
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
size_t writesize = tmpstr.size();
//<= because e.g. if there is already [C], and writepos == 0, and writesize == 1, offset == 1,
//I will just overwrite it.
if( writepos + writesize <= offset )
{
//No need to resize
}
else
{
size_t newend = writepos + writesize;
mfile->filedata.resize( newend );
}
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+writepos );
writepos += writesize;
return *this;
/*
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
mfile->filedata.resize( mfile->filedata.size() + tmpstr.size() );
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+offset );
//mfile->filedata.push_back( tmpstr.begin(), tmpstr.end() );
return *this;*/
}
memfile_ptr& operator<<(std::ostream& (*t)(std::ostream&))
{
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
size_t writesize = tmpstr.size();
if( writepos + writesize <= offset )
{
//No need to resize
}
else
{
size_t newend = writepos + writesize;
mfile->filedata.resize( newend );
}
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+writepos );
writepos += writesize;
return *this;
/*
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
mfile->filedata.resize( mfile->filedata.size() + tmpstr.size() );
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+offset );
//mfile->filedata.push_back( tmpstr.begin(), tmpstr.end() );
return *this;
//_ss << t;
//return *this;
*/
}
memfile_ptr& operator<<(std::ios& (*t)(std::ios&))
{
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
size_t writesize = tmpstr.size();
if( writepos + writesize <= offset )
{
//No need to resize
}
else
{
size_t newend = writepos + writesize;
mfile->filedata.resize( newend );
}
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+writepos );
writepos += writesize;
return *this;
}
memfile_ptr& operator<<(std::ios_base& (*t)(std::ios_base&))
{
std::stringstream _ss;
_ss << t;
std::string tmpstr = _ss.str();
size_t offset = mfile->filedata.size();
size_t writesize = tmpstr.size();
if( writepos + writesize <= offset )
{
//No need to resize
}
else
{
size_t newend = writepos + writesize;
mfile->filedata.resize( newend );
}
std::copy( tmpstr.data(), tmpstr.data()+tmpstr.size(), mfile->filedata.begin()+writepos );
writepos += writesize;
return *this;
}
//None of the other states are set?
template<typename T>
memfile_ptr& operator>>(T& t)
{
std::stringstream _ss( std::string( mfile->filedata.begin()+readpos, mfile->filedata.end() ) );
size_t p1 = _ss.tellg();
_ss >> t;
//Wrap .good .bad .eof .fail etc. so user can use them equivalently.
//Write READLINE etc. functions so user can use them appropriately.
//_ss.clear() should reset all bits?? So I know for each thing if it failed, e.g. if I preiously failed to get INT bc i read in DOUBLE,
//but now I want to try again.
if( _ss.fail() )
{
failstate=true;
badstate=true;
}
if( _ss.eof() ) //check if it is true
{
eofstate=true;
badstate=true;
readpos = mfile->filedata.size();
}
if( bad() )
{
return *this;
}
size_t p2 = _ss.tellg();
size_t mv = p2-p1;
readpos += mv;
return *this;
}
//REV: I want to also be able to use FPRINTF, etc. with it. To do this, derive the string, and then reset it. It is copies, so very slow heh.
//Also, open in binary mode will make it totally different. In binary mode, I will only use my personal write/read stuff.
//What about if user wants to do write/read stuff? sscanf etc. How do we know "how far" user has gone extracting stuff? Can user "restart? Things?
//Overload:
//WRITE/READ/GOOD/GET/TELL/etc....man that is nasty. User might want to freely use seek type commands on the file, in which case...?
//I should just use a local buffer? Overload streambuf? Nah just do it my way, easiest haha.
size_t compute_new_size( const size_t& writesize )
{
size_t offset = mfile->filedata.size();
if( writepos + writesize <= offset )
{
return offset;
//No need to resize
}
else
{
size_t newend = writepos + writesize;
return newend;
}
}
std::vector<char> read( const size_t& numbytes )
{
size_t endpt = readpos + numbytes;
if( endpt >= mfile->filedata.size() )
{
failstate=true;
badstate=true;
eofstate=true;
endpt = mfile->filedata.size();
}
std::vector<char> ret( mfile->filedata.begin()+readpos, mfile->filedata.begin()+endpt );
readpos += numbytes;
return ret;
}
void write( const std::vector<char>& towrite )
{
//Write string data? Or we don't care what type it is haha. It will always write to WRITEPOS...
size_t ws= compute_new_size( towrite.size() );
mfile->filedata.resize( ws );
std::copy( towrite.data(), towrite.data()+towrite.size(), mfile->filedata.begin()+writepos );
writepos += towrite.size(); //Actually written heh.
return;
}
//Where is the "readpos" and "writepos"
template<typename... Args>
void printf(const char* fmt, Args... args )
{
size_t SPRINTF_BUFF_SIZE=1e3;
std::vector<char> buffer( SPRINTF_BUFF_SIZE );
int written = std::snprintf( buffer.data(), buffer.size(), fmt, args... );
while( written >= buffer.size() )
{
buffer.resize( buffer.size()*2 );
int written = std::snprintf( buffer.data(), buffer.size(), fmt, args... );
}
if( written < 0 )
{
//REV: some error
}
else
{
//we need to push back written characters from buffer to our location.
buffer.resize( written );
write( buffer );
}
}
//REV: Do readline functs etc.?
//REV: Would like to write something to make sure I got an int, when I got an int, etc.
//In my stream case, I don't return the actual stream...so It's all messed up.
//E.g. if user tries to get it, but it doesn't get anything, set something to FALSE for them!! Yea I need to do that.
//E.g. if they try to do s >> myint1 >> myint2, but there is only 1 int, there should only be a true thing partway through?
//And they should be able to check state to see that it failed to get second int or something. OK.
//REV: Better way??
template <typename...Ts>
int scanf( const char* fmt, Ts&&...ts )
{
std::string fmtstr = std::string(fmt);
fmtstr+="%n";
int ncharswritten = -1;
std::string buffer = getnextdata();
int nargswritten = std::sscanf(buffer.c_str() ,
fmtstr.c_str() ,
std::forward<Ts>(ts)... ,
&ncharswritten );
if( ncharswritten < 0 )
{
//Huh, something is wrong. User should check how many he "should" have written heh. Oh well.
badstate = true;
failstate = true;
}
else
{
readpos += ncharswritten;
}
//REV: This won't work if it goes past EOF, so need to handle how many were written if conspos wasn't filled becuase
//it hit EOF partway through...
return (nargswritten-1);
}
//REV: What should happen if it fails to fill one of the guys, e.g. it tries to get a DOUBLE from a STR or something?
//REV: Scan from start, if it tries to go past, it returns number anyway?
template<typename... Args>
int scanf2_REV(const char* fmt, Args... args )
{
std::string fmtstr = std::string(fmt);
fmtstr+="%n";
int conspos=-1;
std::string buffer = getnextdata();
//args.push_back( & conspos );
int numargswritten = std::sscanf(buffer.c_str(), fmtstr.c_str(), args..., &conspos);
if( conspos < 0 )
{
//Huh, something is wrong. User should check how many he "should" have written heh. Oh well.
badstate = true;
failstate = true;
}
//fprintf( stdout, "I consumed [%d] chars\n", conspos );
readpos += conspos;
//REV: This won't work if it goes past EOF, so need to handle how many were written if conspos wasn't filled becuase
//it hit EOF partway through...
return (numargswritten-1);
}
};
一个示例程序如下(忽略危险的扫描到string.data哈哈...只是一个例子。):
#include <memfile3.h>
int main()
{
//ssfile sf;
memfile mf;
memfile_ptr sf( mf );
int myint=2;
double mydouble=32.3;
std::string mystr="YOLO";
sf << myint << " " << mydouble << " " << mystr << std::endl;
fprintf(stdout, "Should contain [%s]\n", sf.getdata().c_str() );
//Get data from it? Do I need to seek from beginning?
myint=0;
mydouble=0;
std::string doubletmpstr;
mystr="ASDF";
sf >> myint >> doubletmpstr >> mystr;
//fprintf(stdout, "Contains (no change?) [%s]\n", sf.getdata().c_str() );
fprintf(stdout, "Got out: int should be 2 [%d] double (as str) should be 32.3 [%s], str should be YOLO: [%s]\n", myint, doubletmpstr.c_str(), mystr.c_str() );
fprintf(stdout, "After extraction (should be empty?) [%s]. Now adding via printf\n", sf.getdata().c_str() );
//WRiting more using printf.
sf.printf("%d %f\n", 10, 15.1);
int i2;
float f2;
sf >> i2 >> f2;
fprintf(stdout, "Should have got 10 and 15.1: [%d] [%f]\n", i2, f2 );
//I should consume?
sf.printf("%ld %ld %s\n", 222, 233, "CHAR");
fprintf(stdout, "Current unprocessed portion of file: [%s]\n", sf.getnextdata().c_str() );
int i3, i4;
std::string s3("NOTA");
sf.scanf("%d %d %s\n", &i3, &i4, s3.data() );
fprintf(stdout, "Got (=222) [%d] (=233) [%d] (=CHAR) [%s]\n", i3, i4, s3.c_str() );
fprintf(stdout, "Current unprocessed portion of file: [%s]\n", sf.getnextdata().c_str() );
fprintf(stdout, "I should have got: 11 (or 12 if include newline)\n");
//No, it's not empty. Shit.
//Try to peek?
if( sf.eof() )
{
fprintf(stdout, "Correctly EOF!\n");
}
else
{
fprintf(stdout, "Incorrectly NOT EOF!\n");
}
int a;
sf >> a;
//Should be EOF?
if( sf.eof() )
{
fprintf(stdout, "Correctly EOF!\n");
}
else
{
fprintf(stdout, "Incorrectly NOT EOF!\n");
}
}