Friday, February 09, 2007

Functors with state - 2

I was discussing about a problem here - Functors with State - 1

In continuation, there is a striking point in the functor I used. The member variable "index" is static. Why is it static? I did not know why I did that but at my first attempt I did not put it that way. I had kept it as a mutable non-static member variable. Here is how:

[CODE]

template<typename T>
struct IsEvenIndex{
               private:
                               mutable /*static*/ typename std::vector<T>::size_type index;
                               std::vector<T>& refVec;
               public:
                               bool operator()(const T& t) const{
                                               if (index%2!=0){
                                                               ++index;
                                                               return false;
                                               }else{
                                                               ++index;
                                                               refVec.push_back(t);
                                                               return true;
                                               }
                               }
                               IsEvenIndex(std::vector<T>& vec) : refVec(vec), index(0) {}
};

//template<typename T>
//typename std::vector<T>::size_type IsEvenIndex<T>::index=0;


But this created a problem. A problem that was reflected in the output of the program I showed earlier. Here is the output that I got using the above functor.

[OUTPUT]

Printing vector contents
10 20 30 40 50 60 70 80 90 100
Printing vector contents
30 50 70 90
Printing vector contents
10 20 40 60 80 100


Now, you must be wondering about what happened to the first element 10? It is in a different sequence now!

Also, insert (a suggestion for way to improve the code I presented earlier) did not work well, which led me to keep a reference to 2nd vector in the functor: [EDIT : clarification added : the functor used for this main is the first one (from the part 1 of this series) that had the static index member and not the one posted above]

[CODE]

int main(){
               std::vector<int> myintVector;
               std::vector<int> myotherintVector;
               std::vector<int> myanotherintVector;
               for(int i=1; i<=10; ++i){
                       myintVector.push_back(i*10);
               }
               PrintVector(myintVector);
               std::vector<int>::iterator itend = std::remove_if(myintVector.begin(), myintVector.end(), IsEvenIndex<int>(myotherintVector));
               myanotherintVector.insert(myanotherintVector.begin(), itend, myintVector.end());
               myintVector.erase(itend, myintVector.end());
               PrintVector(myintVector);
               PrintVector(myotherintVector);
               PrintVector(myanotherintVector);
}


It gave the following flawed output:

[OUTPUT]

Printing vector contents
10 20 30 40 50 60 70 80 90 100
Printing vector contents
20 40 60 80 100
Printing vector contents
10 30 50 70 90
Printing vector contents
60 70 80 90 100

What a mess! Isn't it? The functor can be improved to keep a bool toggling member rather than an int but I guess that would suffer the same issue. Try it out and let me know if it doesn't!

The static member variable seems like a necessity. I could not do without it!

The basic problem is using remove_if with a predicate having non-static state! There can be many different implementations of remove_if. There can be cases where remove_if calls another helper algorithm passing the predicate. Now, how would this predicate be passed? By reference or by value? This is unspecified by the standards. And this is the problem that has been haunting us! Our predicate contains state, an integer value. The implementations that I ran my sample codes above, used to call find_if first passing the predicate by value. When passed by value a copy is created, right? Now, it constructs the state with the default state and moves through a few elements until a match is found incrementing the state but when the control returns from the find_if algorithm, that changed state is not transferred to remove_if! That is the problem. The state here remains the initial one as before the call to find_if. And it proceeds with that "wrong" state forward. Surely, it will mess up things and as a fix that is the state that we maintained between those calls when we declared the member variable as static! Mystery solved!

Alternative solutions and how to make that code better comes in later posts! Keep having fun!

(Next installment here - Functors with state - 3)

No comments: