std::ios_base format flags are a resource that needs RAII

Suppose we wanted to write a class Fraction which would come equipped with an input operator>> that would accept input in the form -2/4 or 3/-8, but disallowing any spaces around the division bar. We would need the fraction class itself first, so, trying to stick to Test Driven Development rules, we would need to satisfy the following test function:

auto test_Fraction(void) -> void
  {
  Fraction f(3,-8);
  assert(f.Numerator()==3);
  assert(f.Denominator()==-8);
  std::cerr << "test_Fraction pass ok\n";
  }

This is fairly easy:

#include <iostream>
#include <cassert>

class Fraction
  {
    int numerator_;
    int denominator_;
  public:
    Fraction(int numerator, int denominator=1)
      : numerator_(numerator)
      , denominator_(denominator)
      {
      assert(denominator!=0);
      }
    auto Numerator(void) const -> int
      {
      return numerator_;
      }
    auto Denominator(void) const -> int
      {
      return denominator_;
      }
  };

We can now try to formulate our requirements for the input operator (remembering to add #include <sstream> at the very beginning of the file):

auto test_Fraction(void) -> void
  {
  Fraction f(3,-8);
  assert(f.Numerator()==3);
  assert(f.Denominator()==-8);

  std::istringstream in1("-2/4");
  in1 >> f;
  assert(in1);

  std::istringstream in2("-2/4 3/-8");
  in2 >> f;
  assert(in2);
  in2 >> f;
  assert(in2);

  std::istringstream in3("notnumber");
  in3 >> f;
  assert(!in3);

  std::istringstream in4("-2 /4");
  in4 >> f;
  assert(!in4);

  std::istringstream in5("-2/ 4");
  in5 >> f;
  assert(!in5);

  std::cerr << "test_Fraction pass ok\n";
  }

We may now start working on the input operator, to make it pass the tests. Let me present first a solution that will ignore the no–spacing around division bar requirement, and therefore will fail on tests in4 and in5:

std::istream& operator>>(std::istream& in, Fraction& f)
  {
  int n=0;
  int d=0;
  char c=' ';
  in >> n >> c >> d;
  if(in)
    if(c=='/' && d!=0)
      f = Fraction(n,d);
    else
      in.setstate(std::ios_base::failbit);
  return in;
  }

This is more or less the standard procedure: have some local variables that will be used for partial input, give them some well defined but otherwise irrelevant initial state to avoid random execution behaviour, do the reading, and if all partial input operations succeeded, check if the values pass the respective sanity check. If yes, assign result, if no, signal failure by failing the stream.

Now comes the task of correcting the above to allow for the no–spaces requirement. One of the possible solutions is to use a switch within the stream that decides whether the input operations ignore whitespace before input or not (there are other possibilities, such as using get and peek methods, but that’s not the point here). This switch is on by default, which means that the streams from our test function allow for whitespace. Let us try then to turn it off after the first read:

std::istream& operator>>(std::istream& in, Fraction& f)
  {
  int n=0;
  int d=0;
  char c=' ';
  in >> n >> std::noskipws >> c >> d;
  if(in)
    if(c=='/' && d!=0)
      f = Fraction(n,d);
    else
      in.setstate(std::ios_base::failbit);
  return in;
  }

As a result, we get a pass on tests in4 and in5, but we fail on something that worked already, that is on in3 (to see that in4 and in5 succeed comment out the in3 and rebuild). This is because we operate on the stream by reference, and since we turn off whitespace skipping before the first division bar, the stream will still be in this state when we try to read the second number. And the first character in the stream not consumed by previous input is the space between the numbers. This means that we need to revert the switch before we return from the input operator. Simply turning it on is not an option, because it might have been already turned off on purpose, we need to figure out the initial state and restore it at the end.

The skipws switch is implemented as a flag, a particular bit value that comes together with other bits–switches in a number that can be obtained as the result of the flags method of the stream. We are not interested by the numerical value of that number, but merely on the state of individual bits within. To read out the state of a particular bit in that number, we need to calculate a bitwise–and with a number with just this one bit on, called a mask. The mask we need has a convenient name, std::ios_base::skipws, and hence, the resulting code would be

std::istream& operator>>(std::istream& in, Fraction& f)
  {
  int n=0;
  int d=0;
  char c=' ';
  bool skipws_was_on = (in.flags() & std::ios_base::skipws);
  in >> n >> std::noskipws >> c >> d;
  if(skipws_was_on)
    in >> std::skipws;
  if(in)
    if(c=='/' && d!=0)
      f = Fraction(n,d);
    else
      in.setstate(std::ios_base::failbit);
  return in;
  }

That attempt finally passes all the tests, but raises another question. We modify the state of an object that will outlive the present operation with the intent to revert the modifications at exit. However, in a world with exceptions, we need to ask whether we revert at every possible execution exit path of the given piece of code. And the answer is no, any of the reading operations in in >> n >> std::noskipws >> c >> d; may throw (at least if you change int to your favourite BigInt), and while it may seem unlikely that the flags of the stream will matter after a throw, we cannot be sure. Hopefully, handling of such cases is well understood from cases that deal with resource management, such as dynamic memory allocation. The answer is RAII, the Resource Allocation Is Initialization pattern applied to the flags. We need a class whose objects will remember the flags on construction, and restore them on destruction:

class RestoreFlagsOnExit
  {
    std::ios_base& in_;
    std::ios_base::fmtflags flags_;
  public:
    RestoreFlagsOnExit(std::ios_base& in)
      : in_(in)
      , flags_(in.flags())
      {}
    ~RestoreFlagsOnExit(void)
      {
      in_.flags(flags_);
      }
  };

std::istream& operator>>(std::istream& in, Fraction& f)
  {
  int n=0;
  int d=0;
  char c=' ';
  RestoreFlagsOnExit restore(in);
  in >> n >> std::noskipws >> c >> d;
  if(in)
    if(c=='/' && d!=0)
      f = Fraction(n,d);
    else
      in.setstate(std::ios_base::failbit);
  return in;
  }

In the above code we remember the entire switch set, not just the skipws flag, together with the reference to the given stream, in an object whose destructor restores the flags to the stream. This object is not assignable, due to the reference it holds, but it is copyable. We could disable the copy constructor by declaring RestoreFlagsOnExit(const RestoreFlagsOnExit&) = delete;, but having multiple copies and thus restoring flags many times is not really that dangerous, as opposed to the case of freeing memory.

In fact, the above remarks apply to any situation where we have to setup a long lived object to perform some operation. Imagine there were no iterators to a collection, but an attribute with a pointer to current element in the collection itself. The right to modify this pointer would become a resource in need of management. The externalization of the pointer is thus one of the strengths of the iterator pattern.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: