I was working on a new feature for our renderer so that we could make some new measurements. I wrote up some sloppy code and got it working just fine, but decided to try to clean it up. I always feel bad committing bad code. It doesn’t help that there were 3 recent checkins that all blamed me in some way for a bug. I don’t think they should “blame” me if I documented it :).
Anyhow, I decided to do this whole feature properly and I ran into a pretty weird bug. Telling the story of a bug well takes more time than I want to spend so I’ll just give you some snippets of the badness. First, we have this class, Hider that has tons of pointers. Instead of setting them all to NULL manually, we had this code:
Hider::Hider() {
memset(this, 0, sizeof(this));
}
The feature I was writing added a new class, AggregatedShadingPacket which subclassed ShadingPacket. Here are the prototypes. The actual details are there to add narrative value to this riveting story.
class ShadingPacket {
virtual void GetMpVertices(...) = 0;
};
class AggregatedShadingPacket : public ShadingPacket {
virtual void GetMpVertices(...) { ... }
};
I proceeded to add a AggregatedShadingPacket to Hider. We didn’t need to lazy load it like everything else, so I didn’t make it a pointer. This resulted int:
class Hider {
private:
AggregatedShadingPacket triAgg;
};
We had some existing functions that operated on ShadingPacket*. The idea was to now call those using &triAgg. For example, we had:
void Hide(ShadingPacket* pkt) {
pkt->GetMpVertices(...);
}
After hitting compiling and resolving all the errors, I ran it, anticipating a great success. Instead, it seg faulted! About an hour in gdb didn’t seem to yield anything too off the mark. It wasn’t until we noticed that pkt’s v_tableShadingPacket was set to 0x0. That can’t be good.
It didn’t matter at that point. It was late enough in the game that I just had to revert to the old solution. It wasn’t until about an hour later, while I was grading midterms, that I realized what was going wrong. That line in Hider::Hider() that called memset was setting the vtable pointer to 0! Badness.
There is no real moral of the story. I ended up fixing the bug and committing the nice code that I wanted. My code was needed to generate one number, and after it did, it was never needed again. Personally, I hate not knowing when something wonky goes on. There is something deeply satisfying about getting it right in the end.
Sorry, I wrote a lot of code because I really really like the syntax highlighter.