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