Visual Prolog 7 Tutorials

Exceptions Handling

Written by Yuri Ilyin
Prolog Development Center
Tutorial Version 1.0
Last updated: 15-11-2007

An exception is a deviation of a program from its "normal" execution path. The PFC exception package contains predicates to catch exceptions, which occur in a prolog program. This tutorial describes:

  1. How to handle exceptions;
  2. How to raise your own exception;
  3. How to continue another exception.

Download:

Source files of the example project discussed in this tutorial.

See also:

A detailed description of exceptions in the "Prolog Foundation Classes/Exception" section of Visual Prolog online Help.

How to Catch Exceptions

Let us consider a program that reads a text file and prints its content to the console. The PFC supply the file::readString predicate that can fulfill the task. However, some cases can prevent to implement the task; for example, a specified file name can point to a non-existing file. When predicate file::readString cannot fulfill the task, it generates an exception.

Visual Prolog supplies a built-in trap/3 predicate to catch an exception and process it. Please, see Visual Prolog online Help ("Language Reference -> Built-in Domains, Predicates and Constants-> Predicates ->trap" topic) for more details about the trap/3 predicate.

Therefore the code to catch an exception will look like:

trap(MyTxtFileContent = file::readString(myFileName, _IsUnicode),
     ErrorCode, handleFileReadError(ErrorCode)), 

The most interesting part is the implementation of the handleFileReadError predicate:

class predicates  
  handleFileReadError : ( exception::traceID TraceId ) failure.
clauses  
  handleFileReadError(TraceId):- 
    Descriptor = exception::tryGetDescriptor(TraceId, 
      fileSystem_api::cannotcreate), 
    !, % file cannot be loaded
    exception::descriptor(_ErrorCode, % ErrorCode is used if user invoked 
       % errorExit himself .
        _ClassInfo, % class information of the class, which raised the exception .
        _Exception, % actually it is fileSystem_api::cannotcreate, 
       % but the parameter should not be compared by ' = ' . 
       % See exceptionState::equals
        _Kind, % exception can be raised or continued
        ExtraInfo, 
        _TraceId, % here is the TraceId, but it is not necessary to check once more
        _GMTTime, % the time of exception creation .
        ExceptionDescription,
        _ThreadId) = Descriptor, 
    FileName = namedValue::mapLookUp(ExtraInfo, 
      fileSystem_api::fileName_parameter, string("")), 
    Reason = namedValue::mapLookUp(ExtraInfo, 
      common_exception::errorDescription_parameter, string("")), 
    stdIO::write("Cannot load file due to: ",ExceptionDescription, 
      "\nFileName: ", FileName, 
      "\nReason: ", Reason ), 
    fail. 
  handleFileReadError(ErrorCode):- 
    isDebugMode = true, 
    !, 
    exceptionDump::dumpToStdOutput(ErrorCode), 
    % dump to console for developer needs
    fail. 
  handleFileReadError(_ErrorCode):- 
    % program cannot handle the exception and it does not report about it .
    fail.

When fileSystem_api::cannotcreate exception is expected, the predicate exception::tryGetDescriptor is invoked exactly with such parameter to catch and handle such exception. In our case we use the output to console:

stdIO::write("Cannot load file due to: ",ExceptionDescription, 
  "\nFileName: ", FileName, 
  "\nReason: ", Reason ),

with an explanation of a reason of the exception.

Please notice that since Visual Prolog 7.0 it is not necessary to clear exceptions, as all necessary information is stored in TraceId. This fact actually increases the performance of your program.

The full example presents in catchException\catchException.prj6 project.

How to Raise Own Exceptions

Let us consider a program, that checks the size of a specified file. The PFC supply the predicate file::getFileProperties that can return the size of the specified file. If the size is zero, then we can raise an exception by the predicate exception::raise. It is also necessary to create a predicate, which will specify the exception. We have created myExceptionZeroFileSize for this purpose. The code of raising an exception will look like:

clauses  
  run():- 
    console::init(), 
    trap(file::getFileProperties(myFileName, 
      _Attributes, Size, _Creation, 
      _LastAccess, _LastChange), 
      ErrorCode, 
      handleFileGetPropertiesError(ErrorCode)), 
    Size = unsigned64(0, 0), % file size is zero
    !, 
    exception::raise(classInfo, 
      myExceptionZeroFileSize, 
      [namedValue(fileSystem_api::fileName_parameter, 
      string(myFileName))]). 
  run(). 

The first parameter of the predicate exception::raise is the predicate classInfo, which is generated by the IDE for any new class. It is, of course, a good idea to specify extra information, when an exception is generated. In our case the file name can be helpful, as a zero size belongs to the specified file.

The predicate myExceptionZeroFileSize is created by the template:

clauses  
  myExceptionZeroFileSize( 
    classInfo, 
    predicate_Name(), 
    "File size cannot be zero").

That is, the first and the second parameters for exception predicates are always classInfo and predicate_Name() respectively, and the third parameter contains a text explanation of the exception reason.

The full example presents in raiseException\raiseException.prj6 project.

How to Continue Another Exception

Let us consider a DLL program that reads a text file and returns its content as a return parameter. If an exception occurs, then the DLL continues it and adds extra information about DLL's version. The code to continue the exception will look like:

constants  
    dllVersion = "1.20.0.0". 
clauses  
  loadFile(FileName) = FileContent :- 
    trap(FileContent = file::readString(FileName, _IsUnicode), 
      ErrorCode, 
      exception::continue(ErrorCode, 
        classInfo, continuedFromDll, 
        [namedValue(version_Parameter,string(dllVersion))])).

The predicate continuedFromDll is declared as:

predicates  
        continuedFromDll : core::exception as "_ContinuedFromDll@12"

and it is exported from the DLL. The implementation of the continuedFromDll predicate is created by a template ( the same as myExceptionZeroFileSizeabove):

clauses  
    continuedFromDll( 
      classInfo, 
      predicate_Name(), 
      "Exception continued from continueException.DLL").

When the predicate exception::continue continues the exception, it adds extra information about dllVersion.

The full example presents in continueException\continueException.prj6 project.

Let us look how to catch such continued exception. The code is similar to the code discussed above and we will focus on the differences here.

constants  
  myFileName = "my.txt". 
clauses  
  run():- 
    console::init(), 
    initExceptionState(exception::getExceptionState()), 
    trap(MyTxtFileContent = loadFile(myFileName), 
      ErrorCode, handleFileReadError(ErrorCode)), 
    !, 
    stdIO::write("The content of ",myFileName,"is:\n",MyTxtFileContent). 
  run(). 
      
class predicates  
  handleFileReadError : ( exception::traceID TraceId )failure . 
clauses  
  handleFileReadError(TraceId):- 
    DescriptorContinued = exception::tryGetDescriptor(TraceId, 
       continuedFromDll),
    ErrorDescriptorInformation = 
        exception::tryGetErrorDescriptorInformation(TraceId),
    ContinueException = 
        ErrorDescriptorInformation:tryGetContinueException(),
    !, %file cannot be loaded
    exception::descriptor(_ErrorCodeContinued,
       _ClassInfoContinued,
       _ExceptionContinued,
       _KindContinued,
        ExtraInfoContinued,
        _,
        _GMTTimeContinued,
        ExceptionDescriptionContinued,
       _ThreadIdContinued) = DescriptorContinued,
     Version = namedValue::mapLookUp(ExtraInfoContinued,
            version_Parameter, string("")),
     stdIO::write("Exception continued : ",ExceptionDescriptionContinued,
        "\nDllVersion: "Version,"\n"),
     ExtraInfo = ContinueException:getExtraInfo(),
     ExceptionDescription = ContinueException:getExceptionDescription(),
     FileName = namedValue::mapLookUp(ExtraInfo,
        fileSystem_api::fileName_parameter, string("")),
     Reason = namedValue::mapLookUp(ExtraInfo,
        common_exception::errorDescription_parameter, string("")),
     stdIO::write("Cannot load file due to: ",ExceptionDescription,
        "\nFile Name: "FileName,
        "\nReason: "Reason ),
     fail.
  handleFileReadError(TraceId):- 
    isDebugMode = true, 
    !, 
    exceptionDump::dumpToStdOutput(TraceId), 
    fail. 
  handleFileReadError(_TraceId):- 
    fail. 

In the code we try to find continuedFromDll exception and we retrieve the continued exception by the predicate tryGetContinueException. This clearly shows that if an exception was continued, then we can gain more information about the exception.

The full example presents in continueException\testContinuedException\testContinuedException.prj6 project. It is necessary to build continueException\continueException.prj6 before running testContunedException executable.

 

Home | Company | News | Products Downloads | Shop | Support | Visual Prolog Features | Visual Prolog Compiler | FAQ | Tutorials | Examples | How to Migrate from Visual Prolog 5 to Visual Prolog 7 | Knowledge Base | Discussion Forum | wiki | Site Map
 

Prolog Development Center A/S - H.J. Holst Vej 3-5C - 2605 Broendby, Denmark - Tel +45 3636 0000 - Fax +45 3636 0001 - sales@visual-prolog.com