A different kind of adaptor iterator

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::pair<const int, Object *>, void>
{
void operator () (std::pair<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;
}
 
D

Dan W.

// The container class stores pointers to Object objects.
class Object // Poor name choice, BTW...
{
int id;
bool bActive;
char objectType;
public:
const char objectType;
bool isActive (void) bool isActive (void) const
{
return bActive;
}
};

So, what exactly IS the problem, so one knows what to look for?
 
S

Scott Smedley

^^^^^^^^^^^^

I originally had named this class "Spine", but I didn't want to
confuse readers & have them wonder what the hell a Spine was
so I changed it to Object. Perhaps I was trying to make things
too simple?
So, what exactly IS the problem, so one knows what to look for? ^^^^^^^^^^^^^^^^^^^^^^^^^^

If I use the "it != instr.endPtr()" syntax, the for-loop
aborts after iterating over only 1 object. If I use the
"it.isValid()" syntax, it iterates over all of them (good)
but the nested for-loop still won't work.

Apologies if my initial post wasn't clear, I thought it was
intuitive but it's difficult to be objective about one's
own posts.

SCoTT. :)
 
D

Dan W.

I originally had named this class "Spine", but I didn't want to
confuse readers & have them wonder what the hell a Spine was
so I changed it to Object. Perhaps I was trying to make things
too simple?

I wouldnd care if you named it A or foo, or base;
but NOT "object", or "member" or "vector", you know what I mean?
It's like I have to remind myself of what I mean when I think "this
pointer points to and object of a class derived from object..."
If I use the "it != instr.endPtr()" syntax, the for-loop
aborts after iterating over only 1 object. If I use the
"it.isValid()" syntax, it iterates over all of them (good)
but the nested for-loop still won't work.

Apologies if my initial post wasn't clear, I thought it was
intuitive but it's difficult to be objective about one's
own posts.

SCoTT. :)

It's waay too big.

If you have an iteration problem, can you show this with two or three
classes, like

class my_base
{
};

class derived1 : my_base
{
void ouch() const; ///problem here!
................
etc....

And may be just re-post it as new thread.
 
S

Scott Smedley

For anyone who might be following this thread, I located the
source of my problem - a compiler bug.

With g++-3.2.2 the function object is only invoked ONCE! With
g++-3.3.2 it is invoked on every iteration of the loop (as
expected).
void findNext (void)
{
while (_it != _end)
{
if (!(_fo(_it->second)))
_it++;
else
break;
}
}

SCoTT. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top