I never got a chance to finish my implementation of GLSL hardware shadow mapping two years ago when I built my EnLight engine and I’ve been struggling with the idea of starting work on it again but somehow between work and everything else in two whole years I haven’t written a single line of code towards that goal. I did however implement something in Cg in my engine but that was for a different project.
Anyway…having some time on my hands I’ve built my compile environment and was ready to start working on it a bit when I discovered that after recompiling, now that I had upgraded to Visual Studio 2008 – and a new compiler, my old demos would fail. After some brief debugging I came to the conclusion that I had made an odd mistake.
Here’s the deal, from my time in school I clearly recalled that C++ constructors can be called like any other function – there are many reasons why C++ allows this but I won’t get into it. This, in the hands of someone who, like me, has worked with other OO languages like Java and C# can be lethal. While in Java and C# it’s perfectly healthy for one constructor to call another constructor of the same class to do some initialization, in C++ things tend to get quite perverted. You see, I had used this mechanism(which I thought worked) to call from a more complex constructor the basic constructor to set may member variables to default values.
So, my old code was like this:
GLSLProgram::GLSLProgram() :
m_vsHandle(0), m_fsHandle(0), m_progHandle(0), m_inUse(false)
{
}
GLSLProgram::GLSLProgram(File* sourceVS, File* sourceFS)
{
GLSLProgram();
(void)this->Load(sourceVS, sourceFS);
}
In my ignorance I thought this did the following: construct my object, call the basic no args constructor and initialize my members to zero and then continue with the rest of my constructor code. What really happened was this: the object was indeed constructed but the constructor call from within the constructor created another object which it initialized and then destroyed as it exited the call. Strangely enough this “worked” with VS 2005’s cl compiler and gcc for windows from the mingw package. What probably happened was that my member variables were initialized to 0 by the compiler anyway and I didn’t realize what happened. VS 2008(well, the cl compiler in VS 2008) seems more unforgiving and more strict initializing my variables (and these are pointers!) to random numbers to teach me a lesson that I will not soon forget.
To fix my code, I did this:
GLSLProgram::GLSLProgram(File* sourceVS = 0, File* sourceFS = 0) :
m_vsHandle(0), m_fsHandle(0), m_progHandle(0), m_inUse(false)
{
if (sourceVS || sourceFS)
{
(void)this->Load(sourceVS, sourceFS);
}
}
As you can see, I used the old default parameter values trick C++ has up its sleeve. While this is regarded as dangerous by some developers I find it useful as an alternative to function overloading. Sure, fancier languages like Java and C# have dropped this feature forcing developers to use proper overloading but C++, like the old saying goes, lets you shoot yourself in the foot. It’s actually funny to have code that is legal but useless.
That’s C++ for you, always with the hidden jokes ;).
Your site very interesting! Keep up the remarkable work!