Output Formatting with std::copy

I had to do some output formatting in C++ for showing the content of a buffer. Take for instance this buffer:

unsigned char buffer [] = 
{
    0x00, 0x01, 0x02, 0x03, 
    0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0A, 0x0B,
    0x0C, 0x0D, 0x0E, 0x0F
};

I wanted the output to be like this:

0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f,

The simples way to do it is like this:

for(int index = 0; index < sizeof(buffer); ++index)
{
   std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0')
      << (int)buffer[index] << std::dec << ", ";
   if((index+1) % 4 == 0)
      std::cout << std::endl;
}

That for loop achieves the goal. But then, I though, why not using std::copy? Well, the first thing you can come up with is this:

std::copy(
    &buffer[0],
    &buffer[sizeof(buffer)],
    std::ostream_iterator< unsigned char >(std::cout, " ")
);

But that can only produce this output:

  ? ? ? ? ? ?
 ? ? 

That happens because we used unsigned char instead of int for the ostream iterator. You can correct that like this:

std::copy(
    &buffer[0],
    &buffer[sizeof(buffer)],
    std::ostream_iterator< int >(std::cout, " ")
);

and get the output like this:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

But this is still far from what I want. The best approach to solve the problem is to use a class that encapsulates a character (or any other type that we want to print), provide a printing function for it and overload the operator<< to output instances of this class. This is shown bellow:

template < class T >
class Printer
{
   T value;
public:
   Printer(T val): value(val)
   {
   }

   void Print(std::ostream& os) const
   {
      os << "0x" << std::hex << std::setw(2) << std::setfill('0')
         << (int)value << std::dec << ", ";
   }
};

template < class T >
std::ostream& operator<<(std::ostream& os, Printer< T > const& elem)
{
   elem.Print(os);
   return os;
}

We can now transform the copy call to this:

std::copy(
    &buffer[0],
    &buffer[sizeof(buffer)],
    std::ostream_iterator< Printer < unsigned char > >(std::cout)
);

and the output is:

0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F

That is much better, but not yet perfect. To achieve the final goal, we can transform the Printer class to keep a count of the printer elements:

template < class T >
class Printer
{
   static int index;
   T value;
public:
   Printer(T val): value(val)
   {
   }

   void Print(std::ostream& os) const
   {
      os << "0x" << std::hex << std::setw(2) << std::setfill('0')
         << (int)value << std::dec << ", ";
      if((index+1) % 4 == 0)
         os << std::endl;
      index++;
   }
};
template < class T >
int Printer< T >::index = 0;

template < class T >
std::ostream& operator<<(std::ostream& os, Printer< T > const& elem)
{
   elem.Print(os);
   return os;
}

The copy call remains the same, and the output is:

0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f,

Of course, if you use this several times, you have to reset the static index from the Printer class. I'll leave that to you.

4 thoughts on “Output Formatting with std::copy”

  1. I’m not saying the second solution is simpler or even better. I’ve just shown how is it possible to do it.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.