Exception handling

From FIFE development wiki
Jump to: navigation, search

Introduction

This page is about handling exceptions which occur in the engine and shall or can be handled at script side. Whenever an exception occurs in a call from script to the engine, the script must be able to catch it to react properly. Otherwise we would just have program crashes.

How exceptions must be marked in the engine

SWIG wants all possible types of exceptions, which can be thrown, in the function declaration. For example, let's take engine/core/gui/guimanager.h:

gcn::Font* createFont(const std::string& path, unsigned int size, const std::string& glyphs);

createFont either creates a new TrueTypeFont or SubImageFont (depending on the file extension of the font). But what happens when we load a font which doesn't exists? TrueTypeFont and SubImageFont constructors both want the path to the font as the first parameter. And if they can't find or load it, they throw an exception, which is never caught. The reason is simply explained: the script is the caller (Python), and the exception occurs in the engine (C++).

The solution for that is that SWIG is able to automatically generate wrapper code for scripts to be able to catch these exceptions. To do that, just declare what types of exceptions might occur; directly in the function declarations of the wrapped functions. Let's do this with the guimanager example.

Let's assume that we want to open a TTF font. When we take a look at the TrueTypeFont constructor in engine/core/gui/fonts/truetypefont.cpp, we can find this piece of code which is responsible for the exception throw:

mFont = TTF_OpenFont(filename.c_str(), size);
 
if (mFont == NULL) {
  throw FIFE::CannotOpenFile(filename + " (" + TTF_GetError() + ")");
}

The exception is of the type FIFE::CannotOpenFile, so we have to mark it in the function declaration (at guimanager.h, guimanager.cpp AND guimanager.i, the interface file for SWIG):

// guimanager.h:
gcn::Font* createFont(const std::string& path, unsigned int size, const std::string& glyphs) throw( FIFE::CannotOpenFile );
// guimanager.cpp:
gcn::Font* GUIManager::createFont(const std::string& path, unsigned int size, const std::string& glyphs) throw( FIFE::CannotOpenFile ) { [...]
// guimanager.i:
gcn::Font* createFont(const std::string& path, unsigned int size, const std::string& glyphs) throw( FIFE::CannotOpenFile );

As you can probably see, the important change is this: throw( FIFE::CannotOpenFile ). SWIG will wrap this exception to scripting side now, so we can catch it. Before this will compile properly, you have to include the following line to guimanager.h:

#include "util/exception.h"

And also this line between {% and }% in guimanager.i:

#include "util/exception.h"

This needs to be done so that FIFE::CannotOpenFile is available both for the C++ GuiManager and also from the script.

Example (Python):

try:
    font = self.guimanager.createFont('techdemo/fonts/ERRsamanata.ttf', 12, glyphs)
except fife.CannotOpenFile as e:
    print 'Exception detected: '+ e.getMessage()

(this example is a modification of techdemo.py) In this example we try to load a TTF font which doesn't exist. But this time we catch the exception, print out some little message and go on with the program instead of crashing it. Of course continue running wouldn't be clever in this case, because a missing font could lead to a confusing display of text. ;-)

What kind of exceptions to implement

The question for this topic is: shall we use more general exceptions like:

  • Exception( "File couldn't be found" )
  • Exception( "Index out of range" )

Or specific exceptions like:

  • CannotOpenFile( "abc.txt" )
  • IndexOutOfRange

The big advantage of the second method is that we can easily differ between different exceptions. And every exception can have its own properties. So it is suggested to always use specific exceptions when throwing them from wrapped functions. A disadvantage could be that not every target language support these kinds of exceptions, but only "general ones", like throwing some error string message. This has to be discussed.