lucasg
lucasg

Reputation: 11012

How to create specific std::vector iterators?

I'm working on a really badly designed project, and I've stumbled upon this data structure :

class OldBagOfData 
{
public:
    std::vector< BaseClass* > baseDatas;
    std::vector< Derived1* > derived1Datas;
    std::vector< Derived2* > derived2Datas;
    std::vector< Derived3* > derived3Datas;
    std::vector< Derived4* > derived4Datas;

}

The method to update the class is using a awful lot of if/else conditionals (several dozens) and the members are modifiables (on top of that it is using pointers instead of instances) even though we are only reading the datas.

I've already simplified the code by using generic functions and template :

class CurrentBagOfData 
{
private:
    std::vector< BaseClass* > genericContainer;

    Template< typename DataType>
    std::vector< DataType* > getData( datatype IDtype);

public:
    std::vector< BaseClass* > getbaseDatas(); /* = getData<Base>("base") */
    std::vector< Derived1* > getDerived1Datas(); /* = getData<Derived1>("derived1") */
    std::vector< Derived2* > getDerived2Datas(); /* = getData<Derived2>("derived2") */
    std::vector< Derived3* > getDerived3Datas(); /* = getData<Derived3>("derived3") */
    std::vector< Derived4* > getDerived4Datas(); /* = getData<Derived4>("derived4") */

}

However, since I'm only reading datas and queuing new input , I would like to use iterators :

// This loop is forbidden because obod.getDerived1Datas() is a temporary object
for( std::vector<Derived1*>::iterator it = obod.getDerived1Datas().begin();
                                         it != obod.getDerived1Datas().end(); i++)
{
  /* processing *it */
}

//What I want to do :
for( std::vector<Derived1*>::iteratorDerived1 it = obod.begin(); it != obod.end(); i++)
{
  // it iterate over every Derived1 datas in the generic container
  /* processing *it */
}

How can I create std::vector::iteratorDerivedX ? Any other advices on my design is welcomed.

Upvotes: 0

Views: 205

Answers (3)

lucasg
lucasg

Reputation: 11012

I've found a method using templated iterator. Unfortunately it add a lot of overhead so I am not sure I will use it :

#include <iostream>
#include <vector>

typedef std::string datatype ;

class BaseClass{
public:
    BaseClass():type("base"){}
    datatype type;

};

class Derived1 : public BaseClass{
public:
    Derived1(){ type= "derived1"; }
};
class Derived2 : public BaseClass{
public:
    Derived2(){ type ="derived2"; }
};
class Derived3 : public BaseClass{
public:
    Derived3(){ type ="derived3"; }
};
class Derived4 : public BaseClass{
public:
    Derived4(){ type ="derived4"; }
};


class CurrentBagOfData 
{
private:


    template< typename DataType>
    std::vector< DataType* > getData( datatype IDtype)
    {
        std::vector< DataType* > output;
        for(int i=0; i< genericContainer.size(); i++)
        {
            if(genericContainer[i]->type == IDtype)
            {
                output.push_back( (DataType*) genericContainer[i]);
            }

        }
        return output;
    }

public:

    // Begin of the specialized container
    template< class DataType>
    std::vector< BaseClass* >::iterator begin()
    {
        std::vector< BaseClass* >::iterator it = genericContainer.begin();


        datatype type = DataType().type;
        while( it != genericContainer.end() && (*it)->type != type   )
        {
            it++;
        }

        return it;

    }

    // End of the specialized container
    template< class DataType>
    std::vector< BaseClass* >::iterator end()
    {
        std::vector< BaseClass* >::iterator it = genericContainer.begin();
        std::vector< BaseClass* >::iterator output = it;

        datatype type = DataType().type;
        while( it!= genericContainer.end() )
        {
            it++;


            if( it!= genericContainer.end() && (*it)->type == type )
            {

                output = it;
            }

        }



        return output;

    }


    // Iterate over a certain type of elements in the container
    template< class DataType>
    void gen( std::vector<BaseClass*>::iterator &it)
    {
        const std::vector< BaseClass* >::iterator e =  this->genericContainer.end();

        // Mandatory increment
        if(it!= e )
            it++;

        // Loop until next DataType elem
        datatype type = DataType().type;
        while( it!= e && (*it)->type != type   )
        {
            it++;
        }

    }


    std::vector< BaseClass* > getbaseDatas(){ return getData<BaseClass>("base"); }
    std::vector< Derived1* > getDerived1Datas(){ return getData<Derived1>("derived1"); }
    std::vector< Derived2* > getDerived2Datas(){ return getData<Derived2>("derived2"); }
    std::vector< Derived3* > getDerived3Datas(){ return getData<Derived3>("derived3"); }
    std::vector< Derived4* > getDerived4Datas(){ return getData<Derived4>("derived4"); }


    std::vector< BaseClass* > genericContainer;






};



int main()
{
    // Object
    CurrentBagOfData cbod;

    cbod.genericContainer.push_back(new(BaseClass));
    cbod.genericContainer.push_back(new(Derived1));
    cbod.genericContainer.push_back(new(Derived3));
    cbod.genericContainer.push_back(new(Derived2));
    cbod.genericContainer.push_back(new(Derived1));
    cbod.genericContainer.push_back(new(Derived4));
    cbod.genericContainer.push_back(new(Derived3));
    cbod.genericContainer.push_back(new(Derived3));

    // Loop on Derived4 object, using the original method
    std::vector< Derived4* > o = cbod.getDerived4Datas();
    for (int i=0; i < o.size(); i++ )
    {
        std::cout << o[i]->type << std::endl;
    }
    std::cout << std::endl;

    // Loop on Derived3 objects, with custom iterators.
    std::vector< BaseClass* >::iterator it;
    for( it = cbod.begin< Derived3 >(); it <= cbod.end< Derived3 >(); cbod.gen< Derived3 >(it) )
    {
        std::cout << (*it)->type << std::endl;
    }

}

Upvotes: 0

You can't (as far as I know) add a new member to the std::vector class. You could, however, subclass it, so you could define your own iterators.

On the other hand, you could add some new methods to your CustomBagOfData class, something like:

public:
    std::vector<Derived1*>::iterator getDerived1Begin() { 
        return getDerived1Datas().begin()
    }
    // And the same for getDerived1End

However, despite not knowing how DefinedN classes are implemented, I'd suggest getting rid of those magic numbers (Derived1, Derived2, ...) and doing something more elegant with parameters.

Upvotes: 0

Akobold
Akobold

Reputation: 946

you can hold the return of the function call before the for loop;

Also, you are calling the end() method every iteration, which can be expensive.

And for iterators, pos increment is more expensive than pre incrementors.

std::vector<Derived1*> tmp = obod.getDerived1Datas();
for( std::vector<Derived1*>::iterator it = tmp.begin(), ed = tmp.end(); it != ed; ++i)
{
  /* processing *it */
}

Upvotes: 1

Related Questions