使用内部向量成员创建类作为流类访问类实例的容器

时间:2016-02-11 11:05:13

标签: c++ c++11 boost std boost-iostreams

提前感谢任何愿意看这个的人。

我想创建一个允许所有流接口的简单类,但只读取/写入存储在类中的简单std :: vector。在我试图重新编写所有内容之后,在尝试从basic_stream派生之后,我觉得使用boost :: iostreams可以最大限度地减少重写所需的代码量。例如:这就是我想要做的,但我希望我的类在那里像os一样使用(因此我尝试从boost :: iostreams :: stream派生):http://theboostcpplibraries.com/boost.iostreams-devices

这是&#34;第一次尝试&#34;,我尝试从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();
}

2 个答案:

答案 0 :(得分:1)

以下是三个要点:

继承,非常通用

<强> 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());
}

打印:

OUTPUT: [YOLO 0x2a]

更简单,没有继承

<强> 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());
}

输出相同:

OUTPUT: [YOLO 0x2a]

双向:

使用istream功能的另一种看法。请注意,此修复了容量(为方便起见):

  

注意,如果您推送的容量超过容量,则流状态会变差。您将需要处理错误和/或clear()州。

<强> 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: [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: [                 ]

您会注意到这是源代码的第一个千字节!

答案 1 :(得分:0)

为了记录,我最终做了一些更丑陋的事情,涉及大量的复制。我保留自己的内存,然后当我需要使用像流提取等重载运算符时,只将它的一部分转换为字符串流。 / p>

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

  }

};

示例程序如下(忽略危险的scanf到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");
    }



}