Saturday, September 22, 2012

Compilers should be more evil

Recently I ran in to one of those occasions where you do something incorrectly that should not work, and it still does. In my case it had to do with inheritance of forward declared classes. A simplification of what was done can be seen below

Penguin.h
1:  class Bird;  
2:    
3:  class Penguin : Bird {  
4:  public:  
5:       Penguin();  
6:       virtual ~Penguin();  
7:       void BirdCall();  
8:  };  

Penguin.cpp
1:  #include "Bird.h"  
2:  #include "Penguin.h"  
3:    
4:  Penguin::Penguin() : Bird(false) {  
5:  }  
6:    
7:  Penguin::~Penguin() {  
8:  }  
9:    
10:  void Penguin::BirdCall() {  
11:       cout << "Awk awk awk!" << endl;  
12:  }  

Bird.h
1:  class Bird {  
2:       Bird() {}  
3:       bool canFly;  
4:  public:  
5:       Bird (bool canFly) { this->canFly = canFly; }  
6:       virtual ~Bird() {}  
7:       virtual void BirdCall() = 0;  
8:  };  

My real code was slightly more complicated than this and had a lot less to do with birds. The interesting thing was that the compiler managed to compile this into working code. Forward declared classes such as Bird on the first of Penguin.h cannot be used as base classes. If it could then it would be possible to create circular inheritance and that might very well be the end of the universe.

What probably happened was that the compiler had already parsed the Bird class once and was already aware of it when it came to parsing the Penguin class. However when the compiler "fixes" things like this for us it creates a confusing mess of how things work. The next class that tries to use a forward declared class as a base class might not work and the programmer has no clue why.

This kind of problem leaves a certain amount of room for "luck" when writing software. The is nothing wrong with being lucky, some people build entire careers on it (stock brokers, palm readers and so on). However most people distrust luck and prefers if things behave in a consistent manner and that they can acquire a knowledge on how to receive the same result in each case.  

There can be many things said about the error messages of C++ compilers but I would pick a cryptic error message any day over an inconsistent behavior. I think compilers should be more evil and tell us loud and clear when we do something erroneous.