понедельник, 28 мая 2012 г.

(? 'вопрос)

А вот можно ли сделать такой хак, чтобы в общелиспе при бросании ошибки можно было бы эту ошибку "заметить", не раскручивая стэк, сделать что-нибудь, типа пометки в логе, и пусть эта ошибка идёт дальше? Идеально подошёл бы unwind-protect с доступом к объекту ошибки в cleanup-form, но я что-то в Лиспворксе, немного покопавшись, не нашёл, как такое сделать.

Может, кто-нибудь на свободных лиспах такое делал?

17 комментариев:

  1. кури про handler-bind. unwind-protect для другого предназначен.

    ОтветитьУдалить
    Ответы
    1. Я, кажется, написал, что "не раскручивая стэк".

      Удалить
    2. Анонимный30 мая 2012 г., 0:51

      Если не делать non-local transfer из handler-bind'ового handler'a, то и не будет "раскручивания стэка". Или я гоню?

      (defun lousy-function ()
      (cerror "Continue?" "Fucked up."))

      (defun well-designed-function ()
      (let ((data (list 1 2 3)))
      (handler-bind ((simple-error #'(lambda (condition)
      (format t "Got screwed: ~A." condition))))
      (format t "Calling some stuff...~%")
      (lousy-function)
      (format t "~&Seems ok: ~A.~%" data))))

      CL-USER> (compile 'lousy-function)
      CL-USER> (compile 'well-designed-function)

      CL-USER> (well-designed-function)
      Calling some stuff...
      Got screwed: Fucked up..; Evaluation aborted on #.

      CL-USER> (well-designed-function)
      Calling some stuff...
      Got screwed: Fucked up..
      Seems ok: (1 2 3).
      NIL

      Удалить
    3. Анонимный30 мая 2012 г., 0:53

      В последнем тесте жмем "CONTINUE".

      Удалить
    4. Анонимус абсолютно прав. handler-bind стэк сам не раскручивает как раз, раскручивать его или нет решает сам обработчик. Если он завершится нормально (в смысле нормального возврата из функции) - то дальнейшее выполнение пойдёт так, как будто этого обработчика и не было вообще - этот вариант тебе и нужен; если будет выполнен нелокальный переход из обработчика вовне него - будет раскрутка стэка. handler-case именно так и реализуется - он разворачивается в handler-bind + нелокальный переход из него.

      Удалить
    5. Да, handler-bind - то, что надо. Спасибо.

      Интересно, что в CLHS этого нет. В CLTL2 в главе про кондишены, впрочем, расписано...

      Удалить
    6. Анонимный31 мая 2012 г., 11:44

      (другой анонимус) Ну, блин, и знатоки... не знать такой элементарной важной вещи. Как минимум в PCL это расписано.

      Удалить
  2. забыл сказать - это не хак, это стандартная фича общелиспа. в статьях на lisper.ru про это есть.

    ОтветитьУдалить
  3. Лучше покурить статью про рестарты из PCL. Это - одна из трёх более мозголомных фич языка. И, точки откуда ошибке «идти дальше» придётся явно раскидывать по программе.

    ОтветитьУдалить
    Ответы
    1. Нет, рестарт мне точно не нужен.

      Удалить
  4. Анонимный29 мая 2012 г., 13:43

    В порядке гипотез:

    Если места с ошибками примерно известны, то можно завернуть их в макру, которая временно перенаправляет *trace-output* в чего-нибудь фильтрующее, а потом завёртывает внутренности в функцию с известным именем, которая возвращает либо то, что получилось, либо маркер. Фильтр или видит маркер и что-то пишет или забивает на пришедшее. При запуске говорим trace функции.

    Ещё в CLHS пишут про :report у condition'ов, но непонятно, получится ли как-то это заюзать. Можно попробовать before-метод на print-object условий повесить, наверное

    ОтветитьУдалить
  5. Анонимный29 мая 2012 г., 16:23

    (defpackage :nice.stuff
    (:use :common-lisp)
    (:shadow :error))

    (in-package :nice.stuff)

    (defun error (&rest err)
    (format t "~a~%" err)
    (apply #'cl:error err))

    (error "Wow!")

    ОтветитьУдалить
    Ответы
    1. Переопределение error'а не канает, т.к. есть ловиться должны, в основном, error'ы не моего пакета.

      Удалить
    2. Анонимный29 мая 2012 г., 17:59

      Чего не так с переопределением error'а? Пусть в :common-lisp будет, чтобы всё ловить.

      (in-package :common-lisp)

      (setf (symbol-function '%real-error-function) #'error)

      (defun error (&rest err)
      (format t "Catched: ~a~%" err)
      (apply #'%real-error-function err))

      (defpackage :nice.stuff
      (:use :common-lisp))
      (in-package :nice.stuff)

      (error "Wow!")

      Удалить
    3. К размышлению: (make-instance 'error) и прочее...

      Удалить
  6. мелкое замечание: handler-bind не поможет если кто-то ниже по стэку тоже эту ошибку "заметит" и обработает - не пустит дальше.

    Т.е. если решение нужно только для необработанных ошибок, тогда отлично подходит. А так чтобы логировать вообще все error которые где-либо появляются, но не совсем.

    ОтветитьУдалить