Tuesday, March 13, 2007

Losing Sleep Over A Compiler

If you are an overachiever like I think I am, you probably lose sleep over petty things; except that this isn't that simple. I am charged with maintaining and improving an application at work that was developed using Borland's C++Builder 6 a few years ago. I've sought to move the code to more modern compilers such as Microsoft Visual C++ 6.0 or Bloodshed DevC++ without success because Borland's compiler institutes a lot of dependencies on proprietary libraries. That particular IDE is no longer supported by Borland Corporation as of Dec 2006. Even DLLs developed in C++Builder are incompatible with Microsoft's or Bloodshed's IDE, defeating the whole idea of dynamic libraries in development. You can deploy DLLs built in either to an OS and they'll work fine - just if you wish to debug the DLLs, you better find the original compiler/debugger for that job.
Further demonstrating why the compiler you choose matters to the bit, there's something curious happening when I build versions of the application. At some point, the application is supposed to attempt to corrupt data on a hard drive (we test some features by creating error conditions and ensuring our chips handle them correctly - exception testing). The older version performs the task as expected, but the newer one doesn't. The problem is that the same exact command that accomplishes the task in the old version is used in the new one, but the OS responds differently. Not only that, it doesn't report any errors - the response message is the same whether it did the task or not! I spent a whole day analyzing memory dumps and data structures and I find no differences between these two applications (code changes are in an unrelated module). Walk the code in a debugger all you want, there's no difference as far as I can see.
This leaves one arena to blame - the compiler. Could there be build options that are causing this difference in behavior? I don't see any! Compiler options? Data alignment issues? If I had Windows source code, I could figure it out. My application calls the same win32 API function DeviceIoControl() - which is a pain to work with in its own right - and get the same status and error messages whether it did the task or not! There is some sense data I haven't looked at, but it shouldn't matter because it is the same in both cases. I don't know what the hell is going on, short of analyzing the assembly produced at compile time. Who knew that my assembly language class at school would come in handy like this (well, except this is an Intel assembly as opposed to what we use at school - MIPS). But it's made it vivid in my mind that in the end, the compiler choice does matter.
My tool set includes Microsoft Visual C++ 6.0, Microsoft Visual Studio C++ 2005 Express, Borland C++Builder 6, and Bloodshed DevC++ for C/C++ development. Choose your compilers wisely - it could make or break you. I find Microsoft's Visual C++ 6.0 IDE most flexible for big projects, unpolluted by the .Net stuff they've bundled in succeeding IDEs in Visual Studio.