tags:

views:

208

answers:

7

Lets say I have a linked list with a bunch of different data in it.

class Node
{
public:
    Node* next;
    AAA dataA;
    BBB dataB;
    CCC dataC;
};

Is there a way I make one iterator that would iterate over whatever variable I specify (rather than making three separate ones for each variable). I understand that the iterator could use templates to make it iterate over types AAA, BBB or CCC, but I don't know how I could specify which variable to return.

A: 

There is no way to create a template in C++ which in itself will iterate over the members of a type. Doing so requires some help in the class either in the form of template specializations, special methods, etc ...

The template in itself would be fairly complex and would require a lot of setup work. The reason why is that given a specific type to iterate over, the template must deal with N other types. Namely the type of the members returned.

Not saying it can't be done (it can), just that this is more complex than a simple template method.

JaredPar
A: 

I doubt you can use templates to automatically choose the right variable to return, except by specifying three template specializations, which would be the same as defining three classes. You could however create one iterator class with three different methods to return dataA, dataB or dataC respectively (instead of operator*()).

sepp2k
Alex
+2  A: 

A possible solution is to split the iterator and the access into separate classes:

Iterator class that encapsulates the access to the data via a template argument:

template <typename Access>
class iterator
{
private:
  Node *current;

public:
  iterator(Node *start)
    : current(start)
  {
  }

  typename Access::typeof &operator *() const
  {
    return Access::access(*current);
  }

  bool end() const
  {
    return (current == NULL);
  }

  iterator &operator++()
  {
    if (current !=  NULL)
    {
      current = current->Next;
    }
  }

  // ... other useful operators/methods
};

Classes for accessing the various data fields. THose can be used as template parameters in the iterator class:

class AccessDataA
{
public:
  typedef AAA typeof;
  static AAA &access(Node &node)
  {
    return node.dataA;
  }
};

class AccessDataB
{
public:
  typedef BBB typeof;
  static BBB &access(Node &node)
  {
    return node.dataB;
  }
};

class AccessDataC
{
public:
  typedef CCC typeof;
  static CCC &access(Node &node)
  {
    return node.dataC;
  }
};

Example usage:

Node *start = ...;

// Loop over B:
for (iterator<AccessB> it(start); it++; !it.end())
{
  // ... *it ...
}

// Loop over C:
for (iterator<AccessC> it(start); it++; !it.end())
{
  // ... *it ...
}

One improvement would be to add STL compatible semantic so your list and iterator can be used in STL methods like std::for_each.

rstevens
A: 

Your question is really only a part question.

You could make an iterator adapter that acted like it iterated over a collection of AAA, but was actually iterating over a collection of Nodes. This might not be the best solution to your underlying problem, though.

I'm guessing that you have some sort of action that you want to perform on each aaa member. Suppose that this was a functor such as this.

struct DoAAAAction
{
    void operator()(AAA& a);
};

It is probably easier to adapt the action to act on a Node.

template<class Action>
class DataA_ActionAdapter
{
public:
    DataA_ActionAdapter( Action aa ) : a( aa ) {}
    void operator()(Node& n) { a(n.dataAAA); }
private:
    Action a;
};

This allows you to use standard algorithms on Node iterators.

template<class NodeIterator, class AAAAction>
void TestAAA(NodeIterator first, NodeIterator last, AAAAction aaaa)
{
    std::for_each( first, last, DataA_ActionAdapter<AAAAction>( aaaa ) );
}
Charles Bailey
A: 

If I understand you correctly, you like to iterate over dataA, dataB and dataC - so this implies that AAA, BBB and CCC all share the same base type (or at least share similar characteristics). Why not simply store these in a std::vector or std::set?

Note: AAA, BBB and CCC are all derived from NodeType

class Node
{
public:
    Node()
    {
        dataNodes.push_back(AAA());
        dataNodes.push_back(BBB());
        dataNodes.push_back(CCC());
    }

    // AAA dataA;
    // BBB dataB;
    // CCC dataC;

    std::vector < NodeType > dataNodes;

    std::vector < NodeType >::iterator begin()
    {
        return dataNodes.begin();
    }

    std::vector < NodeType >::iterator end()
    {
        return dataNodes.end();
    }
};
Alan
+3  A: 

The best way I've found to do this is with boost bind and boost transform_iterator

First you'll need a collection of Node objects and an iterator that will traverse the collection. For brevity in my example I'll use a std::list.

#include <boost/bind.hpp>
#include <boost/iterator/transform_iterator.hpp>
using boost;

struct FunctionAAA
{
    void operator() (const AAA& x)
    {}
};

struct FunctionBBB
{
    void operator() (const BBB& x)
    {}
};

typedef std::list<Node> NodeList;
NodeList collection;
std::foreach (
    make_transform_iterator (collection->begin(), bind (&Node::dataA, _1)),
    make_transform_iterator (collection->end(), bind (&Node::dataA, _1)),
    FunctionAAA());

std::foreach (
    make_transform_iterator (collection->begin(), bind (&Node::dataB, _1)),
    make_transform_iterator (collection->end(), bind (&Node::dataB, _1)),
    FunctionBBB());
Stephen Nutt
+1  A: 

I think I've found a way to do pretty much what I want based on rstevens' suggestion. I looked up some stuff on class member pointers and was able to skip the middleman accessor class by doing this:

template <typename T>
class iterator
{
private:
    Node *current;
    T Node::*var;

public:
    iterator()
        : current(NULL), var(NULL) {}

    iterator(Node *start, T Node::*var)
        : current(start), var(var)
    {
    }

    typename T &operator *() const
    {
        return current->*var;
    }

    bool end() const
    {
        return (current == NULL);
    }

    iterator &operator++()
    {
        if (current)
            current = current->next;
        return *this;
    }
};

And then I modified Node to have convenience functions to make the iterators:

class Node
{
public:    
    Node* next;
    AAA dataA;
    BBB dataB;
    CCC dataC;

    typedef iterator<AAA> AIter;
    typedef iterator<BBB> BIter;
    typedef iterator<CCC> CIter;

    AIter getAIter()
    {
        return AIter(this, &Node::dataA);
    }

    BIter getBIter()
    {
        return BIter(this, &Node::dataB);
    }

    CIter getCIter()
    {
        return CIter(this, &Node::dataC);
    }
};

So now I can do this to easily iterate over each data member of my class:

for (Node::CIter iter = n1.getCIter(); !iter.end(); ++iter)
{
    // tada!
}
Alex