Exceptions are the way one should handle results of operations in C++ (I would say the *only* way). Nobody bothers anymore with the old c-ish way of checking the function result error code because: lots of tests/ifs make your code harder to read (thus maintain) and second because, if not careful, it could lead to memory leaks.
Welcome the wonderful world of exceptions. Except in C++ there is no base exception class for everyone to use (yes, there is std::exception but IMO it was designed specifically for the standard library). We want a class that facilitates declaring exceptions, throwing them and that gathers a maximum of information about the context in which the exception was thrown initially.
I tried to keep the base exception class as simple as possible.
/**
Base class for all exceptions.</font>
*/
class Exception
{
public:
Exception(int code,
const char* file,
const char* line
const char* reason)
: m_code(code), m_file(file), m_line(line), m_reason(reason)
{
}
void Print(std::ostream& where) const
{
where << "Exception " << m_reason << "("
<< m_code << ") occured at "
<< m_file << ":" << m_line << std::endl;
}
void SetFile(const std::string& file)
{
m_file = file;
}
void SetLine(const std::string& line)
{
m_line = line;
}
void SetReason(const std::string& reason)
{
m_reason = reason;
}
const std::string& GetReason() const
{
return m_reason;
}
virtual ~Exception()
{
}
private:
int m_code;
std::string m_file;
std::string m_line;
std::string m_reason;
};
As you can see the exception class allows us to assign an exception code, store the file/line where the exception occured and even the reason for which the problem appeared. Of course the user of the class is not expected to put in explicitly the file name and the line no as parameters to the constructor but we will use preprocessor macros for that.
#ifndef STRINGIZE#define STRINGIZE(N) _STRINGIZE(N)
#define _STRINGIZE(N) #N
#endif#define THROW(code, reason) throw Exception(code, __FILE__, STRINGIZE(__LINE__), reason);
Don’t worry about the STRINGIZE thingy, special preprocessor incantation to convert the __LINE__ no.
So now, how would one go about using this class. Well, you should either derive your special exception class from Exception or throw Exception with a special code everytime. I tend to combine the two in my code.
So, for example, in order to best handle exception in say, networking layer, I would derive special Exception in the network namespace base class. Then when I can’t read from a socket I would write THROW(READ_ERROR, “Can’t read from socket”)