Chapter 5. Debugging and Error Handling

Table of Contents

5.1. Printing Debug Information
5.2. Handling Errors
5.3. Registering Error Handlers
5.4. The Class ErrorOutput and Configuring Message Outputs
5.5. Tracing, Exception Handling and Checked Object Construction/Destruction

Chapter Objectives

The ML offers some special support for debugging, error handling, logging and exception handling:

5.1. Printing Debug Information

Debug printing is controllable in and by the ML and there is some material for selective debug printing. The required files are automatically included when the standard ML include file mlModuleIncludes.h . is used.

Controlling and Managing Debug Messages

The ML controls and manages debug (and other) messages by using the instance MLErrorOutput of the class ErrorOutput (see Section 5.4, “The Class ErrorOutput and Configuring Message Outputs”). It controls the debug outputs and the error handling system. However, this instance or class should not be used directly. It is recommended to use the CoreControl module which makes the important settings available (if it is possible by an e.g., application like MeVisLab. There, the debug printing can be enabled/disabled for the entire ML, and debugging can be enabled/disabled for certain classes by using environment variables or debug symbols.

Printing Debug Messages in the Source Code

In the source code e.g., of your project, usually one of the following macros generates the debug prints:

  1. mlDebug(STREAMOUTS)(see mlDebug below - number 1)

  2. mlDebugPrint(STREAMOUTS)(see mlDebugPrint below - number 3)

  3. mlDebugClass(CLASS_NAME, STREAMOUTS)(see mlDebugClass below - number 4 )

  4. mlDebugConst(ENV_VAR, STREAMOUTS)(see mlDebugConst below - number: 2)

  5. mlDebugConditional(COND_SYM, STREAMOUTS)(see mlDebugConditional below - number: 5)

[Important]Important

Each debug output is normally related to a debug symbol which must be enabled in the ML before the debug information can be printed.

Such a debug symbol can be defined as

  1. an environment variable before the ML or the application is started,

  2. as a debug symbol in the CoreControl module when used in an application such as MeVisLab,

  3. or directly via programming in the global MLErrorOutput (see also Section 5.4, “The Class ErrorOutput and Configuring Message Outputs”) instance of the ML

The third option should not be used in normal code, but only in modules dedicated to debug control or diagnostics.

[Important]Important

To improve performance and reduce the amount of code, all debug macros are not compiled in release mode.

If debugging is enabled and the related debug symbol (or environment variable) for the macro is defined, any of the debug macros described below will send

  • the file name,

  • the time stamp,

  • the line number,

  • the debug symbol,

  • and the passed parameter STREAMOUTS

to the global instance MLErrorOutput of the ML. This instance will send the above information to all registered instances (modules such as Console, MLLogFile and MeVisLab application consoles.

The ML provides the following macros for printing debug information:

  1. mlDebug(STREAMOUTS)

    This macro prints the information given by STREAMOUTS. It requires the runtime type system to be implemented in the class. Thus, the macro accesses the type id and creates the debug symbol by using 'ML_' + the class name. This macro is normally used in implementations of the ML modules.

    If you use

    Example 5.1.  mlDebug

      mlDebug("This is the this pointer of this:" << this << ".");

    in a method or function of your AddExample module, the information is printed (provided that the environment variable ML_AddExample is not 0 or another debug symbol ML_AddExample is defined).

  2. mlDebugConst(ENV_VAR, STREAMOUTS)

    This macro is used for printing any type of debug information the developer considers to be interesting. The macro scans for the corresponding environment variable ENV_VAR or for a debug symbol of the same name registered in the MLErrorOutput instance.

    This registering of a debug symbol can also be done in the CoreControl module by defining the debug symbol in the "Debug" panel which is the normal way when e.g., using the ML in the application MeVisLab.

    Example 5.2.  mlDebugConst

      mlDebugConst("ML_HOST", "Test" << 1 << "Help!");

    prints "Test1Help!" if the environment variable ML_HOST is defined as !=0 or if a debug symbol of the same name is defined.

  3. mlDebugPrint(STREAMOUTS)

    This macro is especially designed for ML classes which are not registered in the runtime type system of the ML. It does the same as mlDebugConst(ML_DEBUG_ENV_NAME, STREAMOUTS) where ML_DEBUG_ENV_NAME must be defined by the programmer before mlDebugPrint is called.

    ML_DEBUG_ENV_NAME is usually defined once before e.g., a class is implemented. Then mlDebugPrint(STREAMOUTS) can be used as long as ML_DEBUG_ENV_NAME is undefined. Hence, the programmer does not have to care much about the environment variable for debug outputs and can change it easily without touching any debug print statement.

    Example 5.3.  mlDebugPrint

    // ... previous code
    
    // Define before class "AddHelper":
    #define ML_DEBUG_ENV_NAME "ML_AddHelper"
    
    class AddHelper {
    public:
      void testFunction() {
        mlDebugPrint("This is printed if debug symbol ML_AddHelper is defined.");
      }
    };
    
    // At end of implementation of AddHelper
    #undef ML_DEBUG_ENV_NAME
    
    // next class...

    To avoid side effects, do not forget to undefine the environment variable at the end of the file.

  4. mlDebugClass(CLASS_NAME, STREAMOUTS)

    This macro is used to print debug information for a certain class given by CLASS_NAME. It requires the runtime type system to be implemented in the class CLASS_NAME . Thus the macro accesses the type id and creates the debug symbol from the class name. Hence, symbol-controlled debug outputs for different classes can be mixed.

    Example 5.4.  mlDebugClass

    //...
    
    mlDebugClass(AddExample, "Debug information for the AddExample class.");
    
    mlDebugClass(AnotherExample, "Debug information for the AnotherExample class.");
    
    ///...

  5. mlDebugConditional(COND_SYM, STREAMOUTS)

    This macro is used to specify subsets of debug outputs for a debug symbol given by the runtime type of the class. Debug information is printed if

    • the class name (given by the runtime type) is specified as symbol, or

    • if the class name + "-" + COND_SYM is specified.

    If, for instance, the following macro is used in the class MyModule:

    Example 5.5.  mlDebugConditional

    mlDebugConditional("CASES", "Message1"); 

    the debug information "Message1" is printed if the debug symbol "ML_MYMODULE" is defined or if the debug symbol "ML_MYMODULE-CASES" is specified. If just "ML_MYMODULE-CASES" is specified, only "Message1" is printed.

    This macro requires the runtime type system to be implemented in the class that uses the macro. It accesses the type id and creates the debug environment name.

    • COND_SYM specifies the additional symbol added to the class symbol (separated by "-").

    • STREAMOUTS is the stream output sent to the error/debug output if the symbol given by the class name + "-" + COND_SYM is activated.

[Important]Important

DO NOT implement required functionality in the macro call, because it will not be compiled in release mode.

The code

  int a=0;
  mlDebug("Buggy example, do not use: " << (a=10) << "\n");
  int b= a*10; 
will result in b == 0 in release mode and in b == 100 in debug mode.

[Note]Note

To make debug outputs more readable, long file names are truncated to 30 characters.