2008 07 02Less bugs through compiler optimizations
// Get a symbol void* sym = dlsym(handle, "function1"); // Cast it to correct signature double (*fn)() = sym; // Call it fn(); // Get another symbol void* sym2 = dlsym(handle, "function2");
// Cast it to correct signature
SomeStruct (*fn2)(float, float, float) = sym2;
// Call it
fn(1, 2, 3);
// Misassign new symbol to first pointer fn = sym2; // Call uninitialized function pointer fn2(1, 2, 3);
And *BUG* ! Hmm … strange … let's try that in a Release build, one of my Stupid Voodoo Programming Tactiques, and … it works ! I stare at the code for a stupid amount of time, dumbfounded, wondering the obvious bug lies. Oh. There. I'm getting a new function pointer but calling the former one with the new signature … oups ! I'm getting a new function pointer but assigning it to the first one, then calling that uninitialized pointer.
How come it works in Release but not in Debug ? In Debug, GCC optimizes nothing and allocates two function pointers. In Release, GCC allocates one function pointer and uses it in both calls. Therefore calling fn(1, 2, 3)
equals calling fn2(1, 2, 3);
: that's Invisible Release Build Bug.
Moral of the day : always Release builds ! :)
EDIT I messed up while cleaning the code for this post. I even messed up the diagnostic, but … not the explanation ! :) To sum it up : XCode won't warn you when calling uninitialized function pointers.Argh … I messed up while copying and cleaning up the code for posting. I didn't assign to fn2 and then I called that undefined function pointer. It looked like this :
// No assignment
SomeStruct (*fn2)(float, float, float);
// Misassign to first pointer
fn = sym2;
// Call of undefined pointer
fn2(1, 2, 3);
GCC does catch the bug in the post, but not that one.
Should you have declared fn as 'double (*fn)(void) = sym;' (note the void in between the parenthesis), I expect the compiler would have detected an error in any kind of build; wouldn't it?
I tried adding the void, to no avail. The problem doesn't seem to be the first function definition, but that you can call any function pointer that hasn't been initialized :
double (*fn)(void);
fn();
will crash without any compile-time warning.
Patrick: Yes, that will crash. But the compiler should raise an error if you try to call the function with a different number of parameters, as your original code did. In fact, I tried to compile the following with gcc and got an error:
double (*fn)(void);
fn();
fn(2);
I do get an error in this case. However, the original post is wrong — see my first comment, I copied the code and over cleaned it up to make it shorter.
Moral2: You might want to crank up warnings to the highest level. In that case, you would have gotten a warning that you assigned to fn2 but never used the value.
(Not sure if gcc does that - spending too much time in VS.NET land these days)