0

I use about 6 different C++ containers. I started writing print functions to output the container contents. Is this necessary? I would think this is part of the C++ library?

  void print_list(const list<int>& list_int)
    {
    for (list<int>::const_iterator it = list_int.begin(); it != list_int.end(); it++) cout << *it << " ";
    }
  void print_um(const unordered_map<int, double>& map_int_d)
    {
    for(unordered_map<int, double>::const_iterator it = map_int_d.begin(); it != map_int_d.end(); ++it)cout << "[" << it->first << "," << it->second << "] ";
    }

3 Answers3

5

It's not part of the library, but its easy to write with the tools provided:

C c; // Where C is a container type
std::copy(
    c.begin(), c.end()
  , std::ostream_iterator< C::value_type >( cout, " " )
);

For containers whose element is a pair (like map and I'd believe unordered_map) you'd need a custom Output Iterator to print the pair with the comma and brackets.

K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • @Chris Aaker: The `copy` algorithm from the C++ Standard Library. – K-ballo Oct 21 '11 at 22:33
  • OK...so what is being copied here...can you elaborate on your answer it looks very useful –  Oct 21 '11 at 22:38
  • `std::copy` copies the elements in the range specified by the first two iterators to the container referred to by the third iterator. The interesting thing here is that the third argument is an output iterator, but isn't linked to a container, but to `cout`, i.e. every time it's used to store a value (inside `std::copy`) it writes the value to the associated stream. – Matteo Italia Oct 21 '11 at 22:41
  • so it is copy function which takes 3 arguments, all iterators, two input (beginning and end) and one output(beginning)? And as a special case the third iterator is for the std:cout? –  Oct 21 '11 at 22:49
  • 1) Yes. 2) Yes, but it's just how `ostream_iterator` works, there's no special case in `std::copy` for it, `std::copy` is just a brutal copy like `for(;firstIt!=lastIt; ++firstIt, ++outIt) *outIt=*firstIt;`. Have a look at the documentation for `std::ostream_iterator`. – Matteo Italia Oct 21 '11 at 22:51
  • @Chris Aaker: The input is a range made by two Input Iterators. The output is an Output Iterator, which `ostream_iterator` models. Is no special case, is generic programming in action. – K-ballo Oct 21 '11 at 22:52
  • what is (cout, ""), does this function actually ouput the container? –  Oct 21 '11 at 22:57
  • @Chris Aaker: Is no function, is the construction arguments of the `std::ostream_iterator`. The first one is the stream where to output values, the second is an (optional) separator. – K-ballo Oct 21 '11 at 22:58
  • OK...sorry...how do I use it then? –  Oct 21 '11 at 22:59
  • @Chris Aaker: How do you use an `std::ostream_iterator`? Check the reference and search for examples on how to use it. You may also want to consider researching on the OutputIterator concept (as well as the other iterator concepts). – K-ballo Oct 21 '11 at 23:02
2

The code you give in your question has a hint as to why it is not part of the standard library. Your code uses square brackets and a comma with no spaces to show the pairs in the map. Other people may want it formatted differently so the options the standards committee had were:

  1. Provide a lot of formatting options.
  2. Provide no formatting options and make everyone who doesn't like their formatting roll their own.
  3. Do nothing and make everyone roll their own

They went with option three knowing that libraries would be developed that meet people's specific needs.

IronMensan
  • 6,761
  • 1
  • 26
  • 35
1

What about:

#include <iostream>
#include <map>
#include <algorithm>

template <typename K, typename V>
    std::ostream& operator<<(std::ostream& os, const std::pair<K,V>& p)
{
    return os << "[" << p.first << ", " << p.second << "]";
}

template <typename Container>
    std::ostream& operator<<(std::ostream& os, const Container& c)
{
    std::copy(c.begin(), c.end(), 
        std::ostream_iterator<typename Container::value_type>(os, " "));
    return os;
}

You might also be charmed about Boost Spirit Karma:

#include <boost/spirit/include/karma.hpp>

using boost::spirit::karma::format;
using boost::spirit::karma::auto_;

int main()
{
     std::vector<int> ints(1000);
     std::map<std::string, int> pairs();

     // ...

     std::cout << format(auto_ % " ", ints) << std::endl;
     std::cout << format(('[' << auto_ << ',' << ']') % " ", pairs) << std::endl;
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 2
    The first operator is ok as long as its not in the `std` namespace (because it would be ilegal), and is somewhat constrained from the rest of the program. The second one has the nasty side-effect that now **everything** has an `operator <<`, not just containers. – K-ballo Oct 21 '11 at 22:46
  • @K-ballo: yeah, I figure I'll start showing SFINAE for value containers and associative containers once the OP starts showing signs of life :) – sehe Oct 21 '11 at 22:57
  • This is why we need concepts... `template ...` would do the trick perfectly. – HighCommander4 Oct 22 '11 at 00:56