Tuesday, 19 April 2011

C++ Binary Stream Manipulator

I would consider myself an enthusiastic C++ programmer. I don't know all the internals of the STL library or all the C++ standard but I know how template programming works and frequently make use of the boost libraries.

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.

1 comment:

  1. Here is my attempt: https://github.com/Eelis/geordi/blob/master/prelude/bin_iomanip.hpp

    ReplyDelete