The ML is a library of base classes that many modules and applications use to implement image processing algorithms. In such a complex system, mechanisms to catch, log and handle runtime errors and crashes as well as mechanisms to trace program execution are required. Especially for critical or potentially unsafe functionality, support for additional checks and controls must be provided. The following paragraph describes some macros that allow for the implementation of highly safe source code with crash and error logging especially for critical functionality:
ML_TRACE_IN("<FunctionDescription>")
This macro should be implemented as the first line in all
functions and methods that are not very time-critical. When this
code is compiled in release mode, it implements functionality that
pushes a reference to the string
<FunctionDescription>
on a stack and into a
list, and when the function is finished the pushed information is
popped from the stack. This push/pop/list functionality is
implemented in the classes Trace
and
TraceBuffer
in project
MLUtilities
. The ML error handler (see Section 5.4, “The Class ErrorOutput
and Configuring Message Outputs”) can be configured to append the list of
recently called functions (trace list dumps) and the current call
stack (trace stack dumps) to the registered error output callbacks
for additional bug analysis.
Note that this macro only uses about 8 simple CPU instructions in release code and thus can be added to most functions without significant performance loss.
ML_TRACE_IN_TIME_CRITICAL("<FunctionDescription>")
This macro is identical with the macro
ML_TRACE_IN("")
, however, it is only compiled
if explicitly enabled for diagnostic purposes. In normal debug or
release mode, this tracing macro is not compiled. It is especially
useful for tracing time-critical functionality which is assumed to
operate safely in normal mode.
This macro opens a source code region to be checked for
undesired exceptions. If such an exception occurs, the closing
ML_CATCH*()
macro implements crash handling and
error logging with the ML error manager and memory cleanup.
This macro can be used to close an ML_TRY {
code fragment. The macro sends a fatal error to the ML
error manager with ML_PRINT_FATAL_ERROR()
and
continues with the execution of the memory manager which is returned
by that macro. It is typically used when no resources that were
opened or allocated in the enclosed code need to be cleaned
up.
This is another macro that can be used to close an
ML_TRY {
code fragment. It is identical with
} ML_CATCH()
, but it returns 0.
ML_CATCH_BLOCK(<exception type>){ <handling code> }
This is another macro that can be used to close an
ML_TRY {
code fragment and that allows for
cleaning up resources opened or allocated in the enclosed code.
Multiple implementations of ML_CATCH_BLOCK()
can be implemented one after another to handle different types of
exceptions.
Note that
ML_CATCH_BLOCK()
does not post errors to the ML error manager; this must be done
explicitly in the <handling code>
section
if necessary.
Important | |
---|---|
The macros listed above implement exception catching and error posting only if the code is compiled in release mode. In debug mode, the macros result in dummy code which does not perform exception handling or catching, i.e., errors and exceptions will cause normal program crashes. This strategy has been chosen to simplify debugging in debug mode, because detecting precise error positions becomes more difficult in many debugging tools when exception handling is enabled. |
The following code fragments demonstrate tracing and exception handling:
Example 5.6. Example of a Typical Use of the ML_TRACE_IN() Without Exception Catching
void MyClass::testFunction1() { ML_TRACE_IN("void MyClass::testFunction1()"); <function body> }
Example 5.7. Example of a Typical Use of the ML_TRACE_IN() with Exception Catching
void MyClass::testFunction2() { ML_TRACE_IN("void MyClass::testFunction2()"); ML_TRY { <The function body is implemented here. If an exception is thrown in it then ML_CATCH posts a fatal error to the ML error manager, and - if the error manager does not terminate the process - continues execution normally> } ML_CATCH; // This catches the error, posts it and continues // if the ML error manager continues execution }
Example 5.8. Example of a Typical Use of the ML_CATCH_RETURN_NULL()
int MyClass::testFunction3() { ML_TRACE_IN("int MyClass::testFunction3()"); ML_TRY { <The function code is implemented here. If an exception is thrown in it then ML_CATCH_RETURN_NULL posts a fatal error to the ML error manager, and - if the error manager does not terminate the process - continues execution with a returning 0> return result; // This is the return statement in case of successful execution. } ML_CATCH_RETURN_NULL; // This catches the error, posts it and returns // 0 if the ML error manager continues execution }
Note | |
---|---|
The semicolons behind the |
Constructing and Deleting Objects:
This implements a new
of the passed
expression
. In release mode, it handles the
exception with an ML_PRINT_FATAL_ERROR
post to the ML
error manager. The pointer must have been set to
NULL
before.
ML_CHECK_NEW_TH(ptr, expression)
This executes a new
of the passed
expression
. In release mode, it handles the
exception with a ML_PRINT_FATAL_ERROR
post to the ML
error manager and it throws either an
ML_NO_MEMORY
exception or an
ML_CONSTRUCTOR_EXCEPTION
dependent on whether
the new
statement returned
NULL
or the constructor threw an
exception. The pointer must have been set to
NULL
before.
This macro is used to delete an object allocated with
ML_CHECK_NEW(ptr, expression)
or with
ML_CHECK_NEW_TH(ptr, expression)
. It must
only be used with a single created object, not with an array (see
below).
This macro is used to delete an object allocated with
ML_CHECK_NEW(ptr, expression[<objectNum>])
or with
ML_CHECK_NEW_TH()
. It must only be used for
allocated object arrays.
Important | |
---|---|
Always try to use the above macros for constructing and deleting objects inside of ML code. In future, this will provide a more powerful and failsafe memory management, and it will also correctly handle and log errors that occur in applications. |
Note | |
---|---|
See Section 2.2.2, “
|
This macro posts an
ML_PRINT_FATAL_ERROR()
to the ML error
manager if the passed <expression>
returns false
. This is the typical way of checking
entry conditions in functions, for example.
If the ML error manager continues execution, normal program
execution continues after the
ML_PRINT_FATAL_ERROR()
macro.
ML_CHECK_ONLY_IN_DEBUG(<expression>)
This macro is identical with the
ML_CHECK(<expression>)
macro, however,
it is only compiled in debug mode. In release mode, it is not
implemented at all. So this macro is comparable to the normal
assert()
statement. With the
assert()
statement, however, errors are
redirected to abort()
and not to the ML error
manager.
This macro posts an
ML_PRINT_FATAL_ERROR()
to the ML error
manager if the passed <expression>
returns false
. This is the typical way of checking
program or parameter states in functions for validity.
If the ML error manager continues execution, this macro
throws an ML_BAD_POINTER_OR_0
exception after
the ML_PRINT_FATAL_ERROR()
macro. Thus this
macro is especially useful in code segments which are enclosed in
ML_TRY { <function body> } ML_CATCH*()
segments.
Note | |
---|---|
Also see Section 5.2, “Handling Errors” for explicit usage of error and warning posts. |
Example 5.9. Detailed Example for a Checked Object Allocation with ML_CHECK_NEW_THROW() and Release of Resources on Crashes
double MyClass::testFunction4() { int *newArray = NULL; double retVal = 0; ML_TRY { // Allocate an integer array with new. ML_CHECK_NEW_THROW(newArray, int[200]); int result = 0; /* We assume that the function code makes use of the allocated data here and that it must calculate a non zero return value; if result remains 0 then we have a bug somwhere... */ // This value is expected to be non zero, otherwise // we have a fatal error, check it chere. ML_CHECK_THROW(result); // Calculate the return value. retVal = 10. / result; // Release the allocated memory and reset pointer. ML_DELETE_ARRAY(newArray); } ML_CATCH_BLOCK(...){ // Clean up allocated resources after any crash in // ML_TRY{ } block if pointer is non NULL. ML_DELETE_ARRAY(newArray); // Post and log the error. ML_CHECK(0); // Optionally and dependent on the way how the application // handles errors the exception can be propagated to the caller // such that it terminates execution until the main function is // reached and the program state is cleaned up correctly. // Another option would be to continue here. throw(); } return retVal; }
© 2024 MeVis Medical Solutions AG