S
Scott Smedley
Hi all,
I'm trying to write a special adaptor iterator for my program. I have
*almost* succeeded, though it fails under some circumstances.
See the for-loop in main().
Any pointers/help would be muchly appreciated.
Apologies for the long post - I couldn't find a shorter way to
express my problem without removing too much useful context.
SCoTT.
// About this test program:
// I have a single container (std::map) of objects (Object *) as a data
// member of a class (Instrument).
// I provide a set of iterators to access the elements of the container
// in different ways. ie. access elements as Object&, or Object*, or
// const Object&, etc.
// I also allow a function object to be passed to the constructor of the
// iterators. This has the purpose of making the iterator perform
// selective iteration. ie. elements of the container that don't satisfy
// the function object are automatically "skipped".
// What I am trying to achieve:
// 1. Hide the container type from users of the Instrument object.
// 2. Provide various ways to access the elements of the single container.
// 3. Allow iterators to "skip" undesired elements according to a funciton
// object.
// 4. Iterators should be compatible with STL functions.
#include <map>
#include <iostream>
#include <cassert>
#include <ext/algorithm>
using __gnu_cxx::count_if;
using std::cout;
using std::endl;
// The container class stores pointers to Object objects.
class Object
{
public:
int id;
bool bActive;
char objectType;
bool isActive (void)
{
return bActive;
}
};
// Various function objects used to selectively iterate over container objects.
typedef struct FunctionObject
{
virtual bool operator () (Object *p) const
{
(void) p;
return true;
}
};
typedef struct AllObjects : public FunctionObject { };
typedef struct ActiveGuideObjects : public FunctionObject
{
bool operator () (Object *p) const
{
return (p->isActive() && p->objectType == 'A');
}
};
typedef struct ScienceObjects : public FunctionObject
{
bool operator () (Object *p) const
{
return (p->objectType == 'B');
}
};
// The instrument class has the container & adaptor iterator stuff.
class Instrument
{
public:
std::map<int, Object *> m;
// ObjectIteratorBase - the base iterator class. Subclasses inherit
// & provide the "dereference iterator" function. ie. "operator *".
// This class is basically a wrapper around the standard iterators.
class ObjectIteratorBase : public std::iterator_traits<Object *>
{
protected:
std::map<int, Object *>::iterator _it, _end;
const FunctionObject &_fo;
public:
ObjectIteratorBase (
std::map<int, Object *>::iterator it,
std::map<int, Object *>::iterator end,
const FunctionObject &fo) :
_it(it),
_end(end),
_fo(fo)
{
findNext();
}
virtual ~ObjectIteratorBase() {}
// keep incrementing the internal iterator until we
// satisfy the function object.
void findNext (void)
{
while (_it != _end)
{
if (!(_fo(_it->second)))
_it++;
else
break;
}
}
void operator ++ (void)
{
_it++;
findNext();
}
bool operator != (const ObjectIteratorBase &i) const
{
return _it != i._it;
}
bool isValid (void) const
{
return (_it != _end);
}
};
// ObjectIteratorRef - iterator for accessing container objects
// as a reference.
class ObjectIteratorRef : public ObjectIteratorBase
{
public:
ObjectIteratorRef (
std::map<int, Object *>::iterator it,
std::map<int, Object *>::iterator end,
const FunctionObject &fo) :
ObjectIteratorBase(it, end, fo)
{
}
Object &operator * (void)
{
return *(_it->second);
}
};
// Note: <fo> *MUST* be const. (see [dcl.init.ref])
ObjectIteratorRef beginRef (const FunctionObject &fo)
{
return ObjectIteratorRef(m.begin(), m.end(), fo);
}
ObjectIteratorRef endRef (void)
{
return ObjectIteratorRef(m.end(), m.end(), AllObjects());
}
// ObjectIteratorPtr - iterator for accessing container objects
// as a pointer.
class ObjectIteratorPtr : public ObjectIteratorBase
{
public:
static uint counter;
ObjectIteratorPtr (
std::map<int, Object *>::iterator it,
std::map<int, Object *>::iterator end,
const FunctionObject &fo) :
ObjectIteratorBase(it, end, fo)
{
#if 0
cout << "created ObjectIteratorPtr " << counter++ << endl;
cout << "&it=" << &it << " *it=" << it->first << endl;
cout << "&end=" << &end << endl;
cout << "atEnd? is " << (it == end) << endl;
#endif
}
Object *operator * (void)
{
return _it->second;
}
};
ObjectIteratorPtr beginPtr (const FunctionObject &fo)
{
return ObjectIteratorPtr(m.begin(), m.end(), fo);
}
ObjectIteratorPtr endPtr (void)
{
return ObjectIteratorPtr(m.end(), m.end(), AllObjects());
}
// TODO: also need to provide const versions of the iterators.
// ObjectIteratorBase should be made a template class.
};
uint Instrument::ObjectIteratorPtr::counter = 0;
void print (const Object &r)
{
cout << "object " << r.id << " is " << (r.bActive ? "" : "IN")
<< "active and of type: " << r.objectType << endl;
}
typedef struct PrintMe : public std::unary_function<std:air<const int, Object *>, void>
{
void operator () (std:air<const int, Object *> &p) const
{
print(*(p.second));
}
};
typedef struct PrintRef : public std::unary_function<Object &, void>
{
void operator () (Object &r) const
{
print(r);
}
};
typedef struct PrintPtr : public std::unary_function<Object *, void>
{
void operator () (Object *p) const
{
print(*p);
}
};
// for_each_if<>:
// Apply an operation to each element of a set that satisfies the
// predicate specified by <pred>.
// Very similar to STL std::for_each template function, but only applies
// the operation <f> on elements that satisfy the predicate <pred>.
template <class InputIterator, class Predicate, class Function>
Function for_each_if (
InputIterator first,
InputIterator last,
Predicate pred,
Function f)
{
for ( ; first != last; ++first)
{
if (pred(*first))
f(*first);
}
return f;
}
int main (void)
{
Instrument instr;
for (uint i = 1; i <= 50; i++)
{
Object *p = new Object();
assert(p != NULL);
p->id = i;
p->bActive = lrand48() % 2;
p->objectType = (char) (65 + lrand48() % 3);
instr.m = p;
}
#if 0
cout << "All objects are:" << endl;
std::for_each(instr.m.begin(), instr.m.end(), PrintMe());
#endif
cout << "Science objects are:" << endl;
std::for_each(instr.beginPtr(ScienceObjects()), instr.endPtr(), PrintPtr());
cout << "Active guide objects are:" << endl;
std::for_each(instr.beginRef(ActiveGuideObjects()), instr.endRef(), PrintRef());
cout << "Active science objects are:" << endl;
for_each_if(instr.beginRef(ScienceObjects()),
instr.endRef(),
std::mem_fun_ref(&Object::isActive),
PrintRef());
uint n = count_if(instr.beginRef(ScienceObjects()),
instr.endRef(),
std::mem_fun_ref(&Object::isActive));
cout << "There are " << n << " active science objects." << endl;
for (Instrument::ObjectIteratorPtr it = instr.beginPtr(AllObjects());
#if 1
it.isValid(); // this works.
#else
it != instr.endPtr(); // this doesn't!
#endif
++it)
{
Object *pObject = *it;
cout << ' ' << pObject->id;
#if 0
// A nested for-loop fails too - even if the outer loop termination
// condition is: it.isValid().
for (Instrument::ObjectIteratorPtr it2(instr.beginPtr(AllObjects()));
it2.isValid();
++it2)
{
cout << "*";
}
cout << endl;
#endif
}
cout << endl;
return 0;
}
I'm trying to write a special adaptor iterator for my program. I have
*almost* succeeded, though it fails under some circumstances.
See the for-loop in main().
Any pointers/help would be muchly appreciated.
Apologies for the long post - I couldn't find a shorter way to
express my problem without removing too much useful context.
SCoTT.
// About this test program:
// I have a single container (std::map) of objects (Object *) as a data
// member of a class (Instrument).
// I provide a set of iterators to access the elements of the container
// in different ways. ie. access elements as Object&, or Object*, or
// const Object&, etc.
// I also allow a function object to be passed to the constructor of the
// iterators. This has the purpose of making the iterator perform
// selective iteration. ie. elements of the container that don't satisfy
// the function object are automatically "skipped".
// What I am trying to achieve:
// 1. Hide the container type from users of the Instrument object.
// 2. Provide various ways to access the elements of the single container.
// 3. Allow iterators to "skip" undesired elements according to a funciton
// object.
// 4. Iterators should be compatible with STL functions.
#include <map>
#include <iostream>
#include <cassert>
#include <ext/algorithm>
using __gnu_cxx::count_if;
using std::cout;
using std::endl;
// The container class stores pointers to Object objects.
class Object
{
public:
int id;
bool bActive;
char objectType;
bool isActive (void)
{
return bActive;
}
};
// Various function objects used to selectively iterate over container objects.
typedef struct FunctionObject
{
virtual bool operator () (Object *p) const
{
(void) p;
return true;
}
};
typedef struct AllObjects : public FunctionObject { };
typedef struct ActiveGuideObjects : public FunctionObject
{
bool operator () (Object *p) const
{
return (p->isActive() && p->objectType == 'A');
}
};
typedef struct ScienceObjects : public FunctionObject
{
bool operator () (Object *p) const
{
return (p->objectType == 'B');
}
};
// The instrument class has the container & adaptor iterator stuff.
class Instrument
{
public:
std::map<int, Object *> m;
// ObjectIteratorBase - the base iterator class. Subclasses inherit
// & provide the "dereference iterator" function. ie. "operator *".
// This class is basically a wrapper around the standard iterators.
class ObjectIteratorBase : public std::iterator_traits<Object *>
{
protected:
std::map<int, Object *>::iterator _it, _end;
const FunctionObject &_fo;
public:
ObjectIteratorBase (
std::map<int, Object *>::iterator it,
std::map<int, Object *>::iterator end,
const FunctionObject &fo) :
_it(it),
_end(end),
_fo(fo)
{
findNext();
}
virtual ~ObjectIteratorBase() {}
// keep incrementing the internal iterator until we
// satisfy the function object.
void findNext (void)
{
while (_it != _end)
{
if (!(_fo(_it->second)))
_it++;
else
break;
}
}
void operator ++ (void)
{
_it++;
findNext();
}
bool operator != (const ObjectIteratorBase &i) const
{
return _it != i._it;
}
bool isValid (void) const
{
return (_it != _end);
}
};
// ObjectIteratorRef - iterator for accessing container objects
// as a reference.
class ObjectIteratorRef : public ObjectIteratorBase
{
public:
ObjectIteratorRef (
std::map<int, Object *>::iterator it,
std::map<int, Object *>::iterator end,
const FunctionObject &fo) :
ObjectIteratorBase(it, end, fo)
{
}
Object &operator * (void)
{
return *(_it->second);
}
};
// Note: <fo> *MUST* be const. (see [dcl.init.ref])
ObjectIteratorRef beginRef (const FunctionObject &fo)
{
return ObjectIteratorRef(m.begin(), m.end(), fo);
}
ObjectIteratorRef endRef (void)
{
return ObjectIteratorRef(m.end(), m.end(), AllObjects());
}
// ObjectIteratorPtr - iterator for accessing container objects
// as a pointer.
class ObjectIteratorPtr : public ObjectIteratorBase
{
public:
static uint counter;
ObjectIteratorPtr (
std::map<int, Object *>::iterator it,
std::map<int, Object *>::iterator end,
const FunctionObject &fo) :
ObjectIteratorBase(it, end, fo)
{
#if 0
cout << "created ObjectIteratorPtr " << counter++ << endl;
cout << "&it=" << &it << " *it=" << it->first << endl;
cout << "&end=" << &end << endl;
cout << "atEnd? is " << (it == end) << endl;
#endif
}
Object *operator * (void)
{
return _it->second;
}
};
ObjectIteratorPtr beginPtr (const FunctionObject &fo)
{
return ObjectIteratorPtr(m.begin(), m.end(), fo);
}
ObjectIteratorPtr endPtr (void)
{
return ObjectIteratorPtr(m.end(), m.end(), AllObjects());
}
// TODO: also need to provide const versions of the iterators.
// ObjectIteratorBase should be made a template class.
};
uint Instrument::ObjectIteratorPtr::counter = 0;
void print (const Object &r)
{
cout << "object " << r.id << " is " << (r.bActive ? "" : "IN")
<< "active and of type: " << r.objectType << endl;
}
typedef struct PrintMe : public std::unary_function<std:air<const int, Object *>, void>
{
void operator () (std:air<const int, Object *> &p) const
{
print(*(p.second));
}
};
typedef struct PrintRef : public std::unary_function<Object &, void>
{
void operator () (Object &r) const
{
print(r);
}
};
typedef struct PrintPtr : public std::unary_function<Object *, void>
{
void operator () (Object *p) const
{
print(*p);
}
};
// for_each_if<>:
// Apply an operation to each element of a set that satisfies the
// predicate specified by <pred>.
// Very similar to STL std::for_each template function, but only applies
// the operation <f> on elements that satisfy the predicate <pred>.
template <class InputIterator, class Predicate, class Function>
Function for_each_if (
InputIterator first,
InputIterator last,
Predicate pred,
Function f)
{
for ( ; first != last; ++first)
{
if (pred(*first))
f(*first);
}
return f;
}
int main (void)
{
Instrument instr;
for (uint i = 1; i <= 50; i++)
{
Object *p = new Object();
assert(p != NULL);
p->id = i;
p->bActive = lrand48() % 2;
p->objectType = (char) (65 + lrand48() % 3);
instr.m = p;
}
#if 0
cout << "All objects are:" << endl;
std::for_each(instr.m.begin(), instr.m.end(), PrintMe());
#endif
cout << "Science objects are:" << endl;
std::for_each(instr.beginPtr(ScienceObjects()), instr.endPtr(), PrintPtr());
cout << "Active guide objects are:" << endl;
std::for_each(instr.beginRef(ActiveGuideObjects()), instr.endRef(), PrintRef());
cout << "Active science objects are:" << endl;
for_each_if(instr.beginRef(ScienceObjects()),
instr.endRef(),
std::mem_fun_ref(&Object::isActive),
PrintRef());
uint n = count_if(instr.beginRef(ScienceObjects()),
instr.endRef(),
std::mem_fun_ref(&Object::isActive));
cout << "There are " << n << " active science objects." << endl;
for (Instrument::ObjectIteratorPtr it = instr.beginPtr(AllObjects());
#if 1
it.isValid(); // this works.
#else
it != instr.endPtr(); // this doesn't!
#endif
++it)
{
Object *pObject = *it;
cout << ' ' << pObject->id;
#if 0
// A nested for-loop fails too - even if the outer loop termination
// condition is: it.isValid().
for (Instrument::ObjectIteratorPtr it2(instr.beginPtr(AllObjects()));
it2.isValid();
++it2)
{
cout << "*";
}
cout << endl;
#endif
}
cout << endl;
return 0;
}