0
$\begingroup$

I am trying to implement a two level iterator as I will now describe. Basically I have a "vector of vectors" and I want a single iterator that will loop over all the objects in the vector of vectors.

The code below does this but it is a little ugly and I can't declare the iterator inside the for loop. Thus I declare the iterator before the for loop, then iterate, then delete the iterator, which is klugy. I don't know C++ so I'm a little out of my league here so don't hesitate to offer C++ programming advice. How can this code be fixed so that the iterator can be declared inside the for loop?

// iterator_2.cpp
//to compile:   g++ iterator_2.cpp

#include <iostream>
#include <vector>

using namespace std;

typedef double Object;

typedef vector<Object> ObjectVector;
vector<ObjectVector*> types;

class ObjectIterator{
 protected:

 public:

 Object *object;
 ObjectIterator operator++()
  {object++; //increment object
    // when current object is at the end for type[s] increment s and set object to the first entry in the next type
    if(object==&(*(types[s])->end())){
      s++;
      object=&((*types[s])[0]);
    }
    return (*this);
  }

 ObjectIterator():null(0){s=0;object= &((*types[0])[0]);};

  int s;
  ObjectIterator *null;

};

int num_types=2;

int main(){

  int num_objects[num_types];
  num_objects[0]=3;
  num_objects[1]=5;

  // initialize objects for each type

  for(int s=0;s<num_types;s++){
    ObjectVector *objectlist = new ObjectVector;

    for(int i=0;i<num_objects[s];i++){
      objectlist->push_back((double)2*i*(s+1));
    }
    types.push_back(objectlist);
  }


// use the two-level iterator

  ObjectIterator *OI;
  OI = new ObjectIterator;

  for(; OI->s < num_types; ++(*OI))
    {
      cout << *(OI->object)<<endl;
    }
  delete OI;  

}
$\endgroup$
  • $\begingroup$ What is the problem with declaring the iterator inside the loop? $\endgroup$ – Wolfgang Bangerth Dec 7 '16 at 20:21
  • $\begingroup$ Also, make your life simpler and don't use plain arrays -- just use std::vector, in your case std::vector<ObjectVector>. $\endgroup$ – Wolfgang Bangerth Dec 7 '16 at 20:22
  • $\begingroup$ I'm not sure what you mean by plain arrays? "types" is vector<ObjectVector*> types; // ObjectIterator *PI; // PI = new ObjectIterator; for(ObjectIterator *PI; PI->s < num_types; ++(*PI)) { cout << *(PI->object)<<endl; } // delete PI; this will segmentation fault. I don't know how to indent this code in this comment. Anyway I tried to comment out the two lines before the loop and the one line after the loop and then declare ObjectIterator *PI in the loop. That seg faults. $\endgroup$ – jep Dec 7 '16 at 20:29
  • $\begingroup$ You should consider using vector<ObjectVector> instead of vector<ObjectVector*> so you don't have to manually new or delete the ObjectVectors. Note as well this question would be better asked on StackOverflow. $\endgroup$ – spektr Dec 7 '16 at 21:37
  • 1
    $\begingroup$ Perhaps you can try asking this on codereview.stackexchange.com? There is quite a number of issues with this code, but it's not a question about computational science. $\endgroup$ – Kirill Dec 7 '16 at 22:03
1
$\begingroup$

While I don't state this to be an optimal implementation or one taking into account the all edge cases, below is a sample implementation I put together for your task that can give you an idea of how one might approach your problem.

The Header File

#ifndef _container_hpp_
#define _container_hpp_

#include <vector>

class container {
public:
   class iterator {
   public:
       iterator();
       void operator++();
       double & operator*();
       bool operator==(const iterator & it);
       bool operator!=(const iterator & it);
   private:
       friend container;
       container * parent;
       int count;
       int r, c;
   };

   container();
   container(int rows, int cols);
   void resize(int rows, int cols);
   double & operator()(int r, int c);
   iterator begin();
   iterator end();

   int nrows() const;
   int ncols() const;

private:
    std::vector<std::vector<double>> data;
    int totalElements;
    int rows, cols;
};



#endif

The Implementation File

#include "Container.hpp"

container::iterator::iterator():parent(0),r(0),c(0) {

}
void container::iterator::operator++() {
    int totRows = parent->nrows();
    if( r != totRows ){
        int totCols = parent->ncols();
        if( c == (totCols-1) ){ c = 0; ++r; }
        else{ ++c; }
        ++count;
    }
}
double & container::iterator::operator*() {
    return parent->data[r][c];
}

bool container::iterator::operator==(const iterator & it) {
    return (parent == it.parent && count == it.count);
}
bool container::iterator::operator!=(const iterator & it) {
    return !this->operator==(it);
}

container::container():data(0),totalElements(0),rows(0),cols(0) {

}
container::container(int rows_, int cols_):data(rows_),totalElements(rows_*cols_),rows(rows_),cols(cols_) {
    for(int i = 0; i < rows; ++i ){
        data[i].resize(cols);
    }
}

void container::resize(int rows_, int cols_) {
    rows = rows_; cols = cols_;
    data.resize(rows);
    for(int i = 0; i < rows; ++i ){
        data[i].resize(cols);
    }
}
double & container::operator()(int r, int c) {
    return data[r][c];
}

container::iterator container::begin() {
    iterator it;
    it.parent = this;
    it.count = 0;
    it.r = 0;
    it.c = 0;
    return it;
}
container::iterator container::end() {
    iterator it;
    it.parent = this;
    it.count = totalElements;
    it.r = -1;
    it.c = -1;
    return it;
}

int container::nrows() const {
    return rows;
}
int container::ncols() const {
    return cols;
}

A sample main.cpp:

#include "Container.hpp"

int main( int argc, char** argv ){
    container test(2,2);
    for(int i = 0; i < test.nrows(); ++i ){
        for(int j = 0; j < test.ncols(); ++j ){
            test(i,j) = i + j;
        }
    }

    container::iterator it;
    for( it = test.begin(); it != test.end(); ++it ){
        printf("Value = %lf\n",*it);
    }


    return 0;
}

This outputs, as expected, the following:

Value = 0.000000
Value = 1.000000
Value = 1.000000
Value = 2.000000

As you might be able to tell, it is implemented to follow a similar convention to the STL C++ library with respect to iterators (at least at a basic level).

$\endgroup$
  • $\begingroup$ I have no idea what the STL C++ library looks like but I appreciate your effort. I need to understand your solution better than I do. I'm not sure whether I can morph it into what we want or not. I'll probably post this on stack overflow since it is off topic here. $\endgroup$ – jep Dec 8 '16 at 22:14
  • $\begingroup$ @jep the STL is stuff like std::vector or std::map and how their iterators work. And yes, this is probably a better question for Stack Overflow. Best of luck. By the way, here's an explanation of STL: en.wikipedia.org/wiki/Standard_Template_Library $\endgroup$ – spektr Dec 8 '16 at 23:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.