1

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();
}
Mr Lister
  • 45,515
  • 15
  • 108
  • 150
rveale
  • 66
  • 1
  • 10
  • The class you're trying to make is pretty close to what `strstream` is doing, except that it's not a `vector` internally. Why not just use it? – Adi Levin Feb 11 '16 at 11:11
  • @AdiLevin strstream has been obsolete since C++98. Nearly 20 (twenty) years now. Please don't recommend it – sehe Feb 11 '16 at 12:52
  • `strstream has been deprecated since C++98, std::stringstream and boost::iostreams::array are the recommended replacements.` [source](http://en.cppreference.com/w/cpp/io/strstream#Notes) – Fredrick Gauss Feb 11 '16 at 12:56
  • The reason I'm not just using a stringstream or iostreams::array directly is that I want the user to have access to an object that has other capabilities than just input output. (e.g. he can call other functions of it). In other words, leave the open/close/>>/< – rveale Feb 11 '16 at 14:25

2 Answers2

1

Here are three takes:

Inheritance, pretty generic

Live On Coliru

#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]

Simpler, no inheritance

Live On Coliru

#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]

Bidirectional:

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.

Live On Coliru

#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!

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Dear @sehe, Thank you for the great answer, let me work through it. I think it has pointed me in the right direction. Just briefly, it looks like the example with both in and out fixes the size. I think there is some function that is called when there is an overflow(), would I overload that function and resize() the vector at that point? Also, I guess I would overload operator>> in the same way (notice in my example program I extract the string using >>, I would like to do that as if it were a file, e.g. f >> mystring >> myint >> mydouble;) – rveale Feb 11 '16 at 14:17
  • You could, but (a) then your stream becomes an ordinary stringstream. (b) you'll need to reset the stream too, and this may lead to surprising/undefined behaviour if references are already held – sehe Feb 11 '16 at 14:27
  • `operator>>` already works in the third take. Of course you can just construct an istream on demand, so you can use back_insert_device first, then an `array_source` stream. This will work _unless_ you need full-duplex (concurrent/interleaved input/output) – sehe Feb 11 '16 at 14:29
  • Hm I don't see any current need for full duplex, so I'll leave that for later. I do want it to be have just like an ordinary stringstream...that's the whole point. The only difference is I want to also be able to call other functions (that have access to stringstreams internal vector) – rveale Feb 11 '16 at 14:53
  • Anyway I'll accept it and ask another question if I have more specific requirements or run into another wall. Thankyou, Sehe – rveale Feb 11 '16 at 15:18
0

For the record, I ended up doing something much more ugly that involves lots of copying .I keep around my own memory and then only convert parts of it to stringstream when I need to use overloaded operators like stream extraction etc..

//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 \0 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);

  }

};

An example program is then as follows (ignore dangerously scanf to string.data haha...just an example.):

#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");
    }



}
rveale
  • 66
  • 1
  • 10