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.
Seems like overdesign to me. The first solution is better (simpler and more flexible).
I’m not saying the second solution is simpler or even better. I’ve just shown how is it possible to do it.
You really helped me out with your template idea in std::copy! Thanks!