So, we wrote a toy file reader and put in some simple error handling. There are, however, other ways to fail than simply failing to open the file. In Lisp, the conditions that are signaled must be derived from the condition type. So we can put a general catcher for all errors not caught by file-error as follows:
;; Some examples of using conditions in Lisp code. ;; (declaim (optimize (debug 3) (safety 3))) (defun condition-demo (pathname) (format t "Starting to analyse file ~A~%" pathname) (handler-case (with-open-file (s pathname :direction :input) (analyse-stream s)) (file-error (c) (format t "A file error was encountered while trying to analyse ~A.~%The error returned was:~%~A~%" pathname c)) (condition (c) (format t "A non-file error was encountered while trying to analyse ~A.~%The error returned was:~%~A~%" pathname c)))) ;; Goes through the stream, dividing entries one by the next (defun analyse-stream (stream) (do () (nil) (let ((num1 (get-next-number stream)) (num2 (get-next-number stream))) (format t "The ratio of ~A to ~A is ~A~%" num1 num2 (/ num1 num2))))) ;; Returns the next object in the stream (defun get-next-number (stream) (read stream))
Now, if a file-error occurs, one message is delivered, and if a non-file-error occurs, a different message is delivered. In all cases, the function then exits cleanly, without further processing the file.
Of course, if we reach the end of the file, it’s probably OK for us just to exit cleanly with no error message. An attempt to read past the end of the file will, by default, signal a condition of type end-of-file. We can put a handler on that exception. We now have the following code:
;; Some examples of using conditions in Lisp code. ;; (declaim (optimize (debug 3) (safety 3))) (defun condition-demo (pathname) (format t "Starting to analyse file ~A~%" pathname) (handler-case (with-open-file (s pathname :direction :input) (analyse-stream s)) (file-error (c) (format t "A file error was encountered while trying to analyse ~A.~%The error returned was:~%~A~%" pathname c)) (end-of-file () (format t "Successfully reached the end of the file.~%")) (condition (c) (format t "A non-file error was encountered while trying to analyse ~A.~%The error returned was:~%~A~%" pathname c)))) ;; Goes through the stream, dividing entries one by the next (defun analyse-stream (stream) (do () (nil) (let ((num1 (get-next-number stream)) (num2 (get-next-number stream))) (format t "The ratio of ~A to ~A is ~A~%" num1 num2 (/ num1 num2))))) ;; Returns the next object in the stream (defun get-next-number (stream) (read stream))
However, I promised to show a non-C++ style handling of conditions. That’s what we’ll cover in the next post.