A different kind of adaptor iterator

Discussion in 'C++' started by Scott Smedley, Dec 5, 2003.

  1. 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;
    }
     
    Scott Smedley, Dec 5, 2003
    #1
    1. Advertising

  2. Scott Smedley

    Dan W. Guest

    >// 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?
     
    Dan W., Dec 5, 2003
    #2
    1. Advertising


  3. >>class Object

    > // Poor name choice, BTW...


    >>// About this test program:

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

    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?


    >> See the for-loop in main().

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

    >> #if 1
    >> it.isValid(); // this works.
    >> #else
    >> it != instr.endPtr(); // this doesn't! :(
    >> #endif


    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. :)
     
    Scott Smedley, Dec 5, 2003
    #3
  4. Scott Smedley

    Dan W. Guest

    >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..."

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

    >
    >>> See the for-loop in main().

    > ^^^^^^^^^^^^^^^^^^^^^^^^^^
    >
    >>> #if 1
    >>> it.isValid(); // this works.
    >>> #else
    >>> it != instr.endPtr(); // this doesn't! :(
    >>> #endif

    >
    >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.
     
    Dan W., Dec 5, 2003
    #4
  5. 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. :)
     
    Scott Smedley, Dec 7, 2003
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?S2VudCBL?=

    Creating a SQL adaptor object on a remote server

    =?Utf-8?B?S2VudCBL?=, Apr 1, 2004, in forum: ASP .Net
    Replies:
    0
    Views:
    327
    =?Utf-8?B?S2VudCBL?=
    Apr 1, 2004
  2. Sripathy Ramesh
    Replies:
    0
    Views:
    562
    Sripathy Ramesh
    Nov 25, 2003
  3. Stanislaw Salik

    pointer to reference adaptor

    Stanislaw Salik, Jul 30, 2004, in forum: C++
    Replies:
    3
    Views:
    736
    Stanislaw Salik
    Jul 30, 2004
  4. Replies:
    3
    Views:
    447
    red floyd
    Apr 3, 2006
  5. Hicham Mouline
    Replies:
    1
    Views:
    576
    Hicham Mouline
    Apr 2, 2009
Loading...

Share This Page