You often hear people say things like “It’s memory management that makes C++ difficult”, this is patently false. What makes C++ so incredibly difficult for new users ( and experienced ones! ) is the complexity. Note, I didn’t say difficulty, I said complexity.
Today was one of those perfect examples, one of those experiences that I want to point at and say “THIS IS WHY YOU SHOULD LEARN WITH A DIFFERENT LANGUAGE!”. Yes, that was shouting.
Here’s the story. Today I had a reader request guidance on how to make a release build of my C++ tutorial. This was a really good request, so I put it together in tutorial form. Something quickly dawned on me… I’ve never built this tutorial in release mode. It was pretty obvious that I hadn’t because, well, it didn’t work! Oops, my bad.
Let’s take a look at what happened, I want to see if you can guess what the problem is. A gold star to whoever gets it before I reveal the answer at the end of this post. That gold star offer applies to C++ veterans and new developers alike, this is a somewhat tricky one, especially considering the starting point!
So, I was building Pang 9 ( my Pong tutorial ) in release mode. I downloaded the project files from here if you want to follow along at home. I simply extracted the project, then I downloaded the full SFML 1.6 SDK, imported the Visual Studio 2008 project into Visual Studio 2010 and compiled the DLLs for multithreaded release mode, and copied those DLLs into my release directory. Code compiles just fine under release, so then I run it ( within Visual Studio ) and:
Uhoh… this can’t be good! The offending line of code is right here:
sf::Music* SoundFileCache::GetSong(std::string soundName) const
std::map<std::string,sf::Music *>::iterator itr = _music.find(soundName);
if(itr == _music.end())
sf::Music * song = new sf::Music();
if(!song->OpenFromFile(soundName)) <----- EXCEPTION IS HERE
soundName + " was not found in call to SoundFileCache::GetSong");
..... SNIP ......
Well, there is a big bad pointer right above the line, that seems an obvious candidate doesn’t it? Well don’t waste your time on that train of thought, the pointer isn’t the issue. The .find call seems a likely candidate too, it isn’t though.
Hmm, lets start looking there, ill set a breakpoint and trace into the _music.find() call. Oh yeah, this a release mode only bug, so our hands our tied on the debugging side of things… great. Alright, lets take a closer look.
<Bad Ptr>. Oooh, that’s not good. Obviously I’ve done a bad allocation here somewhere, but that just doesn’t make sense, not with the way this data type works. In fact, the only place these pointers are even allocated is in this actual method, and we haven’t even got to that code yet!
For some reason, our empty map ( it hasn’t been used yet, this is the first call ), is returning a Bad Ptr instead of working as expected. This can’t be right? But wait… that’s a red herring anyways, isn’t it, after all our exception has nothing to do with the Bad Ptr or the std::map at all, this is just the IDE sending us on a wild goose chase. We can literally boil it down to exactly two lines of code:
sf::Music * song = new sf::Music();
Now we are getting somewhere, we have isolated our reproduce case down to exactly a two line program! Hmmm, both are just standard SFML calls. Short of being out of memory, nothing here should be able to fail. So then, what the hell is causing this problem???
// ANSWER TIME, got your guess yet? Did you get it right and I owe you a gold star? Let’s find out!
Well, long story short, it’s our SFML dlls built for Visual Studio 2010. See, this is our first method call into any SFML DLL and it’s causing an explosion… but, why the heck is that???
Well, lets take a trip over to the SFML 1.6 project and check out how I built them. All I did was download the SFML 1.6 project for Visual Studio 2008, delete all the examples and set my build mode to Release DLL and compiled everything. Hidden between the various warnings was one very very very important but cryptic message:
A bunch of warnings as a side effect of the import process, and one extremely important warning… the ahah moment if you will:
Warning 8 warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library C:\temp\t\SFML-1.6\build\vc2008\LINK sfml-graphics
Now here is the thing, with C++ libraries need to be IDENTICAL. You link debug to debug, multithread to multithread, etc… At the same time, you have to link Visual Studio 2010 binaries to Visual Studio 2010 binaries ( although not all versions are binary incompatible, VS 2005 and 2008 could share libraries I believe ), and this here is the source of all the trouble!
The sfml-graphics graphics library statically links to a lib called freetype, and this library isn’t compatible with Visual Studio 2010. Download the newest version, extract it into the extlibs folder, recompile and PRESTO, problem solved.
So there you go, an unhandled exception in a call to open an audio file ended up actually being caused by a statically linked font library in a 3rd party graphics dll!
Still think it’s memory management that makes C++ tricky for beginners? The biggest problem is, new developers run into this stuff almost immediately. You need to conquer so much to get up and running with C++, the linker being one of the earliest and most daunting obstacles. I honestly don’t expect anyone with a few months of programming experience to have been able to puzzle this one out. It’s exactly these kinds of things that make people throw up their hands saying “Oh screw it, programming is too hard!”. I don’t like a quitter, but frankly in this case… they are right!
Of course, this is by no means limited to C++. I have had very similar experiences with Java where some XML file feeding another XML file feeding a code generator called by Maven puked out a message code like “Error, unknown problem”. Thing is, with no other programming languages do you encounter this kind of problem until you are ready to deal with the complexity. In C++, you start dealing with this crap on day 1.
So this, is one such reason why I say C++ isn’t a beginner friendly language!