Error Handling

The old way and its shortcomings

In the old QPix every plug-in method would return an error code. This way of error reporting surely permits checking for the success or failure of every plug-in call, but eventually it leaves the method editor littered with error variable declarations and checks.

Also, for simple calls that may indicate a failure by the value of their result, such a plug-in API is unnatural. For example, it’s way more natural to read $image:=GetAreaImage(areaRef) instead of $error:=GetAreaImage(areaRef;$image). In the first case the empty result clearly indicates that the area contains no image. There are many plug-in calls that don’t really need error checking because they just can’t fail in normal use.

Further, there are cases where a plug-in method failure should be reported to the user (for example when a requested resource does not exist), cases where a failure should be handled by the developer, and cases where errors are actually assertion failures (quite common during development).

Finally, it was reasonable for QPix, due to its dependence on QuickTime and Altura, to stick with the classic MacOS error codes that its underlying technologies were using. With all of them dead and gone, it’s clearly time for something better.

The new way and how it works

Modern software consists of many different modules/libraries, each using error codes from its own domain. For example, in Q2Pix errors may come from the plug-in itself, from the OS (POSIX, Cocoa, Windows OS, WIC, Direct2D), from the XMP Toolkit SDK (the library that provides XMP editing), from PDFium (the library used for PDF manipulation and rendering on Windows), from C++ (the language used to build the plug-in), or from some other domain.

Q2Pix plug-in methods do not return error codes. Instead, the plug-in keeps an error info object per process, similar to how 4D uses the Error global/process variable:

Property name Type Description
Domain String The domain of the error. For errors triggered by Q2Pix the domain will be "Q2Pix". Other possible values include (but are not limited to) "POSIX", "Cocoa" (on macOS), "WIC", "D2D", "Win32" (on Windows), and "Pdfium".
Code Longint The error code, interpreted in the specific domain.
Description String The description of the error (optional).
Reason String The reason why the error happened (optional).
Method String The method where the error occurred (optional).

The error information is cleared on entry of every plug-in method call, and set when the method returns with some failure. The code becomes more natural and easier to read, without sacrificing any functionality.

Retrieving the error information is done via dedicated methods:

Errors triggered by Q2Pix are in the "Q2Pix" domain. The error codes are listed in the constants section.

Consider the following typical (old) QPix code that opens an image file and iterates over its frames:

C_TEXT($imagePath)
$imagePath:="path:to:my:image.jpg"

C_LONGINT($importer;$error)

$error:=QPx_NewImporterForFile ($importer;$imagePath)
If ($error=qpx_noErr)

    C_LONGINT($frameCount)
    $error:=QPx_CountImporterFrames ($importer;$frameCount)
    If ($error=qpx_noErr)

        For ($idx;1;$frameCount)

          // ...
          // Collect some image frame data
          // ...

        End for

    End if

    $error:=QPx_FreeImporter ($importer)
Else
    ALERT("Could not open file due to error #"+String($error)+".")
End if

The same code using Q2Pix would be:

C_TEXT($imagePath)
$imagePath:="/path/to/my/image.jpg"

C_TEXT($imgDocRef)

$imgDocRef:=ImgDoc_CreateFromFile($imagePath)
If ($imgDocRef#"")

    C_LONGINT($frameCount;$idx)
    $frameCount:=ImgDoc_GetFrameCount ($imgDocRef)

    For ($idx;1;$frameCount)

      // ...
      // Collect some image frame data
      // ...

    End for

    ImgObj_Release ($imgDocRef)
Else
    ALERT("Could not open file due to an error:\n\n"+ImgErr_GetDescription)
End if

Some observations:

  • No compiler declaration for the $error variable (C_LONGINT($error)).
  • Cleaner code.
  • The failure message displayed to the user is more meaningful than just an error code.
  • Though the call to ImgDoc_GetFrameCount will not fail with a valid image document reference, a defensive programmer would add an assertion right after that line to catch the impossible error during development: ASSERT(ImgErr_GetCode=0;"The sky is falling!")