views:

148

answers:

5

I am playing around with C++ code in my spare time, working on putting together a library that could be used somewhat generically for building RPG type games. Mostly it is focusing on the underlying data structures and functions. I am still a long ways off from working with graphics.

My question is this: How complicated should exceptions be. I have a basic exception that looks like this:

Header:

#include <iostream>
class Exception
{
protected:
   std::string messageM;
public:
   Exception() throw();
   Exception(std::string message) throw();
   Exception(std::string file, int line) throw();
   Exceptoin(Exception & exception) throw();
   virtual ~Exception();

   virtual std::string getMessage(void) throw();
   virtual void setMessage(std::string message) throw();
};

The constructors are mostly just a bunch of "messageM = message" and that sort of thing.

Exception(std::string, int) uses stringstream to convert the number to a string.

Is this too complex?

I want the average exception call to be something along the lines of:

Exception exception("Message Here");
throw exception;

or

Exceptoin exception(__FILE__, __LINE__);
throw exception;

Also, on a related note, does C++ allow you to create the exception and throw it at the same time. Things along the lines of throw(Exception exception("My Message")); seem to give me errors.

^_^ Thanks.

+1  A: 

Just answering the last part of the question:

Also, on a related note, does C++ allow you to create the exception and throw it at the same time. Things along the lines of throw(Exception exception("My Message")); seem to give me errors.

Try

throw Exception("My message");
Diaa Sami
A: 

You can also create class methods (static methods) on the exception.

e.g.

MyException::throw("my message")

I often create a static method when I want to check for a certain error condition, and if it is true, throw an exception.

e.g. BadArgument::Check("mymethod", "paramName", paramvalue)

Larry Watanabe
Are you really allowed to use "throw" as a function name?
Ropez
@Ropez, no you're not. It's a keyword so no dice there.
KayEss
"raise" is a natural alternative.
outis
+4  A: 

Take a look at the boost documentation on the subject of exceptions, which has some guidance on designing exception classes. You should really consider just using boost, actually.

Tim Sylvester
I will take this advice and give boost::exception a look over.
+2  A: 

While it doesn't look too complex you might consider

  • using / deriving from suitable std exceptions and use stds conventions
  • throw away setMessage() - the constructors are sufficient for throw Exception("foo");
  • throwing away virtual (when would you normally want to hand pointers to exceptions around or upcast them?)

clarification:
At least the destructor of the base exception should be virtual to avoid ambiguities and resulting unwanted hits on catch(...) {} when you wanted to catch the exception base class (background).
Thanks Martin.

Georg Fritzsche
I would follow the std exception example and keep virtual on the destructor.
Martin York
+2  A: 

There isn't a lot that an exception needs to convey: what went wrong and why it was wrong. So that part of your design is good.

However, you're using std::string and std::stringstream. This is generally considered bad because the constructor for std::string can throw an exception when there is no memory (and std::stringstream calls std::string's constructor).

So Conan attempts to cast a spell, this causes an exception because Conan isn't a wizard, you go to create the exception but find out there's no available memory for your std::string to use, so creating the exception throws an exception. At this point you have two problems that need to be dealt with (Conan's attempted spell and no available memory) and the program has no way of knowing which should be dealt with first. C++ solves this problem with a simple solution: the program is terminated immediately. Of course you don't get a whole lot of information out of the program, and nothing is cleaned up (files aren't flushed to disk, for instance).

The std::exception class uses char*s for this reason. And if you're going to go that route, then it makes sense to go the full Monty and inherit (publicly) from std::exception.

As for converting an int to char* without std::stringstream, you can do:

#include <limits>
#include <cstdio>

char as_string[std::numeric_limits<int>::digits10 + 1];
if (std::sprintf(as_string, "%d", x) >= 0) {
    // as_string has the converted char* string here ...
}

I'm not sure what you get out of this. The part of the program catching the exception will be responsible for deciding what to do with the line number. If it chooses to display the line number, it can be expected to do the conversion. If it wishes to do something else, you will have done unnecessary work. If you were dealing with something other than line numbers (errno codes, for instance) there might even be a chance that the exception handler would need to convert the string back to an int (to pass to perror(), for instance).

There are two other issues:

  • There is no need for setMessage().

throw Exception("what went wrong")

is far more common than

Exception ex;
ex.setMessage("what went wrong");
throw ex;

And if you were to go with this second version you wouldn't be throwing ex per se. Instead you'd be throwing a copy of ex (see next point), so you'd need a copy constructor.

  • There's no need for the virtual destructor.

As noted, you won't be manipulating exceptions via pointers to the base class anyway. But you also don't delete exceptions when you're done with them (which is when the virtual destructor would be called). C++ requires special mechanisms for exceptions. throw allocates the object somewhere special (that is, some place it won't be destroyed while the stack is unwound) so that the object will be around inside your catch block. This is the reason throw ex would create a copy of ex and throw the copy -- ex will be gone by the time you get to a catch block. When you're done handling the exception the object gets destroyed automatically.

Max Lybbert
It has been indicated that <code>setMessage()</code> and <code><iostream></code> are bad ideas. How would one go about converting an integer to a string in a safe manner, such as when using <code>__LINE__<code> to pass information though an exception? I think I will take the advice above and give boost::exception a thurough look over as well.My main interest in std::string is how easy it makes it to add information as the object is passed up the stack. message = "added info\n" + message. ^_^
Actually there is a need for the virtual destructor. See http://www.boost.org/doc/libs/1_40_0/libs/exception/doc/using_virtual_inheritance_in_exception_types.html
Georg Fritzsche
Virtual inheritance is not the same thing as a virtual destructor, or virtual methods, or dealing with objects through pointers to their base classes ( http://en.wikipedia.org/wiki/Virtual_inheritance ). The exception mechanism will handle clean up for you correctly.
Max Lybbert