Write a iostream iterator

Hi!

I have a class Source that is is reading from a file line by line. I then have the class VersionData that should parse the file version in the Source class dir in method process. I would now like add an iterator class to the Source class that the VersionData class could use when parsing the file.
I am having some difficulty to figure out how to start, all examples on the net and in the books is talking about how to write an iterator if using a Container class but I would like to write an iterator for the ifstream class and the iterator should step one line when the post-increment or pre-increment operations are called.

/Mansz
#include <iostream>
#include <fstream>
#include <typeinfo>

#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>

#include "procsource.h"
#include "procdata.h"

using namespace std;
namespace bf = boost::filesystem;

Source::Source()
{
    this->dir = new bf::path("/home/mansz/test/dir/");
    this->stream = new ifstream();
}

Source::~Source()
{
    delete this->dir;
    delete this->stream;
}
bool Source::loadFile(const bf::path& filePath)
{
    bf::path source = bf::path(this->dir->string() + filePath.string());

    int i = 0;
    if ( !bf::exists(source) ) return false;

    readFile_label:
    this->stream->open(source.string().c_str());
    if( !this->stream->is_open() ) {
        if( ++i < 10 ) {
            goto readFile_label;
        } else {
            return false;
        }
    }

    return true;
}

bool Source::closeFile()
{
    bool res = true;
    if ( this->stream != NULL ) {
        this->stream->close();
        res = !this->stream->fail();
        this->stream = NULL;
    }
    return res;
}

const string& Source::readLine()
{
    return this->line;
}

bool Source::endOfSource()
{
    if ( this->stream->eof() ) {
        return true;
    }

    if ( this->stream != NULL ) {
        getline(*this->stream, this->line);
    } else {
        return true;
    }

    if ( this->stream->fail() ) {
        return true;
    }

    return false;
}
********************************************
#include <iostream>

#include <boost/filesystem.hpp>
#include <boost/regex.hpp>

#include "versiondata.h"
#include "utility.h"

using namespace std;
namespace bf = boost::filesystem;

VersionData::VersionData()
{
    // TODO Auto-generated constructor stub

}

VersionData::~VersionData()
{

}

void VersionData::process(Source & source)
{
    if (source.loadFile(bf::path("version"))
        && !source.endOfSource()) {
        const string line = source.readLine();
        boost::regex regExp(" ");
        StringList* list = split(line, regExp);
        //out_of_range exception will be thrown from list if index is out of range
        string version = list->at(2);
        if ( !version.empty() ) {
            this->data = string(version);
        } else {
            cout << "failed to retrive data" << endl;
            this->data = "unknown";
        }
        delete list;
    } else {
        cout << "failed to open file" << endl;
        this->data = string("unknown");
    }
    source.closeFile();
}

void VersionData::print() const
{
    cout << "Version: " << this->data << endl;
}

Open in new window

manszAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

evilrixSenior Software Engineer (Avast)Commented:
>> I would like to write an iterator for the ifstream class and the iterator should step one line
You know such an iterator already exists as part of the standard library right?

http://www.cplusplus.com/reference/std/iterator/istream_iterator/
http://www.cplusplus.com/reference/std/iterator/ostream_iterator/

Although the istream iterator behaves much the same as calling the << operator, which means it is delimited by all white space, so I guess that's not what you are after?

If you want one line specific you can simple wrap a class around a call to geline(). Very quick example (not compiled) below


struct MyItr
{
   MyItr(istream is) : is_(is) {}

  string operator ++()
  {
      string s;
      getline(is, s);
      return s;
  }

  string operator ++(int)
  {
       ++(*this);
  }

   istream is_;
};

Open in new window

itsmeandnobodyelseCommented:
Or that (not compiled either):

class SourceIterator
{
     ifstream ifs;
     string    curline;
public:
     SourceIterator(const string & sourceFile) : ifs(sourceFile.c_str()) 
     { if (ifs) getline(ifs, curline); }
     ~SourceIterator() { if (ifs.is_open()) ifs.close(); }
     SourceIterator & operator++() 
     { curline = "";  if (ifs) getline(ifs, curline); return *this; }
     SourceIterator & operator++(int) 
     { curline = "";  if (ifs) getline(ifs, curline); return *this; }
     string operator* () { return curline; }
     operator bool() { return ifs.good(); }
     bool operator!() const   { return !ifs.good(); }

};

Open in new window

ambienceCommented:
BTW, you have a very narrowing design but I hope the following help with at least the basic usages
Didn't test it though, so do expect a few glitches. But it shuold be easy to debug and fix.
Hope that helps ...

class _Iterator 
{ 
	friend class Source;
	
private:	
     Source& src; 
	 string line;
	 bool ended;
	 
     _Iterator(Source& s, bool end) : src(s), ended(end)
     { 
		if(!ended) 
			++ (*this);
	 } 
	 
public: 
	typedef string value_type;

	_Iterator(const _Iterator& o) : src(o.src), line(o.line), ended(o.ended)
     { 
	 } 
     
     _Iterator& operator++()  
     {
		if(ended)
			throw std::exception("reading past end of file");
		ended = src.endOfSource();
		line = src.readLine();
		return *this; 
	 } 

     _Iterator operator++(int)  
     { 
		_Iterator temp(*this);
		++ (*this);
		return temp; 
	 } 
	 
    value_type operator*()
	{ 
		return line; 
	} 

	bool operator!=(const _Iterator& right)
	{
		if(ended)
			return right.ended;
		return false;
	}
	
	bool operator<(const _Iterator& right)
	{
		if(ended)
			return false;
		return true;
	}
 };
 
 
 class Source
 {
 ....
 public:
	typedef _Iterator iterator;
 
 
	iterator begin()
	{
		if (this->stream == NULL) 
		{ 
			throw std::exception("open first");
		} 
		this->stream->seekg(0, ios_base::beg);
		return iterator(*this, false);
	}
	
	iterator end()
	{
		return iterator(*this, true);
	}
 };
 
 
 // Usage
 Source::iterator i = src.begin();
 for(;i<src.end();++i)
	std::cout << *i << std::endl;

Open in new window

CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

manszAuthor Commented:
Thanks guys for your answers. I would like to use the iterator in STL-functions an my biggest hurdle when I was trying to implement the iterator was to understand how to implement the boolean operators like !=, == and so on. Is ambience solution addequate for STL-functions? I thought that I had to add a line counter so I would know if two iterators is on the same line in the file is that overkill when getting it working with the STL-functions?
ambienceCommented:
You can add a line count member to the iterator to check whether two iterators are on the same line. Then you wouldn't even need the ended bool flag.
Alternatively, you can use the file position to compare.
 >> Is ambience solution addequate for STL-functions?
That really depends - which functions are you talking about? Most of the time the equality, inequality and less-than operator are enough. If you really need, you can implement others in terms of these.
See the modified version .. attached

class _Iterator  
{
	friend class Source;
	
private:         
    Source& src;  
    int line; 
          
    _Iterator(Source& s, bool end) : src(s), line(0)
    {  
        if(!end)
            ++ (*this);
		else 
			line = -1;
    }  
          
public:  
	typedef string value_type; 
 
    _Iterator(const _Iterator& o) : src(o.src), line(o.line) 
    {  
    }  
      
    _Iterator& operator++()   
     { 
		if(line == -1) 
			throw std::exception("reading past end of file"); 
		if(!src.endOfSource())
			line++; 
		return *this;  
	}  

     _Iterator operator++(int)   
     {  
		_Iterator temp(*this); 
		++ (*this); 
		return temp;  
	 }  
          
    value_type operator*() 
	{  
		if(line == -1) 
			throw std::exception("reading past end of file"); 
		return src.readLine();  
	}  
 
	bool operator==(const _Iterator& right) 
	{ 
		return (&src == &(right.src)) && line == right.line; 
	} 

	bool operator!=(const _Iterator& right) 
	{ 
		return !this->operator==(right); 
	} 
	 
	bool operator<(const _Iterator& right) 
	{ 
		if(line == -1) 
			return right.line == -1 ? false : true; 
		return line < right.line; 
	} 
 }; 
  
  
 class Source 
 { 
 .... 
 public: 
	typedef _Iterator iterator; 


	iterator begin() 
	{ 
		if (this->stream == NULL)  
		{  
				throw std::exception("open first"); 
		}  
		this->stream->seekg(0, ios_base::beg); 
		return iterator(*this, false); 
	} 
	 
	iterator end() 
	{ 
		return iterator(*this, true); 
	} 
}; 
  
  
 // Usage 
 Source::iterator i = src.begin(); 
 for(;i<src.end();++i) 
        std::cout << *i << std::endl;

Open in new window

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
manszAuthor Commented:
I am on my trial period for expert-exchange but must say I am mighty impressed by both your answers and the time it takes for you experts to answer the question.
itsmeandnobodyelseCommented:
For use in STL functions you would need to derive from std::iterator. But actually there is already a basic_istream::iterator defined (which you could use for your Source class). I never used it myself because streams have an automatic iteration and therefore using an iterator is more code that simply call getline in a loop.
evilrixSenior Software Engineer (Avast)Commented:
>> For use in STL functions you would need to derive from std::iterator
You would? Why?

>> there is already a basic_istream::iterator
It doesn't read lines... it just calls operator <<

NB. I've always thought this was a mistake.
manszAuthor Commented:
itsmeandnobodyelse do you mean that I should use the std::iterator or the basic_istream::iterator and write a template which will use dose iterator to return one line at a time? Since the std iterators will only return a char at a time, right? Maybe I should create a new question?  
itsmeandnobodyelseCommented:
>>>> For use in STL functions you would need to derive from std::iterator
>>You would? Why?

I deduced it from MSDN:

---------------------------------------------------------------
std::iterator
An empty base class that provides types and that may be used to ensure that a user-defines iterator class works properly with iterator_traits.
----------------------------------------------------------------

But when reading the above more thoroughly, it seems that std::iterator is only a helper which *can* be used.

>>>> do you mean that I should use the std::iterator or the basic_istream::iterator
As told I decided for myself to not using iterators on streams because of the efforts to using them and because of the limitations (only forward, no two iterators on the same stream, only one char per iteration). But when you intend to use functions like std::for_each or std::find for your Source class where the *typename* is a string (line) rather than a char then it probably makes more sense to try to adopt your own Source::iterator rather than trying to overload istream::iterator.  I often made iterators for my own container classes but I never made them compatible with the STL iterators. So, unfortunately I can't say what efforts are needed.

It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.