Whenever I program any little project, I go to some length to write some of the code in such a general way that I might be able to re-use it later. In a current project I want to write out numbers to std::cout (or any stream for that matter) in binary format. Instead of just writing a little function which does the trick, I decided that I want to have a manipulator, much the same as the hex or oct manipulator of the STL. So what I want to be able to write in my code is
std::cout << binary << x;
and then x would be written in binary format.
Let' start with the easier task. So first I want to be able to write
std::cout << toBinary(x);
Somehow my sense of programming style commands me not to take the obvious route and create a temporary string object which is then passed to the << operator. I want to write the binary directly to the stream. But then I also want to be able to use toBinary like this
std::string s = toBinary(x);
This means that toBinary can't be a straightforward function. Instead it is a class. I will derive it from an abstract base class like this
class toBinary : public stream_formatter {
private:
unsigned long val;
public:
toBinary(unsigned long val_) : val(val_) {}
virtual std::ostream& toStream(std::ostream& os) const
{
unsigned long tmp = val;
unsigned long tmp2 = 0;
int length = 0;
while (tmp != 0)
{
tmp2 = (tmp2<<1) | (tmp&1);
tmp = tmp>>1;
++length;
}
if (length==0)
{
os << '0';
return os;
}
while (length>0)
{
os << (tmp2&1)?'1':'0';
tmp2 = tmp2>>1;
--length;
}
return os;
}
};
Now we can overload the << operator for this class
std::ostream& operator<<(std::ostream& os, const stream_formatter &conv)
{
return conv.toStream(os);
}
This completes my first task, I can write std::cout << toBinary(x); In order to be able to assign a toBinary object to a string, I add the typecast operator to the toBinary class
operator const std::string()
{
std::ostringstream os;
this->toStream(os);
return os.str();
}
This does the trick and now I can write std::string s = toBinary(x); But what about my original quest? I thought that should be relatively straightforward. Alas, it isn't. After a little search on Google, I found this tutorial on how to write your own manipulators. The tutorial tells you how to write a manipulator that modifies the value of a number. It doesn't tell you how to write a completely new output handler from scratch. It turns out that it involves a little more inside knowledge of the STL than I thought.
I will keep trying and I will report here, when I know more.
Here is my attempt: https://github.com/Eelis/geordi/blob/master/prelude/bin_iomanip.hpp
ReplyDelete