Sign in to follow this  
load_bitmap_file

Exceptions - On how specific/nonspecific to make them

Recommended Posts

Let's say we have a Sprite class that loads a sprite file in the constructor. In constructing the Sprite, there could a number of things that go wrong, such as the file not existing, an invalid transparency value, invalid number of frames value, or invalid animation time between frames value. Should any of these problems occur, an exception should be thrown so that the code calling it can identify the problem and act appropiately. I pondered two possibilities for "naming" the exceptions: Note: In both cases the exceptions would be derived from std::exception 1. Have NonexistentFile, InvalidTransparency, InvalidNumberOfFrames, etc structs. In other words, have a separate exception struct for each possible type of error. The external code then, has to have a catch() block for every type of error to handle it. 2. Have one SpriteException class that will accept a std::string message and store it internally. A call to the overrided member function what() will then return that message. The different types of errors would all throw a SpriteException, but pass in their own unique string of what the problem is so the type of problem can be identifier. For example: throw SpriteException("File does not exist") or throw Sprite Exception("Invalid Transparency"); And then the external code that calls it has a single catch(SpriteException) that then determines via a bunch of if/else if statements and what() the specific problem. Any thoughts on which approach to take? Oh, and another related question: Should the specific exception structs for classes like these be place within the class itself? I suppose it would be either that or within the same namespace as the class.

Share this post


Link to post
Share on other sites
Wrap up your file-system access and have it throw the FileNotFound exception and others (determine how specific you want to get, although, for a system level thing like this, specific == good). For the sprite, throw a generic SpriteException with a string describing the problem. At least, that's how I would do it.

Share this post


Link to post
Share on other sites
I'll describe my own situation. My generic serialization library can have any number of bad things happen that generate an exception (broken streams, missing files, bad checksums, missing resources, etc, etc), but there is only one thing that is going to be done with the exception - and that is - indicate that loading failed and report the error message (with details of what was being done at the time).

Because there isn't much to do besides report an error message - there is no reason to mess around making various exception classes (Yag Ni, Folks). The actual use of the thing looks like this:

// the throw:
throw ASerializeError(serializer) << "Error doing XYZ in " << something << ".";

// the catch
catch(ASerializeError& e)
{
e.Log();
return false; // indicate to the caller that loading failed
}



So yes - unless you actually have something you can do with the various kinds of error classes - just make one (otherwise you're probably wasting effort).

If it turns out you need more classes later for more specific catching behaviour - it is fairly trivial to add more classes that inherit from your main exception class.

Share this post


Link to post
Share on other sites
Just want to mention two things [smile].


  • C++ iostreams can throw exceptions, just turn them on with std::basic_ios::exceptions set with std::ios_base::iostate

  • its not necessary to have user-defined exception types be sub-types of std::exception, infact if i remember correctly Bjarne's recommends not to derive from it (not that there is anything particularly wrong by doing it) and have your own base exception type if you wish

Share this post


Link to post
Share on other sites
When I use custom exceptions, I use a base exception class like CException. It usually takes 4 parameters. The Exception name, the File it was thrown from, the line it was thrown from (this ends up being the throw command, but it at least points you in the right direction), and then a custom message field. I just derive a all of my other exceptions from that CException class, so specializations can be made, but ultimately I just need to catch CException to get them. What is also nice is with a little bit of work, you can even build an Exception that will catch Windows Structured exceptions (good for debugging, but I definately wouldn't leave these on in a real release).

Share this post


Link to post
Share on other sites
Thanks for the replies. Route #2 it is.

Quote:
Original post by snk_kid

  • its not necessary to have user-defined exception types be sub-types of std::exception, infact if i remember correctly Bjarne's recommends not to derive from it (not that there is anything particularly wrong by doing it) and have your own base exception type if you wish



He recommends against it? I remember reading a few articles that recommended for it. Something about catch(...) being bad for some reason and catch(std::exception& e) being a better solution which would work if all your exceptions were derived from std::exception.

Share this post


Link to post
Share on other sites
catch(...) should be the last thing, if you have a list of catches, its the catch all.

I usually have the following in my main loop


try
{
// some code
}
catch(const CException& ex) // See my post above
{
// Print information from the exception
}
catch(...)
{
// Print that an unknown exception occured.
}




[edit]

Also note the use of catching an exception by reference. This is a little more efficient. In C++ everything is an exception, you can throw ints, chars, any data type. I try to control what gets thrown in my code (a few try/catch blocks that catch exceptions and then throw one of my custom ones).

Share this post


Link to post
Share on other sites
Quote:
Original post by Rattrap

Also note the use of catching an exception by reference. This is a little more efficient.


If I'm not mistaken, you actually need to use reference, since the exception might be a subtype of CException (or whatever you use).

I personally derive most of my exceptions from std::runtime_error, since it comes with a constructor that takes a string for what() (so i don't have to write it my self. Call me lazy. ;) Or I just use runtime_error or logic_error as such. Making a utility exception class that takes the linenumber and file name sounds like a good idea. It might be worth it to write a macro for throws:


#define THROW(E,M) (throw E(M,__FILE__,__LINE__))

Or something like that.

Share this post


Link to post
Share on other sites
You don't HAVE to catch by reference (as it were), you cold just do

catch(CException ex)

The difference being it is going to call the copy constructor to pass ex to the code block. Passing by reference just prevents a copy from occuring.

[edit]
Quote:
Original post by FlowingOoze
Quote:
Original post by Rattrap

Also note the use of catching an exception by reference. This is a little more efficient.


If I'm not mistaken, you actually need to use reference, since the exception might be a subtype of CException (or whatever you use).

I personally derive most of my exceptions from std::runtime_error, since it comes with a constructor that takes a string for what() (so i don't have to write it my self. Call me lazy. ;) Or I just use runtime_error or logic_error as such. Making a utility exception class that takes the linenumber and file name sounds like a good idea. It might be worth it to write a macro for throws:


#define THROW(E,M) (throw E(M,__FILE__,__LINE__))

Or something like that.


And how your doing it is almost identical to the way I do it, I just made my own base class and derived clases.

Share this post


Link to post
Share on other sites
Quote:
Original post by Rattrap
You don't HAVE to catch by reference (as it were), you cold just do

catch(CException ex)

The difference being it is going to call the copy constructor to pass ex to the code block. Passing by reference just prevents a copy from occuring.


If you're lucky enough to have a copy constructor that will work properly for subtypes (that may override what()). Otherwise you're in a mess and better off using reference.

Share this post


Link to post
Share on other sites
Quote:
Original post by FlowingOoze

If I'm not mistaken, you actually need to use reference, since the exception might be a subtype of CException (or whatever you use).



What I was trying saying that you should pass by reference, but in my last post I was merely pointing out that you don't have to.

Share this post


Link to post
Share on other sites

#include <exception>
#include <iostream>
#include <string>
#include <typeinfo>

namespace exceptional
{

class exception : public std::exception
{
private:

friend class hack;

mutable std::string str;

void prepend(const std::string & pre) const { str=pre+str; };

public:
exception(const std::string & what) : str(what) {};

virtual const char * what() const throw() { return str.c_str(); };

virtual ~exception() throw() {};
};

class hack
{
private:
std::string msg;
public:
hack(const char * m) : msg(m) {};

template<class T>
const T & operator || (const T & e)
{
e.prepend(msg);
return e;
}
};
};

#define STRINGIFY1(line) #line
#define STRINGIFY2(line) STRINGIFY1(line)

#define THROW throw exceptional::hack( __FILE__ ":" STRINGIFY2(__LINE__) ": " ) ||

struct MyException : public exceptional::exception
{
MyException(const std::string & msg) : exceptional::exception(msg) {};
};

using namespace std;

void foo()
{
THROW MyException("Boo");
}

main()
{
try {
foo();
} catch(const exception & e)
{
cout << typeid(e).name() << " " << e.what() << endl;
}
}




What do you think of this? Just cooked it up.
Prints on GCC:
11MyException exceptional.cpp:57: Boo

EDIT:
I just realized that it could be made to work on other exception types that have a string constructor, if hack::operator||() just threw T(msg+T::what()).

Share this post


Link to post
Share on other sites

#include <stdexcept>
#include <iostream>
#include <string>
#include <typeinfo>

namespace exceptional
{

class hack
{
private:
std::string msg;
public:
hack(const char * m) : msg(m) {};

template<class T>
void operator || (const T & e)
{
throw T(msg+e.what());
}
};
};

#define STRINGIFY1(line) #line
#define STRINGIFY2(line) STRINGIFY1(line)

#define THROW exceptional::hack( __FILE__ ":" STRINGIFY2(__LINE__) ": " ) ||

struct MyException : public std::logic_error
{
MyException(const std::string & msg) : std::logic_error(msg) {};
};

using namespace std;

void foo()
{
THROW MyException("Boo");
}

main()
{
try {
foo();
} catch(const exception & e)
{
cout << typeid(e).name() << " " << e.what() << endl;
}
}


Here's the improved version.

Share this post


Link to post
Share on other sites
Quote:
Original post by FlowingOoze
I personally derive most of my exceptions from std::runtime_error, since it comes with a constructor that takes a string for what() (so i don't have to write it my self. Call me lazy. ;)


Hmm, so it does. My copies of The C++ Programming Language and The C++ Standard Library A Tutorial and Reference don't list std::runtime_error's interface! Why! [flaming]

EDIT: I just realized I forgot to get an answer for this:

Do you think the exception structs for classes like be placed within the class itself or just within the same namespace as the class? As in:


class Sprite
{
public:
class SpriteException
{
//blah blah
};

//blah blah

private:
//blah blah
};


or


class SpriteException
{
//blah blah
};

class Sprite
{
public:
//blah blah

private:
//blah blah
};


? I am leaning towards not sticking it within the actual class itself to reduce clutter. I don't really know though.

[Edited by - load_bitmap_file on June 22, 2005 1:41:11 PM]

Share this post


Link to post
Share on other sites
Naming exceptions only makes sense if you're actually going to catch them.
Ie. if you'll ever write something like catch(Sprite::SpriteException), which is be pretty awkward. If you really need exception classes that are thrown only by specific classes, then it might be worth it.

[Edited by - FlowingOoze on June 23, 2005 12:05:36 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by FlowingOoze
Naming exceptions only makes sense if you're actually going to catch them.
Ie. if you'll ever write something like catch(Sprite::SpriteException), which is be pretty ackward. If you really need exception classes that are thrown only by specific classes, then it might be worth it.


I agree. It's a little akward. I use a namespace that hold all of my exceptions and a master include file that I can just tack onto what ever is going to be throwing or handling exceptions.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this