Error in Error

Discussion in 'AutoCAD' started by Ken Alexander, Aug 6, 2004.

  1. The code below is a basic error routine that is plastered all over
    this NG. This error routine has an error in it itself. The problem
    is (error nil) at the bottom of the main routine. When an error
    occurs a string is passed as an argument and generally tested as a
    string in the error routine. Nil is not a string. I believe this
    error goes unnoticed because a custom error routine will recurse on
    itself one time if there is an error in the error routine therefore
    all sys var are reset. If you cause two errors, as I have done with
    (1+ msg), the default acad error kicks in and crashes the custom error
    routine causing sys vars. not to be reset.
    I would think it would be better to pass an empty string (*error* "")
    and then test for an empty string in the error routine.

    (defun C:test (/ *error* oce oos)
    (setq oce (getvar "cmdecho")
    oos (getvar "osmode"))
    (setvar "cmdecho" 0)
    (setvar "osmode" 0)

    (defun *error* (msg)
    (if
    (not
    (member
    msg
    '("console break" "Function cancelled" "quit / exit abort")
    )
    )
    (princ (strcat "\nError: " msg))
    )
    ;(1+ msg)
    (setvar "cmdecho" oce)
    (setvar "osmode" oos)
    (princ)
    )

    ;do something

    (*error* nil)
    )

    --
    Ken Alexander
    Acad2004
    Windows XP

    "We can't solve problems by using the same kind
    of thinking we used when we created them."
    --Albert Einstein
     
    Ken Alexander, Aug 6, 2004
    #1
  2. I guess I generally use (or something like it)

    (if
    (and msg
    (


    but thanks for pointing that out.
     
    Jason Piercey, Aug 6, 2004
    #2
  3. I've went both ways. I've been reading up on error routines and have
    found several posted around with this problem. Also have read that
    custom error routine will not recurse on itself. They do one time
    only which is kind of strange.

    --
    Ken Alexander
    Acad2004
    Windows XP

    "We can't solve problems by using the same kind
    of thinking we used when we created them."
    --Albert Einstein
     
    Ken Alexander, Aug 6, 2004
    #3
  4. Ken,

    This is my general template for that approach.

    (defun C:Test (/ *Error*)
    (defun *Error* (Msg)
    (cond ((not Msg)) ; normal exit (nil)
    ((member Msg '("Function cancelled" "quit / exit abort"))) ; <Esc>
    ((princ (strcat "\nError: " Msg)) ; display fatal error
    (cond (*Debug* (vl-bt))))) ; if in debug mode, dump backtrace
    (rrbI:VarSav nil)) ; restore environment
    ;| main code here |;
    (*error* nil))

    I think some people probably didn't understand the ((not Msg)) conidition,
    and removed it causing the error you pointed out.

    --
    R. Robert Bell


    "Ken Alexander" <kalexATfremontmillworkDOTcom> wrote in message
    The code below is a basic error routine that is plastered all over
    this NG. This error routine has an error in it itself. The problem
    is (error nil) at the bottom of the main routine. When an error
    occurs a string is passed as an argument and generally tested as a
    string in the error routine. Nil is not a string. I believe this
    error goes unnoticed because a custom error routine will recurse on
    itself one time if there is an error in the error routine therefore
    all sys var are reset. If you cause two errors, as I have done with
    (1+ msg), the default acad error kicks in and crashes the custom error
    routine causing sys vars. not to be reset.
    I would think it would be better to pass an empty string (*error* "")
    and then test for an empty string in the error routine.

    (defun C:test (/ *error* oce oos)
    (setq oce (getvar "cmdecho")
    oos (getvar "osmode"))
    (setvar "cmdecho" 0)
    (setvar "osmode" 0)

    (defun *error* (msg)
    (if
    (not
    (member
    msg
    '("console break" "Function cancelled" "quit / exit abort")
    )
    )
    (princ (strcat "\nError: " msg))
    )
    ;(1+ msg)
    (setvar "cmdecho" oce)
    (setvar "osmode" oos)
    (princ)
    )

    ;do something

    (*error* nil)
    )

    --
    Ken Alexander
    Acad2004
    Windows XP

    "We can't solve problems by using the same kind
    of thinking we used when we created them."
    --Albert Einstein
     
    R. Robert Bell, Aug 6, 2004
    #4
  5. Ken Alexander

    GaryDF Guest

    This is what I use, am I doing it right?

    (defun ARCH:ERROR (msg)
    (if (or (= msg "*Cancel*")
    (= msg "exit")
    (= msg "quit / exit abort")
    (= msg "Funtion cancelled")
    (= msg "console break")
    )
    (princ "\n*** ///////// Program CANCELLED ///////// ***\n")
    )
    (setq *error* ORG_ERROR)
    (redraw)
    (ARCH:F_R-VAR) ;;my restore vars routine
    (princ)
    )

    Gary
     
    GaryDF, Aug 6, 2004
    #5
  6. That depends. Are you using the error handler to cleanup after a normal exit
    of the main code? If you are, then no, there is no provision for the nil
    argument.

    That question is important because it doesn't make much sense to write the
    same code to handle cleanup both for errors and at the end of the main code
    for a normal finish.

    So some of us are using the error handler, or at least a call to an exit
    function, to do the work in both cases.

    (defun i:ExitFunc ()
    ;| setvar cleanup and undos ;|
    (princ))

    (defun C:Test (/ *Error*)
    (defun *Error* (msg)
    ;| error handling |;
    (i:ExitFunc))

    ;| main code |;

    (i:ExitFunc))

    --or--

    The approach I showed in the prior post simply uses the *Error* function
    directly for both an error and a normal exit; if the error handler is passed
    nil as the error message, I know it is simply a normal exit, and print no
    error message.

    --
    R. Robert Bell


    This is what I use, am I doing it right?

    (defun ARCH:ERROR (msg)
    (if (or (= msg "*Cancel*")
    (= msg "exit")
    (= msg "quit / exit abort")
    (= msg "Funtion cancelled")
    (= msg "console break")
    )
    (princ "\n*** ///////// Program CANCELLED ///////// ***\n")
    )
    (setq *error* ORG_ERROR)
    (redraw)
    (ARCH:F_R-VAR) ;;my restore vars routine
    (princ)
    )

    Gary
     
    R. Robert Bell, Aug 6, 2004
    #6
  7. Since msg isn't being passed as a bad argument type yours is fine.

    --
    Ken Alexander
    Acad2004
    Windows XP

    "We can't solve problems by using the same kind
    of thinking we used when we created them."
    --Albert Einstein
     
    Ken Alexander, Aug 6, 2004
    #7
  8. Well, Ken, in all honesty, you can't tell what is being passed there. ;^)

    --
    R. Robert Bell


    "Ken Alexander" <kalexATfremontmillworkDOTcom> wrote in message
    Since msg isn't being passed as a bad argument type yours is fine.
     
    R. Robert Bell, Aug 6, 2004
    #8
  9. That depends. Are you using the error handler to cleanup after a
    normal exit
    of the main code? If you are, then no, there is no provision for the
    nil
    argument.<<

    Don't quite understand what you mean. There is no provision for the
    nil, but the error argument is only being tested in his routine. It's
    not being pasted as a bad argument.

    --
    Ken Alexander
    Acad2004
    Windows XP

    "We can't solve problems by using the same kind
    of thinking we used when we created them."
    --Albert Einstein
     
    Ken Alexander, Aug 6, 2004
    #9
  10. Ken Alexander

    GaryDF Guest

    Here are all of my functions...I use the ARCH:ERROR to cleanup my routines and
    dcl routine abort.
    I have modified my ARCH:ERROR to match yours...seems to be working fine.

    Gary



    (defun ARCH:ERROR-original (msg)
    (if (or (= msg "*Cancel*")
    (= msg "exit")
    (= msg "quit / exit abort")
    (= msg "Funtion cancelled")
    (= msg "console break")
    )
    (princ "\n*** ///////// Program CANCELLED ///////// ***\n")
    )
    (setq *error* ORG_ERROR)
    (redraw)
    (ARCH:F_R-VAR)
    ;;fixed 11/6/02
    (princ)
    )




    (defun ARCH:ERROR (Msg)
    (cond ((not Msg))
    ((member Msg '("Function cancelled" "quit / exit abort")))
    ((princ "\n*** ///////// Program CANCELLED ///////// ***\n")
    (cond (*Debug* (vl-bt)))
    )
    )
    (setq *error* ORG_ERROR)
    (redraw)
    (ARCH:F_R-VAR)
    )

    (defun ARCH:F_R-VAR ()
    (setq op nil
    sset nil
    )
    (if (boundp 'stdvarlist)
    (ARCH:RESTVARS stdvarlist)
    (princ "\r\n*** ///////// Nothing to Restore ///////// ***")
    )
    (if SSET
    (command "change" SSET "" "p" "c" "bylayer" "")
    )
    (setq *error* ORG_ERROR)
    (setvar "cmdecho" arch_cmd)
    (ARCH:UEND)
    ;;for undo
    (princ)
    )
    ;;;
    (defun ARCH:RESTVARS (varslist)
    (while (boundp 'varslist)
    (cond
    ((= (strcase (car (car varslist))) "CLAYER")
    (progn (setvar "cmdecho" 0)
    (command ".layer" "m" (cadr (car varslist)) "")
    )
    )
    ((= (strcase (car (car varslist))) "CELTYPE")
    (progn (setvar "cmdecho" 0)
    (command ".linetype" "s" (cadr (car varslist)) "")
    )
    )
    ((= (strcase (car (car varslist))) "CECOLOR")
    (progn
    (setvar "cmdecho" 0)
    (command
    ".color"
    (if (and (> (ascii (car (cdr (car varslist)))) 47)
    ;; is 1st char a digit?
    (< (ascii (car (cdr (car varslist)))) 58)
    )
    (atoi (car (cdr (car varslist))))
    ;; return this if 1st char is digit
    (car (cdr (car varslist)))
    ;; return this otherwise
    )
    )
    )
    )
    (t (setvar (car (car varslist)) (cadr (car varslist))))
    )
    (setq varslist (cdr varslist))
    )
    (if (/= arch_dms nil)
    (progn (setvar "cmdecho" 0)
    (command "dimstyle" "r" arch_dms)
    (graphscr)
    )
    )
    (princ)
    )

    (defun ARCH:F_S-VAR ()
    (setq arch_cmd (getvar "cmdecho"))
    (ARCH:UBEG)
    ;;for undo
    (setq op nil
    sset nil
    ARCH#SSET nil
    ARCH#_NIC nil
    )
    (setq arch_dms (getvar "dimstyle"))
    (setq ORG_ERROR *error*
    *error* ARCH:ERROR
    ;;fixed 11/6/02
    )
    (setq stdvarlist
    (ARCH:SAVEVARS
    (list '"APERTURE" '"ATTDIA" '"ATTREQ"
    '"BLIPMODE" '"CECOLOR" '"CLAYER"
    '"CELTYPE" '"CMDECHO" '"DIMSCALE"
    '"DRAGMODE" '"EXPERT" '"FILEDIA"
    '"FILLETRAD" '"GRIDMODE" '"HIGHLIGHT"
    '"LUNITS" '"MENUECHO" '"MIRRTEXT"
    '"OFFSETDIST" '"ORTHOMODE" '"OSMODE"
    '"PICKBOX" '"PLINEWID" '"REGENMODE"
    '"SNAPMODE" '"SNAPUNIT" '"SNAPBASE"
    '"SNAPANG" '"SNAPSTYL" '"TEXTEVAL"
    ;;'"TEXTSTYLE"
    )
    )
    )
    (princ)
    )
    ;;;
    (defun ARCH:SAVEVARS (listonames / newlist)
    (setq newlist nil)
    (while (boundp 'listonames)
    (if (null newlist)
    (setq newlist
    (list
    (cons (car listonames) (list (getvar (car listonames))))
    )
    )
    (setq
    newlist
    (cons (cons (car listonames)
    (list (getvar (car listonames)))
    )
    newlist
    )
    )
    )
    (setq listonames (cdr listonames))
    )
    newlist
    )

    (defun ARCH:UBEG ()
    (setvar "CMDECHO" 0)
    (setq UNDO_BEGIN T)
    (command "UNDO" "BEGIN")
    (setvar "CMDECHO" 1)
    (princ)
    )
    (defun ARCH:UEND ()
    (setvar "CMDECHO" 0)
    (setq UNDO_BEGIN ())
    (command "UNDO" "END")
    (setvar "CMDECHO" 1)
    (princ)
    )
     
    GaryDF, Aug 6, 2004
    #10
  11. Exactly (I think) ;^)

    Gary has posted his subroutines without the context of how they are being
    used. So if we take Gary's code and...:

    (defun C:Test (/ *Error*)
    (setq *Error* ARCH:ERROR)

    ;| main code here |;

    (*error* nil))

    .... he would get the error you mention. Without the context of *how* Gary
    uses the subr, we really can't say if it is "correct" or not.

    It is obvious from Gary's code that he is still saving/restoring the error
    subr pre-2000 style (org_error) vs. just localizing the *error* symbol.

    --
    R. Robert Bell


    normal exit
    of the main code? If you are, then no, there is no provision for the
    nil
    argument.<<

    Don't quite understand what you mean. There is no provision for the
    nil, but the error argument is only being tested in his routine. It's
    not being pasted as a bad argument.

    --
    Ken Alexander
    Acad2004
    Windows XP

    "We can't solve problems by using the same kind
    of thinking we used when we created them."
    --Albert Einstein
     
    R. Robert Bell, Aug 6, 2004
    #11
  12. Ken Alexander

    GaryDF Guest

    Here is how I am using the error code.

    Gary

    (defun C:TEST ()
    (ARCH:F_S-VAR)
    ;| main code here |;
    (ARCH:F_R-VAR)
    (princ)
    )
     
    GaryDF, Aug 6, 2004
    #12
  13. Gottcha Robert.

    Do you know why a custom error routine will recurse on itself only
    once?

    --
    Ken Alexander
    Acad2004
    Windows XP

    "We can't solve problems by using the same kind
    of thinking we used when we created them."
    --Albert Einstein
     
    Ken Alexander, Aug 6, 2004
    #13
  14. I'm surprised it did. Smart thinking to avoid a key-rash?

    --
    R. Robert Bell


    "Ken Alexander" <kalexATfremontmillworkDOTcom> wrote in message
    Gottcha Robert.

    Do you know why a custom error routine will recurse on itself only
    once?

    --
    Ken Alexander
    Acad2004
    Windows XP

    "We can't solve problems by using the same kind
    of thinking we used when we created them."
    --Albert Einstein
     
    R. Robert Bell, Aug 6, 2004
    #14
  15. Ken Alexander

    David Bethel Guest

    There was a in depth discussion of this back in the Compuserve days.

    The general consensus was ( if I remember correctly ) that due the fact
    that *error* is hard coded into ACAD, that a user should never call it
    directly. If you need a user error trapping function, it should be
    totally independent function.

    (defun my_error (s) .... )

    -David
     
    David Bethel, Aug 6, 2004
    #15
  16. David,

    Could you elaborate on that a little? I don't
    see a difference between the hard coded
    *error* function and any other hard coded
    function (pick one).
     
    Jason Piercey, Aug 6, 2004
    #16
  17. I agree. Doesn't make much sense to throw your routine into an
    *error*. Makes sense to have an exiting routine as Robert pointed
    out.

    --
    Ken Alexander
    Acad2004
    Windows XP

    "We can't solve problems by using the same kind
    of thinking we used when we created them."
    --Albert Einstein
     
    Ken Alexander, Aug 6, 2004
    #17
  18. I guess I should have read that a little slower. I don't see the
    problem in calling the *error* function just like any other function.
    I just think that it makes more sense to exit without an error.

    --
    Ken Alexander
    Acad2004
    Windows XP

    "We can't solve problems by using the same kind
    of thinking we used when we created them."
    --Albert Einstein
     
    Ken Alexander, Aug 6, 2004
    #18
  19. Ken Alexander

    John Uhden Guest

    Ken:

    I didn't read all the responses, but take a look at:
    (princ (strcat "\nError: " msg))

    Er, if msg is nil you've got your error within *error*
     
    John Uhden, Aug 7, 2004
    #19
  20. Ken Alexander

    Tom Smith Guest

    Ken, I'm probably one indirect source of the *error* error. I agree that the code you posted is erronious when it calls (*error* nil) on normal termination. However, it would be perfectly fine if it called (*error* "") or (*error* "\nDone."). Or *error* might be revised to include a (null msg) test as Robert noted.

    Passing an incorrect argument type to a function isn't a flaw in the function, strictly speaking, it's an error in calling it. Perhaps the function should be able to block all invalid input whatsoever, and fail gracefully on a bad argument type, but that's a bit harsh IMHO when we're talking about a behind-the-scenes function like a custom *error*, which nobody but its author is going to explicitly call. The author should know how to call his own function correctly.

    The flaw in not in the princ -- (princ nil) is valid -- it's in the strcat which puts the "\nError: " in front of msg. Since that will fail on anything but a string, a more comprehensive argument test would be:

    (if (stringp msg)
    (do_the_princ_thing)
    ;else, who cares
    )

    In the error-handling approach I've posted, I've indeed used an (*error* nil) on normal termination. Because my version of a custom *error* contained no provision for echoing the cryptic Acad-generated error message. Perhaps it should -- that's another discussion. In the version I've posted, msg was a dummy parameter which wasn't used at all. But in versions where I've included error-echoing, I've used only (princ msg), which will still work with a null msg. Without the strcat there's no harm.

    When the user sees nothing good happen, and an incomprehensible line of gobbledygook appears on their command line, believe me, they know it's an error without the title "Error: " being prepended! The ugliness of this is the main reason I shield users from it. Nowadays we could use the debug-mode flag to conrol things, but in actual practice I haven't needed to add that small bit of complexity. In the very rare cases where my functions fail (silently) due to unforeseen conditions, I just comment out the error handler and run it again to see what happened. If I were going to echo anything to the user on error, it would probably be "\nUnknown error, call Tom!" or something of that nature. Of course these are in-house programs, not publicly distributed. That would be different, obviously.

    This thread gets into semantics a bit, I think, on when and where *error* "should" be called, and corresponding philosophies on whether a variable-restoring function should be part of it. My opinion is that the *error* function, as Acad supplies it, doesn't necessarily imply badness, wrongness, or failure -- it's not a term of judgement, and we shouldn't let our everyday associations of the word "error" dictate how we "must" use an *error* function.

    Why? Because Acad saw fit to include cancellation via Esc as an "error" condition which will trigger *error*. And there's nothing bad or wrong about that. Users hit Esc all the time, for valid reasons of their own. It's not an "error" in everyday language, but it is in the way Acad handles it.

    What we're really talking about in all these threads is a cancellation-handler, which will restore any variables that were altered before the function was aborted. It needs to be an undo-handler as well, to guarantee that the function's undo group is closed on cancellation.

    Now you can call your restoration function (variable-restore <variable-list>) if you like, but that won't be automatically called on cancellation, whereas a custom *error* will. To me it's simpler and more straightforward to deal with the variables and the undo all in the same local custom *error* function. And since the things I want done on cancellation are identically the same as the things I want done on normal termination, it's more clear IMHO to explicitly call the same *error* in the latter case, rather than sugar-coat it by calling the "good" case something different. I don't think of this as ending the function with an "error" (everyday meaning). I think of it as exiting cleanly, regardless of the circumstances, using an automatic mechanism which happens to have the hard-coded name *error*.
     
    Tom Smith, Aug 7, 2004
    #20
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.