If an exception of type A is raised in a guarded section, then Exception handling system see if there is a corresponding catch for type A at the end of the guarded section. If so the code in the catcher is executed. If not, then the exception is passed to the higher level function and so on until an appropriate catcher is found : this mechanism is called stack unwinding. Notice that during stack unwinding, the destructors of the class allocated on the stack are automatically called.
A typical guarded function may look like this :
void function1(int arg1) { string wazaaa("Class allocated on the stack"); ... //Some unguarded code ... try //beginning of the guarded section { //guarded code here ... if ( something_is_wrong ) throw "this is a char * type exception"; ... //more guarded code ... } // End of guarded section catch ( char * e ) //char * exception type catcher { cout << "We just print the exception : " << e << endl; //exception was gracefully handled, execution will continue AFTER the last catch block } catch (...) //catch all types of exception not previously catched { //some clean up code here throw; // we can't handle it, so start stack unwinding // wazaaa destructor will be called and execution will continue in next appropriate catch block } ... //some more unguarded code if you want to ... }
Exception handling is an out of the flow execution. However, if the guarded section installing doesn't cost much, exception handling causes a great execution overhead as the CPU exectuion cache as to be flushed. So exceptions should ONLY be used to handle unexpected errors, likes access failure, etc...
main
function is a good place to call the INIT_TRACED_EXCEPTIONS. (NB : if you are using microsoft compiler, you have to use the /EHa instead of the /EHs flag)
A guarded section can be easily defined using the TRY_BLOCK_START
and TRY_BLOCK_END(x)
macros. All the code between this macros will be in the guarded section. On top of that the TRY_BLOCK_END takes the name of the guarded section as a parameter. Usually it's the name of the function which the guarded section blongs to. Such a guarded section catch by default all exceptions of type std::exception and TracedException. The default behavior for std::exception type is to throw TracedException constructes from it. The default behavior for TracedException type is to log the name of the guarded section and then start unwinding the stack.
Remember that the unwinding history only get the names of guarded sections the exception pass through. If some function lacks of guarded section, they will *NOT* be referenced.
You may also want only to use default catcher. For this, use the macro CATCH_TRACED_EXCEPTION(SectionName)
just after a try block.
THROW_TRACED_EXCEPTION(Reason)
: Just throw a TracedException with indicated reason.THROW_TRACED_FMT_EXCEPTION(Reason, Args)
: throw a TracedException with formated text as reason. Args are separated by '%' and Reason use standard printf format. For more details on format string see boost::format
library.THROW_TRACED_EXCEPTION_FROM(Reason,FunctionName)
: throw a TracedException with a virtual guarded section name. This mean you can throw it from a function lacking a guarded section, but still trace the origin.THROW_TRACED_FMT_EXCEPTION_FROM(Reason,FunctionName, Args)
: This one is a combination of THROW_TRACED_FMT_EXCEPTION and THROW_TRACED_EXCEPTION_FROMCATCH_CUSTOM_EXCEPTION(x)
macro jsut before an TRY_BLOCK_END
. The parameter x is the type of the exception you want to catch. Code to handle the exception follows immediatly the macro, and you may chain as many CATCH_CUSTOM_EXCEPTION as you want.In general it's better to use a class derived from TracedException as type filter in order to keep the guarded section history, but you may use any other types as well.
"->"
symbol. You may retrieve it using TracedException::GetClassStack.
class MyException : public TracedException; int f1(const char * arg1) { TRY_BLOCK_START ... if(arg1_is_malformed) THROW_TRACED_FMT_EXCEPTION("Error %d : String %s is malformed", 32 % arg1 ); ... TRY_BLOCK_START if ( OpenTheFile(arg1) ) throw MyException("File not found"); ... CATCH_CUSTOM_EXCEPTION(MyException& e) // do some clean up here throw; // pass the exception to the next handler. This is not mandatory TRY_BLOCK_END("File Opening"); ... // some code to imlement the function ... TRY_BLOCK_END("f1"); return false; } int main(in argc, char * argv[]) { INIT_TRACED_EXCEPTIONS; try { f1(argv[1]); } catch(TracedException e) { cerr << "Catched exception " << e.GetReason() << endl; cerr << "In function " << e.GetClassStack() << endl; } catch(...) { cerr << "UnHandled exception raised" << endl; } return 0; }