Sunday, April 22, 2012

#13 April 22

The 6th project is under our belts...and the only hurdle between me and sunny freedom is Test 3. We'll cross that bridge later.

The final project was based on Conway's game of life, but with a slight variation of the cells - Fredkin cells, also in the fray. I think the whole point of the project was to appreciate inheritance and abstract base classes and how polymorphism could be used to construct generic heterogenous structures. It was also interesting because I just reviewed a paper on smart pointers, and the Cell class that we were writing was similar to one - a wrapper around a raw pointer whose memory management was being dealt with internally. It's interesting to write the copy-assignment and copy constructors for such classes, because you have to be very careful about strict ownership of the raw pointer(so that delete is called only once) and other possibilities of memory leaks.

The code for the FredkinCell and the ConwayCell in itself was not hard to do at all - especially since my partner and I spent a good amount of time thinking about the design (we did not give in to the temptation of writing monolithic un-maintainable monster functions to avoid unit tests - though it is kind of ambiguous whether you should write unit tests for every implementation of shared functions). Our initial design was the simple 1-pass update, in which every cell - dead or alive - was checking its neighbors and updating its state accordingly. However, when we ran the ConwayCell case using valgrind - this was taking nearly 8 minutes to run! There was an obvious need for re-design! So we re-designed it so that only the live cells would be checked and its neighbors' neighbor counts would be updated. Much faster, but wait! There's havoc on the generic Cell board. A FredkinCell won't update a ConwayCell which is diagonally its neighbor, and a ConwayCell will try to update the neighbor count of a diagonally placed FredkinCell whether it likes to or not! The realization was that we had to handle orthogonal neighbors (the kind that FredkinCells recognize) and diagonal neighbors differently. With that tweak, and some optimizations as far as vector handling was concerned, our run time using valgrind dropped to ~6s! And without the overhead of valgrind's checks, it runs in less than a second. Now that's what we're talking about!

Another interesting issue that I'm glad to have learned is the concept of the virtual destructor. If you get used to Java, you forget the dangers of polymorphism being turned OFF by default. One of these is the innocuous fact that the free destructors the compiler generates for you in C++ can be dangerous when it comes to inheritance hierarchies. If you assign a derived class object to a base pointer and then call delete on it, unless the base destructor has been marked virtual - this will cause undefined behavior (the base destructor will be invoked, not the derived one)! You have been warned!

No comments:

Post a Comment