Bugs of the day, Wednesday 2013-03-20

Uninitialized class member

The problem:

struct Foo {
  int m_A;
  int m_B;
  int m_C;
  Foo():m_A(0), m_B(0) {}
  void Bar() {
    DoSomethingWith(m_C); // fail it
  }
};

My defense is that this was spread across a header and a .cpp file, which is to say I was careless. (My preference really is to put all code inline in one place — .h and .cpp is repetitive and costs time and effort to keep in sync)

This was some existing code that I reworked a couple of weeks ago and hadn’t got to the point of running/testing until today. I’m really not sure how I managed to remove the initializer here — it existed in the original version.

The fix:

Initialize to zero, which was not just known-value, but correct-starting-value.

Prevention:

Always initialize everything. Keep class definitions in sync with constructors and Init() methods.

Sliced inheritance

The problem:

class Bar {
  virtual void Baz() {/*nothing interesting*/}
};

class BarExtender: public Bar {
  virtual void Baz() {DoSomethingInteresting();}
};

class Foo : public Bar {
  Foo(Bar& bar): Bar(bar) {}
  void Yolo() { Baz(); }
};

BarExtender b;
Foo f(b);
f.Yolo(); // intended that this would DoSomethingInteresting(). Actually does nothing interesting

The fix:

class Foo : public Bar {
  Foo(Bar& bar): m_Bar(bar) {}
  void Yolo() { m_Bar.Baz(); }
  Bar& m_Bar;
};

Prevention:

Know the language well enough to know when you’re writing code that doesn’t say what you mean.

Undesired printf output

The problem:

This is the first “functional” bug — something that didn’t work quite the way I’d intended.

For a data serialization class, I had hoped to replace several functions of the form:

WriteInt32(int32_t i) { snprintf(..., "%d", i); }
WriteDouble(double d) { snprintf(..., "%f", d); }

to a single function:

WriteNumber(double d) { snprintf(..., "%f", d);  }

Unfortunately, the %f format specifier will, by default, always write a decimal point and 6 figures thereafter, which is undesirable for integers-types-cast-to-double written by this function — for particular use cases it doubles (hah!) the size of the data written.

The fix:

I’ve not tested this yet, but it looks like %g (or probably %.ng will provide the desired behaviour by not writing the surplus zeroes. I will need to determine if the output for floating point input and large integers will be acceptable.

Prevention:

I still have a lot to learn about printf format specifiers, though I’ve found http://www.cplusplus.com/reference/cstdio/printf/ useful when considering/trying to to understand the available options. This does make me wonder if a printf-format-string constructor exists analogous to http://cdecl.org

Update:

Rather than trying to find a single format that works for all input or testing the number before printing it, I’ve opted to overload the function by type. This way we can use knowledge of the type to do something appropriate and more efficient.

I have overloads for int, unsigned int, double, long and unsigned long, and a comment as to why there’s no implementation for unsigned long long (the loader treats all numbers as doubles, which means we could end up writing out numbers that we can’t read back without loss of precision along the way). I’ve also added static asserts in case this code gets compiled on a system where sizeof(long) > sizeof(int). There are separate functions for writing out bool and pointer values.

Leave a Reply

Your email address will not be published.