Skip to content

UseOfExceptions

djewsbury edited this page Jan 27, 2015 · 1 revision

#Use of exceptions in XLE

XLE uses exceptions for certain operations. Typically games technology avoid exceptions because it adds some instruction cache overhead, but there are some advantages.

##Rationale

Why use exception at all? Well, there are some useful aspects we want to take advantage of.

Tools XLE is used by tools, as well as the game itself. We want those tools to be error tolerant, and we want them to be able to deal with common IO errors and similar problems. The performance trade off is much less important for tools, and the advantages are much greater. We also want to try to handle very serious errors (like out-of-memory) gracefully in a tool.

Errors inside of constructors Exceptions provide the only mechanism in C++ to report an error from a constructor. There's no return code from a constructor, so the other only way to report an error is by creating an IsGood() method. But this is an unreliable pattern, and pushes a lot of the work onto the client code. It's also awkward from an object oriented point of view; because a serious error during a constructor means that object can only be partially constructed (as best). Ideally, I'd like to avoid the idea of a partially constructed object.

Stack unwinding makes sense on resource errors during rendering It's common for resources to be unavailable during rendering. They may be currently streaming in. Or (while developing) we may have a compile error in a shader. In these cases, we typically want to respond with something like stack-unwinding. We normally want to pop up a few levels in the call stack, and then continue on with the next operation or object. We certainly don't want to abort rendering for the entire frame. Stack unwinding just feels natural here.

Asset processing errors Errors during asset processing (eg, importing from Collada) are common. Often we want to attach error information to an error, so it can be reported to the artists, and the asset can be fixed. Exceptions are a handy tool here.

Can be disabled for performance comparison When exception handling is disabled in the compiler settings, XLE will still compile. This provides a mechanism to profile the costs in real-world situation. So, if the overhead becomes too great, it can be disabled and alternative error handling can be used.

##Common exceptions in XLE

There are a limited number of different exception types used in XLE. Ideally, we only need a new exception type when it's meaningful for the client to distinguish this exception from others. In many cases, the client will end up handling all exceptions in the same way, so it's more practical to use only a very limited set of exception types.

###Utility::Exceptions::IOException

This exception is used for unpredictable IO errors. Typically this means either file access or network connections. Common cases might be a missing file, or network disconnection.

Of particular interest, Utility::BasicFile will throw this exception from the constructor.

Tools should always handle this exception gracefully. A game might sometimes handle this, or sometimes just crash.

###Assets::Exceptions::PendingResource

This exception is used when a resource is a "pending" state. This means it is currently being loaded, and we expect it to be loaded successfully. If we start a background load a texture, and then immediately attempt to use it in a rendering operation, we would get a PendingResource exception. Both tools and games are expected to always handle these exceptions gracefully.

###Assets::Exceptions::InvalidResource

This exception occurs if a resource if invalid or missing. Most commonly, this will occur if there is a compile error in a shader. Tools should always handle this exception gracefully. A game might sometimes handle this, or sometimes just crash. During development it's convenient if the game always handles this conveniently.

###RenderCore::Exceptions::GenericFailure

This is a generic error during related to rendering. Typically this is related to an underlying graphics API problem. It could be caused by a compatibility problem or a hardware failure. This should be uncommon, so is less critical to handle gracefully.

###Core::Exceptions::BasicLabel

This is a generic exception with a string label. All the exceptions in XLE are derived from this one.

##Wrapped for exception syntax

The C++ exception syntax has been wrapped with some macros. Generally, XLE doesn't use the exception keywords throw, try and catch directly.

Consider the following:

void Function()
{
	TRY {
		BasicFile file("somefile", "wb");
	} CATCH (const Utility::Exceptions::IOException& e) {
		LogWarning << "Suppressed exception during file loading. Exception message: " << e.what();
	} CATCH (...) {
		LogWarning << "Got unexpected exception in Function()";
		RETHROW
	} CATCH_END

	ThrowException(
		Exceptions::BasicLabel("This is what throwing looks like"));
}

If compiler support for exceptions is turned off, XLE will still compile. If any exceptions occur, they will result in a call to ::exit().

I've intentionally used the same form as the similar "boost" macros.

##Smart use of exceptions

Using exceptions brings with it many "best practices" rules! There are a lot of difficulties related to C++ exceptions that it's important to be aware of. So, please refer to the best practices section.