Обработка исключений: различия между версиями

Материал из wikiru.visual-prolog.com

Строка 107: Строка 107:
Полностью пример представлен в проекте '''raiseException\raiseException.prj6'''.
Полностью пример представлен в проекте '''raiseException\raiseException.prj6'''.


==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:
Рассмотрим программу, помещенную в DLL, которая читает текстовый файл и его содержимое передает в возвращаемом параметре. Если происходит исключение, то DLL продолжает его и добавляет информацию о версии DLL. Для продолжения исключения код выглядит так:


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


The predicate continuedFromDll is declared as:
Предикат continuedFromDll объявлен как:


<vip>predicates
<vip>predicates
    continuedFromDll : core::exception as "_ContinuedFromDll@12"</vip>
  continuedFromDll : core::exception as "_ContinuedFromDll@12"</vip>


and it is exported from the DLL. The implementation of the continuedFromDll predicate is created by a template ( the same as myExceptionZeroFileSizeabove):
и этот предикат экспортируется из DLL. Имплементация предиката continuedFromDll создается по шаблону (аналогично предикату myExceptionZeroFileSizeabove):


<vip>clauses
<vip>clauses
     continuedFromDll(classInfo, predicate_Name(),
  continuedFromDll
          "Exception continued from continueException.DLL").</vip>
     (
    classInfo,
    predicate_Name(),
    "Exception continued from continueException.DLL"
    ).</vip>


When the predicate exception::continue continues the exception, it adds extra information about dllVersion.
При продолжении исключения предикат exception::continue добавляет информацию о версии DLL.


The full example presents in '''continueException\continueException.prj6''' project.
Полностью пример представлен в проекте '''continueException\continueException.prj6'''.


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.
Посмотрим теперь, как перехватывать такого рода исключения. Код в принципе похож на обсуждавшийся выше, поэтому мы сосредоточимся на различиях.


<vip>constants
<vip>constants
    myFileName = "my.txt".
  myFileName = "my.txt".
clauses
clauses
    run():-
  run():-
        console::init(),
    console::init(),
        initExceptionState(exception::getExceptionState()),
    initExceptionState(exception::getExceptionState()),
        trap(MyTxtFileContent = loadFile(myFileName),
    trap
            ErrorCode,
      (
            handleFileReadError(ErrorCode)),
      MyTxtFileContent = loadFile(myFileName),
        !,
      TraceID,
        stdIO::write("The content of ",myFileName,"is:\n",MyTxtFileContent).
      handleFileReadError(TraceID)
    run().
      ),
      !,
      stdIO::write("The content of ",myFileName,"is:\n",MyTxtFileContent).
  run().


class predicates
class predicates
    handleFileReadError : ( exception::traceID TraceId )failure .
  handleFileReadError : ( exception::traceID TraceId )failure .
clauses
clauses
    handleFileReadError(TraceId):-
  handleFileReadError(TraceId):-
        DescriptorContinued = exception::tryGetDescriptor(TraceId, continuedFromDll),
    DescriptorContinued = exception::tryGetDescriptor(TraceId, continuedFromDll),
        ErrorDescriptorInformation = exception::tryGetErrorDescriptorInformation(TraceId),
    ErrorDescriptorInformation = exception::tryGetErrorDescriptorInformation(TraceId),
        ContinueException = ErrorDescriptorInformation:tryGetContinueException(),
    ContinueException = ErrorDescriptorInformation:tryGetContinueException(),
        !, %file cannot be loaded
    !, %file cannot be loaded
        exception::descriptor(_ErrorCodeContinued,
    exception::descriptor
            _ClassInfoContinued,
      (_ErrorCodeContinued,
            _ExceptionContinued,
      _ClassInfoContinued,
            _KindContinued,
      _ExceptionContinued,
            ExtraInfoContinued,
      _KindContinued,
            _,
      ExtraInfoContinued,
            _GMTTimeContinued,
      _,
            ExceptionDescriptionContinued,
      _GMTTimeContinued,
            _ThreadIdContinued) = DescriptorContinued,
      ExceptionDescriptionContinued,
        Version = namedValue::mapLookUp(ExtraInfoContinued, version_Parameter, string("")),
      _ThreadIdContinued) = DescriptorContinued,
        stdIO::write("Exception continued : ",ExceptionDescriptionContinued,
      Version = namedValue::mapLookUp(ExtraInfoContinued, version_Parameter, string("")),
            "\nDllVersion: ", Version,"\n"),
      stdIO::write("Exception continued : ",ExceptionDescriptionContinued,"\nDllVersion: ", Version,"\n"),
        ExtraInfo = ContinueException:getExtraInfo(),
      ExtraInfo = ContinueException:getExtraInfo(),
        ExceptionDescription = ContinueException:getExceptionDescription(),
      ExceptionDescription = ContinueException:getExceptionDescription(),
        FileName = namedValue::mapLookUp(ExtraInfo,
      FileName = namedValue::mapLookUp(ExtraInfo,fileSystem_api::fileName_parameter, string("")),
        fileSystem_api::fileName_parameter, string("")),
      Reason = namedValue::mapLookUp(ExtraInfo,common_exception::errorDescription_parameter, string("")),
        Reason = namedValue::mapLookUp(ExtraInfo,
      stdIO::write
            common_exception::errorDescription_parameter, string("")),
        (
        stdIO::write("Cannot load file due to: ",ExceptionDescription,
        "Cannot load file due to: ",ExceptionDescription,
            "\nFile Name: ", FileName,
        "\nFile Name: ", FileName,
            "\nReason: ", Reason ),
        "\nReason: ", Reason ),
        fail.
      fail.
    handleFileReadError(TraceId):-
  handleFileReadError(TraceId):-
        isDebugMode = true,
    isDebugMode = true,
        !,
    !,
        exceptionDump::dumpToStdOutput(TraceId),
    exceptionDump::dumpToStdOutput(TraceId),
        fail.
    fail.
    handleFileReadError(_TraceId):-
  handleFileReadError(_TraceId):-
        fail.</vip>
    fail.</vip>


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.
Мы пытаемся здесь найти исключение '''continuedFromDll''' и мы вызываем продолжающееся предикатом исключение '''tryGetContinueException'''. Это ясно показывает, что, если исключение продолжается, то мы можем получить дополнительную информацию касательно самого исключения.


The full example presents in '''continueException\testContinuedException\testContinuedException.prj6''' project. It is necessary to build '''continueException\continueException.prj6''' before running testContunedException executable.
Полностью пример представлен в проекте '''continueException\testContinuedException\testContinuedException.prj6'''. Необходимо построить сначала приложение '''continueException\continueException.prj6''' перед тем как вызывать на выполнение исполняемое приложение testContunedException.


==Ссылки==
==Ссылки==

Версия 10:45, 29 октября 2007

Исключение - это отклонение программы от маршрута "нормального" выполнения. Пакет exception из состава PFC содержит предикаты для перехвата исключений, которые возникают в программах на Прологе. Это руководство объясняет:

  • Как обрабатывать исключения;
  • Как возбуждать ваши собственные исключения;
  • Как продолжать другое исключение.

Как перехватывать исключения

Рассмотрим программу, которая читает текстовый файл и выводит его содержимое на консоль. PFC предлагает предикат file::readString, который позволяет выполнить эту задачу. Однако, некоторые обстоятельтства могут помешать эту задачу; например, заданное имя файла ссылается на несуществующий файл. Когда предикат file::readString не может выполнить задачу, он генерирует исключение.

Visual Prolog предлагает встроенный предикат trap/3, который перехватывает исключение и позволяет его обработать. Детальное описание предиката trap/3 можно найти в справке по Visual Prolog (Language Reference -> Built-in Domains, Predicates and Constants-> Predicates ->trap).

Соответственно, код для перехвата исключения будет выглядеть так:

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

Самое интересное здесь это - имплементация предиката handleFileReadError:

class predicates
    handleFileReadError : ( exception::traceID TraceId ) failure.
clauses
    handleFileReadError(TraceId):-
        Descriptor = exception::tryGetDescriptor(TraceId, fileSystem_api::cannotcreate),
        !, % файл не может быт загружен
        exception::descriptor(_ErrorCode, % ErrorCode используется, если пользователь сам вызывает errorExit.
            _ClassInfo, % информация о классе, который возбудил исключение.
            _Exception, % на самом деле это fileSystem_api::cannotcreate,
            % но параметр не может участвовать в сравнении с помощью ' = '.
            % смотрите exceptionState::equals
            _Kind, % исключени может быть возбуждено или продолжено
            ExtraInfo,
            _TraceId, % здесь идентификатор ошибки, но необходимости в проверке нет
            _GMTTime, % время возникновения исключения
            ExceptionDescription,
            _ThreadId) = Descriptor,
        FileName = namedValue::mapLookUp(ExtraInfo,
        fileSystem_api::fileName_parameter, string("")),
        Reason = namedValue::mapLookUp(ExtraInfo,
        common_exception::errorDescription_parameter, string("")),
        stdIO::write("Невозможно загрузить файл по причине: ",ExceptionDescription,
            "\nИмя файла: ", FileName,
            "\nПричина: ", Reason ),
        fail.
    handleFileReadError(ErrorCode):-
        isDebugMode = true, % режим отладки
        !,
        exceptionDump::dumpToStdOutput(ErrorCode),
        % выводим на консоль, поскольку режим отладки
        fail.
    handleFileReadError(_ErrorCode):-
        % программа не может обработать исключение и не информирует о нем
        fail.

Когда предполагается исключение fileSystem_api::cannotcreate, предикат exception::tryGetDescriptor вызывается именно с этим параметром для перехвата и обработки такого исключения. В нашем случае используется вывод на консоль:

stdIO::write("Невозможно загрузить файл по причине: ", ExceptionDescription,
    "\nИмяФайла: ", FileName,
    "\nПричина: ", Reason ),

с объяснением причины исключения.

Обратите внимание, что нет необходимости в очистке следов исключения, поскольку вся необходимая информация сохраняется в TraceId. Этот факт значительно ускоряет работу программы.

Полный пример содержится в проекте catchException\catchException.prj6.

Как вызвать исключение

Рассмотрим программу, которая проверяет размер заданного файла. PFC предлагает предикат file::getFileProperties, который возвращает размер заданного файла. Если файл имеет нулевой размер, то мы можем вызвать исключение с помощью предиката exception::raise. Необходимо при этом создать предикат, который будет характеризовать исключение. С этой целью мы создаем предикат myExceptionZeroFileSize. Код, вызывающий исключение, будет выглядеть так:

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().

Первый параметр предиката exception::raise - предикат classInfo, который генерируется средой IDE для каждого нового класса. Для генерируемого исключения неплохо было бы определить дополнительную информацию, относящуюся к данному исключению. В нашем случае полезно имя файла, поскольку нулевой размер связан с этим заданным файлом.

Предикат myExceptionZeroFileSize создается по шаблону:

clauses
  myExceptionZeroFileSize
    (
    classInfo,
    predicate_Name(),
    "Размер файла не может быть нулевым"
    ).

Здесь первый и второй параметры предикатов исключений всегда есть classInfo и predicate_Name() соответственно, а третий параметр содержит текстовое пояснение причины исключения.

Полностью пример представлен в проекте raiseException\raiseException.prj6.

Продолжение другого исключения

Рассмотрим программу, помещенную в DLL, которая читает текстовый файл и его содержимое передает в возвращаемом параметре. Если происходит исключение, то DLL продолжает его и добавляет информацию о версии DLL. Для продолжения исключения код выглядит так:

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))])
      ).

Предикат continuedFromDll объявлен как:

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

и этот предикат экспортируется из DLL. Имплементация предиката continuedFromDll создается по шаблону (аналогично предикату myExceptionZeroFileSizeabove):

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

При продолжении исключения предикат exception::continue добавляет информацию о версии DLL.

Полностью пример представлен в проекте continueException\continueException.prj6.

Посмотрим теперь, как перехватывать такого рода исключения. Код в принципе похож на обсуждавшийся выше, поэтому мы сосредоточимся на различиях.

constants
  myFileName = "my.txt".
clauses
  run():-
    console::init(),
    initExceptionState(exception::getExceptionState()),
    trap
      (
      MyTxtFileContent = loadFile(myFileName),
      TraceID,
      handleFileReadError(TraceID)
      ),
      !,
      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.

Мы пытаемся здесь найти исключение continuedFromDll и мы вызываем продолжающееся предикатом исключение tryGetContinueException. Это ясно показывает, что, если исключение продолжается, то мы можем получить дополнительную информацию касательно самого исключения.

Полностью пример представлен в проекте continueException\testContinuedException\testContinuedException.prj6. Необходимо построить сначала приложение continueException\continueException.prj6 перед тем как вызывать на выполнение исполняемое приложение testContunedException.

Ссылки