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;
}
          © 2025 MeVis Medical Solutions AG