Page 2 of 3 FirstFirst 123 LastLast
Results 11 to 20 of 24

Thread: Error Handling in LISP/Net

  1. #11
    Programming Moderator BlackBox's Avatar
    Join Date
    2009-11
    Posts
    5,482
    Login to Give a bone
    0

    Default Re: Error Handling in LISP/Net

    Especially given the inherent requirement of 'more lines of code' in .NET vs. LISP, etc., I wouldn't fret the extra few lines personally... Then again, I haven't coded in VB in at least a few years anyways (all of my Exchange Apps are in C#). LoL
    "How we think determines what we do, and what we do determines what we get."

    Sincpac C3D ~ Autodesk Exchange Apps

    Computer Specs:
    Dell Precision 3620, Core i7-7700K 4.2GHz, 64GB RAM, Samsung 970 Pro M.2, 8GB NVIDIA Quadro P4000

  2. #12
    Past Vice President / AUGI Volunteer peter's Avatar
    Join Date
    2000-09
    Location
    Honolulu Hawaii
    Posts
    1,029
    Login to Give a bone
    0

    Default Re: Error Handling in LISP/Net

    Moved topic of Common LISP Object System syntax to different thread.

    We can continue to discuss error handling here.
    AutomateCAD

  3. #13
    Certifiable AUGI Addict irneb's Avatar
    Join Date
    2015-11
    Location
    Jo'burg SA
    Posts
    4,512
    Login to Give a bone
    0

    Default Re: Error Handling in LISP/Net

    Just thinking a bit, wondering if this would work. Note the idea from Gile's code (as BlackBox's link) and using the P/Invoke or the newer Application.Invoke.
    Code:
    using System;
    using Autodesk.AutoCAD.DatabaseServices;
    using Autodesk.AutoCAD.Runtime;
    
    namespace ALispDotNetLink {
        /// <summary>
        /// Throw a generic exception to be handled by the Lisp Environment
        /// </summary>
        public class LispException : System.Exception {
        	/// <summary>
        	/// Constructor
        	/// </summary>
        	/// <param name="msg">The error message</param>
            public LispException(string msg) : base(msg) { }
            
            /// <summary>
            /// Pass the exception to the Lisp Environment instead of stack-tracing the DotNet assembly
            /// </summary>
            /// <returns>Always null</returns>
            public object SendLispError() {
                try {
                    ResultBuffer rb = new ResultBuffer();
                    rb.Add(new TypedValue((int)LispDataType.Text, "vl-exit-with-error"));
                    rb.Add(new TypedValue((int)LispDataType.Text, this.Message));
                    LispEnvironment.Invoke(rb);
                } catch (System.Exception) { }
                return null;
            }
        }
    
        /// <summary>
        /// Throw a number-of-arguments exception to be handled by the Lisp Environment
        /// </summary>
        public class LispArgumentCountException : LispException {
        	/// <summary>
        	/// Constructor
        	/// </summary>
        	/// <param name="ToMany">Boolean value: true="too many"; false="too few"</param>
            public LispArgumentCountException(bool ToMany) :
            base(string.Format("too {0} arguments", (ToMany) ? "many" : "few")) { }
        }
    
        /// <summary>
        /// Throw an unexpected argument type exception to be handled by the Lisp Environment
        /// </summary>
        public class LispArgumentException : LispException {
        	/// <summary>
        	/// Constructor
        	/// </summary>
        	/// <param name="s">Expected lisp argument type</param>
        	/// <param name="tv">The actual typed value passed in</param>
            public LispArgumentException(string s, TypedValue tv)
            :base(string.Format("invalid argument type: {0}: {1}", s,
                                tv.TypeCode == (int)LispDataType.Nil ? "nil" : tv.Value)) {}
        }
    }
    Haven't tested it yet, will need to do so later today.

    Actually the vl-exit-with-error doesn't seem to have any influence on Lisp's error itself - though it does close the defun at that point. Will have to look at what can be dome otherwise.
    Knowledge is proportional to experience, but wisdom is inversely proportional to ego!
    My little bit of "wisdom": Hind-sight is useless, unless used to improve the next forethought!

  4. #14
    Certifiable AUGI Addict irneb's Avatar
    Join Date
    2015-11
    Location
    Jo'burg SA
    Posts
    4,512
    Login to Give a bone
    0

    Default Re: Error Handling in LISP/Net

    Other than that, the only things looking promising is using P/Invoke on some of acad's loaded DLLs:
    • void __cdecl error_output(char * __ptr64) in VL.CRX
    • void __cdecl AcEdPromptSelection::InputCallbackArgs::setErrorMessage(class AcString const & __ptr64) __ptr64 in accore.dll
    Knowledge is proportional to experience, but wisdom is inversely proportional to ego!
    My little bit of "wisdom": Hind-sight is useless, unless used to improve the next forethought!

  5. #15
    Certifiable AUGI Addict irneb's Avatar
    Join Date
    2015-11
    Location
    Jo'burg SA
    Posts
    4,512
    Login to Give a bone
    0

    Default Re: Error Handling in LISP/Net

    Quote Originally Posted by irneb View Post
    Actually the vl-exit-with-error doesn't seem to have any influence on Lisp's error itself - though it does close the defun at that point. Will have to look at what can be dome otherwise.
    As I thought, this doesn't work. The vl-exit-with-error function only operates on separate namespaces. Will have to try and see what I can do with the other 2 p/invoke functions. The vl.crx one would necessitate VLisp to be loaded through vl-load-com, and the one from accore.dll is a bit strange - I've never used a class method through p/invoke before, but willing to give it a try.

    Edit: BTW, I used Dll Export Viewer to find those other 2. There's a whole plethora of functions with error in their name, but these 2 seemed as if they might be doing what I'm after.
    Knowledge is proportional to experience, but wisdom is inversely proportional to ego!
    My little bit of "wisdom": Hind-sight is useless, unless used to improve the next forethought!

  6. #16
    Past Vice President / AUGI Volunteer peter's Avatar
    Join Date
    2000-09
    Location
    Honolulu Hawaii
    Posts
    1,029
    Login to Give a bone
    0

    Default Re: Error Handling in LISP/Net

    I have played around with pinvoke before.

    Successfully created new ones.

    Thanks for your work on the error handling...

    I also want to continue our discussion of common lisp...

    Peter
    AutomateCAD

  7. #17
    Certifiable AUGI Addict irneb's Avatar
    Join Date
    2015-11
    Location
    Jo'burg SA
    Posts
    4,512
    Login to Give a bone
    0

    Default Re: Error Handling in LISP/Net

    Neither of those 2 seemed to work at all. But I didn't give up - seems there's a fail function exported in VL.CRX which accepts a char-array as a string. It seems to do the job (half-way) when used in ACad Vanilla 2014:

    The code to import the fail function. Note I'm exposing it in my LispEnvironment class (also contains get/set/invoke):
    Code:
    [System.Security.SuppressUnmanagedCodeSecurity]
            [DllImport("vl.crx", EntryPoint = "?fail@@YAXPEAD@Z",
                       CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
            extern static private void fail([In, MarshalAs(UnmanagedType.LPStr)] string message);
    Then the exception class, as previous, except for the SendLispError method:
    Code:
            /// <summary>
            /// Pass the exception to the Lisp Environment instead of stack-tracing the DotNet assembly
            /// </summary>
            /// <returns>Always null</returns>
            public object SendLispError() {
            	try { LispEnvironment.Fail(this.Message); } catch { }
            	return null;
            }
    And then lastly an example use:
    Code:
    namespace ALispDotNetLink
    {
    	/// <summary>
    	/// Description of TestError.
    	/// </summary>
    	public class TestError
    	{
    		[LispFunction ("err-vl")]
    		public object errVL(ResultBuffer arguments) {
    			try {
    				if (arguments == null) 
    					throw new LispArgumentCountException(false);
    				TypedValue[] args = arguments.AsArray();
    				if (args.Length < 1)
    					throw new LispArgumentCountException(false);
    				if (args[0].TypeCode != (int)LispDataType.Text)
    					throw new LispArgumentException("stringp", args[0]);
    				return args[0];
    			} catch (LispException e) {
    				return e.SendLispError();
    			}
    		}
    	}
    }
    It's not exactly "perfect" but it does work the way I wanted it - though there are some extra stuff printed to the command-line:
    Code:
    Command: (defun test (toEval / *error*) (defun *error* (msg) (princ "This is my error: ") (princ msg) (princ)) (princ "Result: ") (princ (eval toEval)) (princ "\n\nNo errors!!!") (princ))
    TEST
    Command: (test '(/ 1 0))
    Result: This is my error: divide by zero
    Command: (test '(/ 5 2))
    Result: 2
    No errors!!!
    Command: netload
    Command: (test '(err-vl "testing"))
    Result: testing
    No errors!!!
    Command: (test '(err-vl 1))
    Result: This is my error: invalid argument type: stringp: 1nil
    No errors!!!
    "\n;;; CNTX trace stack warn\n""\n;;; CNTX trace stack warn\n"
    Command: (test '(err-vl 2 3))
    Result: This is my error: too many argumentsnil
    No errors!!!
    ; error: Exception occurred: 0xC0000005 (Access Violation)
    ; warning: unwind skipped on exception
    ; error: Exception occurred: 0xC0000005 (Access Violation)
    "\n;;; CNTX trace stack warn\n"
    So for some strange reason, the *error* function does get called, but the currently running function isn't stopped. And then on the 2nd such error the entire thing fails - Access violation.

    But then after waiting a bit it then seems to work - sort-of (not even displaying that CNTX message):
    Code:
    Command: (test '(err-vl "testing again"))
    Result: testing again
    No errors!!!
    Command: (test '(err-vl 1))
    Result: This is my error: invalid argument type: stringp: 1nil
    No errors!!!
    Command: (test '(err-vl 1 2))
    Result: This is my error: too many argumentsnil
    No errors!!!
    Command: (test '(err-vl nil))
    Result: This is my error: invalid argument type: stringp: nilnil
    No errors!!!
    Command: (test '(err-vl 1 2))
    Result: This is my error: too many argumentsnil
    No errors!!!
    Command: (test '(err-vl nil))
    Result: This is my error: invalid argument type: stringp: nilnil
    No errors!!!
    It seems the fail is simply calling the *error* defun with the message, but doesn't actually reset the lisp environment. I'll have to research a bit more, but I think I'm getting close now.
    Knowledge is proportional to experience, but wisdom is inversely proportional to ego!
    My little bit of "wisdom": Hind-sight is useless, unless used to improve the next forethought!

  8. #18
    Certifiable AUGI Addict irneb's Avatar
    Join Date
    2015-11
    Location
    Jo'burg SA
    Posts
    4,512
    Login to Give a bone
    0

    Default Re: Error Handling in LISP/Net

    I've just thought of another way of doing this. Similar to returning nil on errors, but allowing for nil as an accepted return value (as it should be, since it could mean a false or not-found or empty - which aren't necessarily "errors").

    My idea was to use some "special" value which cannot be used anywhere else. I was thinking (a while ago) to make some temporary ename and pass that as an ObjectId so it could be checked in Lisp - similar to vl-catch-all-error-p does. However, the method started to become too cumbersome - not to mention it needed to be implemented in ARX (not DotNet).

    So now, I've though: "Is there any possible value which can be returned from a LispFunction, which Lisp cannot possibly use in any "normal" situation?" And it seems there is: double.NaN (i.e. a double value which refers to an error condition "Not-a-Number"). Here's the test function
    Code:
            [LispFunction ("test-NAN")]
            public static object testNAN(ResultBuffer arguments) {
                return double.NaN;
            }
    And when run in acad:
    Code:
    Command: (test-NAN)
    -1.#IND
    Then to make an is-error function I used this:
    Code:
            [LispFunction ("test-input")]
            public static object testInput(ResultBuffer arguments) {
                Document activeDocument =
                    Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
                Editor activeEditor = activeDocument.Editor;
                try {
                    if (arguments == null) throw new ArgumentNullException();
                    TypedValue[] args = arguments.AsArray();
                    if (args.Length < 1) throw new ArgumentException("No arguments passed in");
                    activeEditor.WriteMessage(string.Format("{0} arguments passed in\n", args.Length));
                    foreach (TypedValue element in args) {
                        activeEditor.WriteMessage(string.Format("\t{0}\t{1}\n", 
                                                                LispDataType.GetName(typeof(LispDataType), element.TypeCode),
                                                                element.Value));
                    }
                    return true;
                } catch (System.Exception e) {
                    activeEditor.WriteMessage(e.Message);
                    return null;
                }
            }
    And then ran it inside acad:
    Code:
    Command: (test-input (test-NAN))
    1 arguments passed in
     Double NaN
    T
    So it would be possible to test for such from Lisp.

    But even easier - it would be possible to test for a NaN value direct inside Lisp. See these tests in acad:
    Code:
    Command: (= (test-NAN) 0)
    nil
    
    Command: (<= (test-NAN) 0)
    T
    
    Command: (>= (test-NAN) 0)
    T
    
    Command: (and (>= (test-NAN) 0) (<= (test-NAN) 0) (/= (test-NAN) 0))
    T
    Notice the last item makes no logical sense? So the "is-error" function could simply be something like this:
    Code:
    (defun is-error (value)
      (and (>= value 0) (<= value 0) (/= value 0)))
    Thereafter it would simply require that the DotNet needs to set the ErrNo and/or somewhere place the error message so it can be inspected from the Lisp side. And as optional extra the normal *error* can then be called as per my previous posts.
    Knowledge is proportional to experience, but wisdom is inversely proportional to ego!
    My little bit of "wisdom": Hind-sight is useless, unless used to improve the next forethought!

  9. #19
    Past Vice President / AUGI Volunteer peter's Avatar
    Join Date
    2000-09
    Location
    Honolulu Hawaii
    Posts
    1,029
    Login to Give a bone
    0

    Default Re: Error Handling in LISP/Net

    I wanted to demonstrate a style of writing LISP that utilizes the 'and' expression.

    It is why I like to have my expressions return a value or T for success and nil for failure.

    To demonstrate.

    Someone asked me yesterday how to explode all blocks in a block definition.

    I has an old function using is if statements.

    Code:
    ;___________________________________________________________________________________________________________
    ;
    ; Function to explode all components in a block definition ;___________________________________________________________________________________________________________
    
    (defun ExplodeItemsInBlock (objBlockDefinition / intCount objLast blnRerun)  
     (setq intCount (vla-get-count objBlockDefinition))  
     (while (not blnReRun)
      (setq blnRerun T
            intCount 0
      )    
      (while (< intCount (vla-get-count objBlockDefinition))         
       (setq objLast (vla-item objBlockDefinition intCount))
       (if (and (wcmatch (vla-get-objectname objLast) "AcDbBlockReference,AcDbMInsertBlock")            
                (errortrap '(vla-update objLast))
           )
        (progn
         (errortrap '(vla-update objLast))
         (if (errortrap '(vla-explode objLast))
          (progn
           (errortrap '(vla-delete objLast))
           (setq blnRerun nil intCount (vla-get-count objBlockDefinition))
          )
         )
        )
       )
       (setq intCount (1+ intCount))
      )
     )
    )
    
    ; Standard error trap that returns nil for error or a value or T for success.
    
    (defun ErrorTrap (symFunction / objError result XYZ123)  
     (if (vl-catch-all-error-p
          (setq objError (vl-catch-all-apply
                         '(lambda (XYZ123)(set XYZ123 (eval symFunction)))
                          (list 'result))))
      nil
      (if result result 'T)
     )
    )
    Now this is a rewrite of the code above using the 'and' syntax.

    Code:
    ;___________________________________________________________________________________________________________
    ;
    ; Function to explode all components in a block definition rewritten to use 'and' syntax ;___________________________________________________________________________________________________________
    
    (defun ExplodeItemsInBlock (objBlockDefinition / intCount objLast blnRerun)  
     (setq intCount 0)    
     (while (< intCount (vla-get-count objBlockDefinition))         
      (and (setq objLast (vla-item objBlockDefinition intCount))
           (wcmatch (vla-get-objectname objLast) "AcDbBlockReference,AcDbMInsertBlock")          
           (errortrap '(vla-update  objLast))
           (errortrap '(vla-explode objLast))
           (errortrap '(vla-delete  objLast))
           (ExplodeItemsInBlock objBlockDefinition)
           (setq intCount (vla-get-count objBlockDefinition))
      )
      (setq intCount (1+ intCount))
     )
     (princ)
    )
    Which one do you prefer?

    P=
    AutomateCAD

  10. #20
    Certifiable AUGI Addict irneb's Avatar
    Join Date
    2015-11
    Location
    Jo'burg SA
    Posts
    4,512
    Login to Give a bone
    0

    Default Re: Error Handling in LISP/Net

    The last one definitely looks cleaner.
    The trouble I have with only restricting yourself to nil/T as return value is that then you cannot make your DotNet LispFunction actually return useful data - you'll need to set some global variable if you want to return any data back to Lisp. For that reason I'd then want a error token instead of simply a "yes it succeeded" or "no something went wrong" value.

    That way, you can still use it as per your example - your error trap would simply return nil if the function's return value is the token error. E.g.
    Code:
    (defun is-error (value)
      (and (= (type value) 'REAL) (>= value 0) (<= value 0) (/= value 0)))
    
    (defun errortrap (toEval / result)
      (not (or (vl-catch-all-error-p (setq result (vl-catch-all-apply 'eval (list toEval))))
               (is-error result))))
    Last edited by irneb; 2014-02-26 at 11:58 AM.
    Knowledge is proportional to experience, but wisdom is inversely proportional to ego!
    My little bit of "wisdom": Hind-sight is useless, unless used to improve the next forethought!

Page 2 of 3 FirstFirst 123 LastLast

Similar Threads

  1. Error Handling
    By ticad02 in forum AutoLISP
    Replies: 16
    Last Post: 2009-12-21, 03:39 PM
  2. Error Handling
    By whattaz13 in forum AutoLISP
    Replies: 2
    Last Post: 2008-07-16, 01:03 PM
  3. Error handling
    By dfuehrer in forum AutoLISP
    Replies: 8
    Last Post: 2007-09-24, 04:17 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •