Custom iterator not working

I'm making a class similar to std::vector but instead of storing the contents in memory, I'm storing them in files, since this can save me from using new and delete, make my container dynamically sizable, make it store large values and help me save data after ending the program.

To make my container work with <algorithm>, I made an iterator. But it's not working

I made a base class bfvec (binary file vector, no i/o capabilities)
I derived ibfvec(input only) and obfvec(output only) from bfvec
Then I derived iobfvec from ibfvec and obfvec
(all derivations are public)

I made an input iterator for ibfvec, but it's giving compilation errors

My base class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
template<class typ,class ftyp>	//typ is type of element
	//ftype is file stream handling class (fstream, ofstream or ifstream)
class bfvec
{
protected:
	char fname[256];	//name of file
	int po_mode/*previous open mode*/;	//previous file opening mode
	bool _save;	//should the file be saved or deleted on file closing
	ftyp file;	//stream object
	
public:
//Bitsetting functions
	void setgood(){file.clear();}
	void setsave(bool b){save=b;}
	
//Bitstate checking functions
	bool isbad()const {return !file.good();}	//stream not good
	bool isopen()const {return file.is_open();}	//stream open
	bool issave()const {return _save;}
	bool isgood()const {return (file.is_open() && file.good());}
	//bfvec object is fit for read/write

//Constructors
	bfvec():po_mode(0){}

//Functions
	bool open(const char* filename,const int open_mode,const bool save);
	bool rename(const char* new_file_name);
	void close(){if(file.is_open())file.close();if(!issave())remove(fname);}
	bool get_file_name(char* buffer);
};


My input class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
template<class typ,class ftyp>
class ibfvec:virtual public bfvec<typ,ftyp>
{
protected:
	typ buff1;
	int _size;	//size of file (will remain fixed as there is only input)
		//but this will not be the case in iobfvec
		//hence, I made a virtual function bef_func_call() which will update
		//_size during every function call in iobfvec
	void get_size(){file.seekg(0,file.end);_size=file.tellg()/sizeof(typ);}
	void adjust_length(const int from_pos,int& length);
	virtual void bef_func_call(){}

public:
	ibfvec():bfvec<typ,ftyp>(){}
	bool open(const int open_mode,const bool sav)
	{if(!bfvec::open(open_mode,sav))return false;get_size();return isgood();}
	bool open(const char* filename,const int open_mode,const bool sav)
	{if(!bfvec::open(filename,open_mode,sav))return false;get_size();return isgood();}

	int size(){bef_func_call();return _size;}

	typ read(const int pos);
	typ back();
	typ operator[](int pos){read(pos);}
	bool read(typ& buffer,const int pos);
	bool read(typ* buffer,const int from_pos,const int size);
	void read(typ* buffer){read(buffer,0,_size);}

};


input iterator for ibfvec(called ibfiter):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
template<class typ,class ftyp>
class ibfiter
{
protected:
	int pos;
	ibfvec<typ,ftyp>* ptr;

public:
//constructors
	ibfiter():pos(0),ptr(NULL){}
	ibfiter(ibfiter& a):pos(a.pos),ptr(a.ptr){}
	ibfiter(ibfvec<typ,ftyp>& arr,int p=0):pos(p),ptr(&arr){}
	ibfiter(ibfvec<typ,ftyp>* parr,int p=0):pos(p),ptr(parr){}

//assignment
	void operator=(int a){pos=a;}
	void operator=(ibfvec<typ,ftyp>& arr){ptr=&arr;}
	void operator=(ibfvec<typ,ftyp>* parr){ptr=parr;}
	void operator=(ibfiter it){pos=it.pos;ptr=it.ptr;}

//dereference
	typ operator*(){return ptr->read(pos);}
	typ operator[](int n){return ptr->read(pos+n);}

//increment/decrement
	ibfiter operator+(int n){return ibfiter(ptr,pos+n);}
	ibfiter operator+=(int n){pos+=n;}
	ibfiter operator-(int n){return ibfiter(ptr,pos-n);}
	ibfiter operator-=(int n){pos+=n;}
	int operator-(ibfiter& it){pos-it.pos;}

	ibfiter operator++(){pos++;ibfiter it(ptr,pos);return it;}
	ibfiter operator--(){pos--;ibfiter it(ptr,pos);return it;}
	ibfiter operator++(int){ibfiter it(ptr,pos);pos++;return it;}
	ibfiter operator--(int){ibfiter it(ptr,pos);pos--;return it;}

//comparison
	bool operator==(ibfiter& a){return (ptr==a.ptr && pos==a.pos);}
	bool operator!=(ibfiter& a){return !operator==(a);}

	bool operator<(ibfiter& it){return pos<it.pos;}
	bool operator<=(ibfiter& it){return pos<=it.pos;}
	bool operator>(ibfiter& it){return pos>it.pos;}
	bool operator>=(ibfiter& it){return pos>=it.pos;}
};

template<class typ,class ftyp>
inline ibfiter<typ,ftyp> operator+(int n,ibfiter<typ,ftyp>& it)
{return ibfiter<typ,ftyp>(it.ptr,it.pos+n);}


My main program:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
#include<fstream>
#include<algorithm>
#include<eku\ibfvec.h>

using namespace std;

void main()
{
	//char i[8]="eklavya";
	ibfvec<char,ifstream> a;
	ibfiter<char,ifstream> i(a,0);
	a.open("tile.txt",ios::in,true);
	cout<<count(i,i+6,'a')<<endl;
}


But I'm getting this as the 1st error

error C2039: 'iterator_category' : is not a member of 'ibfiter<typ,ftyp>'
with
[
typ=char,
ftyp=std::ifstream
]
e:\dropbox\eku\under construction\test.cpp(15) : see reference to class template instantiation 'std::iterator_traits<_Iter>' being compiled
with
[
_Iter=ibfiter<char,std::ifstream>
]

I think this error shouldn't come as pointers can be used as iterators in arrays as pointers don't have a member called 'iterator_category'!

Any help or suggestion would be appreciated.
Last edited on
*Credits - Code below taken from mingw GCC headers*

The iterators in the STL are designed with nested typedefs like this.
As you can see one of these typedefs is called iterator_category

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
   *  @brief  Common %iterator class.
   *
   *  This class does nothing but define nested typedefs.  %Iterator classes
   *  can inherit from this class to save some work.  The typedefs are then
   *  used in specializations and overloading.
   *
   *  In particular, there are no default implementations of requirements
   *  such as @c operator++ and the like.  (How could there be?)
  */
  template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t,
           typename _Pointer = _Tp*, typename _Reference = _Tp&>
    struct iterator
    {
      /// One of the @link iterator_tags tag types@endlink.
      typedef _Category  iterator_category;
      /// The type "pointed to" by the iterator.
      typedef _Tp        value_type;
      /// Distance between iterators is represented as this type.
      typedef _Distance  difference_type;
      /// This type represents a pointer-to-value_type.
      typedef _Pointer   pointer;
      /// This type represents a reference-to-value_type.
      typedef _Reference reference;
    };




Then there is the iterator_traits struct that details some
particulars about a given iterator_type.

1
2
3
4
5
6
7
8
9
  template<typename _Iterator>
    struct iterator_traits
    {
      typedef typename _Iterator::iterator_category iterator_category;
      typedef typename _Iterator::value_type        value_type;
      typedef typename _Iterator::difference_type   difference_type;
      typedef typename _Iterator::pointer           pointer;
      typedef typename _Iterator::reference         reference;
    };



STL algorithms that uses iterators - uses the iterator_traits
struct above when they are making use of the iterators.
Here is the std::copy algoithm you are using.
As you can see it needs to instantiate an iterator_traits struct
in order For Example - To get the iterator_traits<_InputIterator>::value_type typedef.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
	 template<typename _InputIterator, typename _Tp>
    typename iterator_traits<_InputIterator>::difference_type
    count(_InputIterator __first, _InputIterator __last, const _Tp& __value)
    {
      // concept requirements
      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
      __glibcxx_function_requires(_EqualOpConcept<
	typename iterator_traits<_InputIterator>::value_type, _Tp>)
      __glibcxx_requires_valid_range(__first, __last);
      typename iterator_traits<_InputIterator>::difference_type __n = 0;
      for (; __first != __last; ++__first)
	if (*__first == __value)
	  ++__n;
      return __n;
    }
	


As you can see because your iterator layout does not match/complement
the way the stl iterators work, then using the stl alogrithm functions
with your iterator is doomed to failure.


Summary:
If you want your iterator to work with the stl algorithms you will
be better of inheriting your iterator from the stl iterator.

(I have a link somewhere about how to do this - I'll dig it out)
Last edited on
Thanks guestgulkan.
I derived ibfiter from
std::iterator<std::input_iterator_tag, ibfvec<typ,ftyp> >
and now my iterator is working!

I learn C++ on my own so I didn't know that it is almost mandatory to derive iterators from std::iterator.

Pointers also work as iterators. They work with all STL algorithms. Pointers are not derived from std::iterator. They are not classes! So why do they work with STL algorithms? (Anyone please answer this)

This is what I thought when I was making my iterator, so I didn't think they need to be derived from a class.
Last edited on
IIRC there is a type_traits struct specialization for when the
Iterator is a standard C++ pointer T*
It's not mandatory to use std::iterator. It's just there to make it easier to implement iterators. If you don't derive from std::iterator you can put the typedefs for difference_type, value_type, pointer, reference and iterator_category inside the class body instead or specialize std::iterator_traits for your type.

Standard algorithms works with pointers because std::iterator_traits has been specialized for pointers as
1
2
3
4
5
6
7
8
9
namespace std {
	template<class T> struct iterator_traits<T*> {
		typedef ptrdiff_t difference_type;
		typedef T value_type;
		typedef T* pointer;
		typedef T& reference;
		typedef random_access_iterator_tag iterator_category;
	};
}
Topic archived. No new replies allowed.