Tuesday, February 13, 2007

Functors with state - 3 (print contents of vector using std::copy)

I had discussed an issue with function objects containing state and the problems they lead to (especially for std::remove_if algorithm) because of copy creation of those due to argument passing by value. The way they should be passed around is not guranteed by the standards. Here is the starting point for that discussion - Functors with state - 1.

There can be multiple things that can be improved in that code. The first one that I will pick up is the way the function "PrintVector" is laid down.

[CODE]

template<typename T>
void PrintVector(const std::vector<T>& t){
        std::cout << std::endl << "Printing vector contents" << std::endl;
        for(typename std::vector<T>::size_type i=0; i<t.size(); ++i){
                 std::cout << t[i] << '\t';
        }
        std::cout << std::endl << std::endl;
}

There are problems with this code. It is not flexible enough to let you choose the stream where you want the output to be pushed. Secondly, it uses loops and that loop leads to a third problem and that is the repeated call to vector<T>::size() function.

When we have algorithms at our disposal and feel they can improve the code, we should use them. Here is a better way to write the PrintVector function:

[CODE]

#include<vector>
#include<iostream>
#include<algorithm>
#include<iterator>
#include<string>

template<typename T>
void PrintVector(std::ostream& ostr, const std::vector<T>& t, const std::string& delimiter){
        std::copy(t.begin(), t.end(), std::ostream_iterator<T>(ostr, delimiter));
}

A simple one liner!

Another way to better it could be making it independent of the container type. Currently it would work for std::vector only (as demanded as the second argument). We can use iterator inputs (forward iterators should suffice). Here:

[CODE]

#include<iostream>
#include<algorithm>
#include<iterator>
#include<string>

template<typename T, typename InputIterator>
void Print(std::ostream& ostr, InputIterator itbegin, InputIterator itend, const std::string& delimiter){
        std::copy(itbegin, itend, std::ostream_iterator<T>(ostr, delimiter));
}

Now, this Print template can be used to print contents of any sequence that supports input iterators i.e. std::vector, std::deque, std::list, std::string and even plain arrays. The output stream can be anything, a file or the console or anything deriving from std::ostream and similarly we have generalized the delimiter between two subsequent elements that previously was a tab to any string.

More, later! Have fun!!

(Next installment here - Functors with state - 4)

2 comments:

Ola said...

Great example, it really helped me out.

A minor error, however, as you need to extract the char* from the delimiter string when constructing the ostream_iterator:

delimiter.c_str()

The Print function can also be made a bit simpler, by extracting the value_type from the InputIterator:

std::copy(itbegin, itend, std::ostream_iterator<typename InputIterator::value_type>(ostr, delimiter.c_str()));

Again, thanks. I would never have come up with a solution as good as yours myself!

Andrea Turli said...

Hi,

Great Job! Thank you very much!
I've just one more question: can you provide an example of usage of "Print" function?

Thank you again