3
\$\begingroup\$

I move back and forth between Python and C++ and I often need a nice/quick way to output STL objects to the screen for debugging purposes. I'd like the output to match the output of a comparable Python object, thus I have different templates for vectors, sets, etc...

Is this the best way to go about this?

template <class T>
ostream& operator<<(ostream& s, const vector<T> &A) {
  if(A.empty()) 
    return s << "[]";

  s << "[";
  typename vector<T>::const_iterator itr_penultimate = --A.end();
  typename vector<T>::const_iterator itr = A.begin();

  while(itr != itr_penultimate) {
    s << *itr << ", ";
    itr++;
  }
  return s << *itr << "]";
}
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

This looks fine, but perhaps a more idiomatic option would be to use ostream_iterator, or similar constructs with std::copy, to avoid the actual loop. See this link, for example. From the first answer:

std::ostringstream ss;

std::copy(v.begin(), v.end() - 1, std::ostream_iterator<int>(ss, ", "));
ss << v.back();

std::cout << ss.str() << "\n";
\$\endgroup\$
7
  • \$\begingroup\$ Can you give a simple example or point me in the right direction when you say "ostream_iterator or similar constructs with copy to avoid the actual loop"? Is this the method that uses std::copy? \$\endgroup\$
    – Hooked
    Aug 24, 2013 at 3:29
  • \$\begingroup\$ Added a quote from the link. Just surround with the outside delimiters of choice. :) \$\endgroup\$
    – sfjac
    Aug 24, 2013 at 4:13
  • \$\begingroup\$ Thanks, just learned about ostream_iterators and the copy. I realize you copied the post, but isn't it better to use std::endl rather than "\n" though? \$\endgroup\$
    – Hooked
    Aug 24, 2013 at 4:21
  • 1
    \$\begingroup\$ @sfjac: Based on one of the comments under that answer, the -1 is not needed. \$\endgroup\$
    – Jamal
    Aug 24, 2013 at 4:44
  • 1
    \$\begingroup\$ --end should be preferred as it will require a reversible container rather than a random access container. With some rather nasty looping you could even make it support forward-iterable-only containers, but since forward_list is the only one, there's probably no point. \$\endgroup\$
    – Corbin
    Aug 24, 2013 at 18:44
8
\$\begingroup\$

The above answer is fine.

The only problem I have is the extra ',' in the output:

std::cout << "[ ";
std::copy(data.begin(), data.end(), std::ostream_iterator<int>(std::cout, ", "));
std::cout << " ]\n";

Generates the output:

[ 1, 2, 3, 4, 5, 6,  ]
                  ^  extra comma

You can get around this with some extra work.

auto end = data.end();
if (!data.empty()) { --end;}
std::cout << "[ ";
std::copy(data.begin(), end, std::ostream_iterator<int>(std::cout, ", "));
if (!data.empty()) {std::cout << *end;}
std::cout << " ]\n";

Generates the output:

[ 1, 2, 3, 4, 5, 6 ]

This is better but it sort of defeats the purpose of using algorithms.

If we had used the loop it would look like this:

std::cout << "[";
for(auto loop = data.begin(); loop != data.end(); ++loop)
{
     std::cout << *itr << ", ";
}
std::cout << "]";

Of course this has the same problem as the first version of the algorithm above. So if we take that into account you can re-write like this:

std::cout << "[";
auto begin = data.begin();
if (!data.empty) {std::cout << *begin;++begin}
for(auto loop = begin; loop != data.end(); ++loop)
{
     std::cout << ", " << *loop;
}
std::cout << "]";

Now it works. And because it only use one test on empty is better than the altered algorithm version in my opinion. So what we really need is a version of the output iterator that does the above.

template<typename T>
class PrefexOutputIterator
{
    std::ostream&       ostream;
    std::string         prefix;
    bool                first;
    public:

    typedef std::size_t                 difference_type;
    typedef T                           value_type;
    typedef T*                          pointer;
    typedef T                           reference;
    typedef std::output_iterator_tag    iterator_category;

        PrefexOutputIterator(std::ostream& o,std::string const& p = ""): ostream(o), prefix(p), first(true) {}

        PrefexOutputIterator& operator*()       {return *this;}
        PrefexOutputIterator& operator++()      {return *this;}
        PrefexOutputIterator& operator++(int)   {return *this;}

        void operator=(T const& value)
        {
            if (first)      {ostream << value;first = false;}
            else            {ostream << prefix << value;}
        }
};

Now we can use this and get the output we want:

std::cout << "[";
std::copy(data.begin(), data.end(), PrefexOutputIterator<int>(std::cout, ", "));
std::cout << "]";

The output is:

[1, 2, 3, 4, 5, 6]

Nowadays though I seem to be using a lot of Json. So now I use this template librrys: https://github.com/Loki-Astari/ThorsSerializer

using ThorsAnvil::Serialize::jsonExport;
using ThorsAnvil::Serialize::jsonImport;

std::cout << jsonExport(data) << "\n"; // Serialize the array as json (looks like above)
std::cin  >> jsonImport(data);         // Reads a json array into an array.
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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