Along with many others, I don’t really recommend C++ as someone’s first language for various reasons. Sometimes concrete examples aren’t easy to come by off the tip of your tongue, so I figured the next time I encountered one of those things that make C++ so beginner unfriendly, I would post it. Not the obvious stuff like memory leaks, dealing with the linker or exceptionally cryptic template errors, but the more benign stuff that add up to frustrate new and experienced users. If you are a veteran C++ coder, you will probably spot the problem in a second or two, but this is the kind of thing that will trip up a beginner completely and is a complete nightmare to solve.
Consider the following C++ header file:
#pragma once #include "SFML/Audio.hpp" class SoundFileCache { public: SoundFileCache(void); ~SoundFileCache(void); const sf::Sound& GetSound(std::string) const; const sf::Music& GetSong(std::string); private: static std::map<std::string, sf::Sound> _sounds; static std::map<std::string, sf::Music> _music; }; class SoundNotFoundExeception : public std::runtime_error { public: SoundNotFoundExeception(std::string const& msg): std::runtime_error(msg) {} }
Pretty straight forward stuff right? Two class declarations, nothing really funky going on. Now consider the following implementation:
#include "StdAfx.h" #include "SoundFileCache.h" SoundFileCache::SoundFileCache(void) {} SoundFileCache::~SoundFileCache(void) {} const sf::Sound& SoundFileCache::GetSound(std::string soundName) const { std::map<std::string,sf::Sound>::iterator itr = _sounds.find(soundName); if(itr == _sounds.end()) { sf::SoundBuffer soundBuffer; if(!soundBuffer.LoadFromFile(soundName)) { throw new SoundNotFoundExeception( soundName + " was not found in call to SoundFileCache::GetSound"); } sf::Sound sound; sound.SetBuffer(soundBuffer); _sounds.insert(std::pair<std::string,sf::Sound>(soundName,soundBuffer)); } else { return itr->second; } } const sf::Music& SoundFileCache::GetSong(std::string soundName) { //stub }
Again, pretty standard code, ignore the fact GetSound and GetSong don’t return values, they aren’t the issue here.
Now consider the error:
error C2533: 'SFMLSoundProvider::{ctor}' : constructors not allowed a return type
If you are new to the expression ctor, it basically just shorthand for constructor. For the record, it’s Visual Studio Express 2010 and if you double click that error, it brings you to this line:
SoundFileCache::SoundFileCache(void) {}
So… what’s the problem? By the error, it is quite obvious that the constructor doesn’t in fact return a value, so the message is clearly not the problem.
What then is the problem? I’ll show you after this brief message from our sponsors…
Welcome back… figured it out yet? If not, I don’t blame you, the warning sure as hell didn’t help. Here then is the offending code:
class SoundNotFoundExeception : public std::runtime_error { public: SoundNotFoundExeception(std::string const& msg): std::runtime_error(msg) {} } <-- Missing semicolon
So, forgetting a semi colon ( something you will do A LOT! ) results in a message that your constructor cannot return a value. Now, once you’ve been coding C++ for a while this kind of stuff becomes pretty much second nature. Getting nonsensical error messages? Check your header for a missing include guard or semi colon become pretty much the norm. But for a new developer, this is the beginning of a complete train wreck.
Think about if for a second, you just developed some of your first code, the error says you are returning something you aren’t, the compiler is pointing at your constructor and now you need to figure out just WTF is going on.. What do you do? Many run off to a forum and post the error verbatim and hope for an answer ( which they will probably get, learning nothing in the process ). Hopefully you Google it using exact quotes, but even then you get forum answers like this where you have a bunch of other new developers fixating on the error message itself and leading the developer down a wild goose chase. Fortunately a moderator stepped in and gave a proper explanation, but that doesn’t always happen. Even worse, it is a legit error, you really can’t return from a ctor, so if you encounter it again in the future you may actually have a real problem but find yourself instead on a missing semi-colon hunt!
How would this work in Java, C#, Python or Ruby? Well it wouldn’t, as no other modern language I can think of use header files any more. For good reason too, they add a level of complexity for very little positive return. It could be argued that separating interface from implementation is “a good thing”, but even that is munged up by the fact you can actually have you implementation in your header file. Also don’t get me wrong, other languages have their faults too, just wait till you get forced to go on your first Java XML configured error hunt, you will be begging for C++ errors again!
This is just a classic example of the little things experienced developer forget the pain of experiencing in the first place! As I said, once you’ve got a bit of experience this kind of stuff becomes second nature, but while you are learning this kind of error is absolutely horrific. It’s little things like this that add up and make it so hard to recommend new developers start with C++. When I say it isn’t pointers and memory management that make C++ difficult, I mean it. It’s crap like this.