PDA

View Full Version : Handling Exceptions



peter
2014-02-17, 03:54 PM
This thread was started discussing removal of xdata but focused on the handling of exceptions, so I moved it to its own thread.

BlackBox
2014-02-17, 07:07 PM
I am sure that many things are possible using old fashioned autolisp... that is not my point...


Peter, my friend -

Words have meaning... These statements, and those made above, are mutually exclusive.

We both share an appreciation for, and interest in extending, LISP. So let's do exactly that by putting out the most accurate information, and quality examples we can.

I know you're very passionate about this topic, and I want for this initiative to be successful as well... Please do not take my comments & corrections personally, as they're intended to help the reader.





The focus of this forum is to extend the power of LISP creating useful functions in .NET...


Equally important is not letting new developers incorrectly believe that they don't need to account for exception handling in their LispFunction Methods, as I once (stubbornly) believed, and Gile was kind enough to educate me (I owe him a few bottles of Champagne for all that he's taught me :beer:)... Properly validating both the number, and type of parameters supplies with a given LispFunction Method's ResultBuffer is essential.

An excerpt from one of my many, many learning moments in .NET development (in this instance, specifically LispFunction Method) best practices:



...

An exception will be thrown if there's more or less than one argument or if a non-number is passed to 2x.
One reason this is not mandatory in .NET is that you can define a single LISP functions with a variable number and type of arguments (which cannot be done in LISP) and this requires a robust arguments checking.

...

Returning nil (null in .NET) rather than throwing an exception may be "preference" in some cases, but IMO only if nil (null) is a valid return value type, on no account for a number (this won't be possible in .NET where numbers aren't nullable).

...

When we implement LISP functions (or classes, frameworks, ...) to be used by other programmers, we have to make them as robust and safe, and also as comfortable to use as possible.
IMO it's no so comfotable to have to systematically check if the return value of a function is nil.



Separately, have you considered implementing a 'using' statement in your Transaction call? Why opt for a Document Transaction, when the entity data is Database resident?

Cheers

BlackBox
2014-02-17, 07:13 PM
Just to be complete, I recoded the above example to use the entmod method mentioned by blackbox for you LISP purists.

I had been monitoring Keans site and he was discussing removal of xdata and so I thought I would expose that functionality
to lisp using .net. That is why I tried to code it using .net. I haven't really studied removal of xdata using lisp or .net until today.

I was just playing around solving a programming puzzle and I solved it using .net and with blackbox's suggestion created the pure lisp version too.

I wanted to have a function that would remove xdata from a selection set of objects with a wildcard application name filter.

I like CAB's solution too... Its all good.


This post must have come in while I was typing....

I appreciate the motivation, and strongly encourage you to continue exploring how to extend LISP. You're very talented, and creative. As a wiser man than I once said, 'I prefer clarity to agreement'. I'd like to believe that we have more in common than not.

Cheers

peter
2014-02-17, 07:17 PM
It is my opinion that most errors can be handled by the programmer.

In the style I prefer, I choose to make sure the arguments are the correct type.

Rather than throw an exception for a number instead of a string, I just use the tostring and convert whatever it is to a string.

I also use case statements that ignore extra arguments...
and have been working on a general solution for converting typedvalue result buffers to arguments and back.

I also like to allow the user to have different numbers of arguments, including 0 if possible.

I also manage the errors by checking success at the end of a function by returning nothing or nil for failure and a value, true, or a T_Atom at the end for success.

I handle the exception in the routine rather than have a lot of error statements mess up the command prompt.

I recognize that you prefer to have exception feedback and I think that is a valid way of doing it too.

I do think for demonstrating basic functionality, adding exception handling is more complicated than handling the errors in the flow.

So for simple examples I prefer to not include them.

In your posts I have no problem with you demonstrating them and including them in your examples... It is just another way of doing it.

peter
2014-02-17, 07:29 PM
If you look at my lisp code I like to use the 'and' statement to handle errors




(if (and
(function1 "test")
(setq xyz (function2 "test"))
(errortrap (quote (/ 1 0)))
etc...
)
(princ "\nSuccess!!!")
(princ "\nOops something didn't work - handle the error")
)



This style which I prefer... it immediately drops out if an error is detected, but it doesn't crash the program.

I hope you understand the reasoning behind the 'nil' return value now.

I just continue on in my loop or whatever.

I am always contemplating what it means if I encounter an error and what it will do to the results of the routine.

p=

peter
2014-02-18, 04:24 PM
I would like to explore ideas that would move the handling of exceptions out of base functions into an encapsulated function that could be called from the base function in a single function call.

Exceptions do provide useful information to the programmer, but putting all of the exception functionality in all of your base functions is redundant and IMHO clouds the simplicity of a base function.

I have thought of using xml to transfer all of the information to the error exception handler.

Like if I pass the arguments, required data types, and default values to the exception handler, it should be able to check all of them and create the error messages if required and return the data values back to the base function without messing up the code.

That way a single function could handle all of the argument exceptions.

I would be interested in looking at anyone elses code that has tried to do that.

p=

BlackBox
2014-02-18, 10:22 PM
It seems that the original, incorrect statements made on the inability to remove XData with vanilla LISP (which prompted me to participate in the first place) have not been included here, when moving these posts to a new thread... Please restore those as well for clarity / context.

peter
2014-02-19, 12:49 AM
I left the comments on the xdata post in the thread with the xdata and split the thread on exceptions to here.

They seemed to be two different subjects and this subject justified its own thread.

p=

peter
2014-02-19, 01:18 AM
IMHO

I write code in a work environment while working on drafting projects, and use them immediately.

I am sure since 1986 I have coded way more than 5000 functions for AutoCAD.

These are my priorities when programming for AutoCAD or anything else.

I have always coded while I am drafting real jobs not test drawings as a part of my work.

1.) Protect the Drawing Database... Crashes can corrupt databases and loose valuable time restoring them, avoiding crashes is absolutely imperative.

2.) It follows the I protect the routine. Crashes cost time and time is money. If I can make it so a routine is almost bullet proof to crashing, I am saving time.

3.) Exception handling is not necessary for a function to work, unless a function is used incorrectly, and since I code them and use them immediately I rarely need exception handling for new functions.

4.) For toolbox functions that are used regularly and are used after the time of creation, I add exception handling.

5.) The value of a function is based on a simple calculation "time of development" + "sum or runtimes" < "time to manually accomplishing same work"

6.) Most of my code takes me 10 minutes to code and use.

7.) I am practicing my vb.net programming to get it down to 10 minutes... it is around 20 minutes per function now.

8.) I find adding exception handling doubles the amount of time invested in a routine...
I am always calculating the time and money to see if it is worth adding.

9.) Exception handling also makes the routine more complicated, so when making simple examples,
I leave it out because if used correctly the function will work without it.

10.) I also protect the function by ignoring extra arguments, converting arguments
to string (when strings are required), etc...

This kind of development is different than the kind where programmers are paid
to develop full time and run on a different set of priorities.

If you examine your priorities, you may find they are different than mine, and
that may make exception handling more of a priority for you.

I am familiar with gile, kerry and tt and they all have different styles and
priorities that affect their programming style.

I think you would have difficulty getting any group of programmers to agree
on anything, let alone mix Microsoft in on the discussion.

If you think my code is lacking because of the simplicity in it,
feel free to modify and re-post in your preferred style...

It doesn't hurt my feelings.

There is no one right way of doing this... Do what works for you.

P=

BlackBox
2014-02-19, 01:31 PM
I left the comments on the xdata post in the thread with the xdata and split the thread on exceptions to here.

They seemed to be two different subjects and this subject justified its own thread.

p=

Found it (http://forums.augi.com/showthread.php?154689-Delete-XData-with-a-specified-application-name-from-a-selected-entity&p=1260131&viewfull=1#post1260131), thanks. :wink:

peter
2014-02-19, 07:05 PM
One of my basic coding rules is a function should never be longer than a page.

Each function should have a description of what it does and an example of its syntax.

As I mentioned before I think that a function or series of functions could be developed and placed into its own class that would handle all of the exceptions for arguments.

I would be interested in discussing this and coming up with a class for handling that.

That way the handling of the exceptions would not cloud the example, and the exception codes would be generated, and it would be easy to add exception handling to any routine.

Function specification

As I see it, the array of arguments, an array holding the data types for each argument, array of optional (default values) and a flag that would tell the routine to display the error messages to the command line in AutoCAD, the console in the development environment, record them into a file, or do not display them at all also maybe a protection mode that helps the data types to strings etc...

I could see the function returning an array of arguments or (nothing) before moving on through the routine.

Also one of friends at AU (Scott McFarlane) in his lecture emphasized the need to not mix AutoCAD dependent functions with other generic functions.

I interpreted that and applied that to LISP functions and pure .NET functions.

I prefer to make the LISPMethod function call the pure .NET function.

Do you have any suggestions or opinions on these ideas?



DO you have any robust exception handling functions?

irneb
2014-02-20, 09:15 AM
I was thinking of something similar. I.e. a class working in the opposite direction as the ObjectToTypedValue class in the other thread (http://forums.augi.com/showthread.php?154713-NET-Return-Conversion-to-TypedValue). This should convert the result buffer into its object(s) - perhaps some list/array.

While it's at it, it should also check to see if the correct types are found in the resbuf as was expected. Unfortunately this last aspect is very complex to achieve (especially for a general purpose class). There may be infinite combination possibilities, e.g. some argument might be allowed to be more than one type (e.g. int and real). Another argument might be allowed to be omitted. You'd need some way of telling this ResBuf->ObjectList class just what you're expecting so it can check the input against those rules.

BlackBox
2014-02-20, 02:29 PM
I'm really struggling to understand this; I'd be remiss if I didn't also point out the irony here.

Some have all but balked at the suggestion that basic Exception handling be implemented in the LispFunction Methods being offered, yet somehow a new, indeterminably complex Class structure to handle each-and-every-single-possible Exception potentiality (which would most assuredly be longer than one page, of unknown font size, font style, and page margins, etc.) is worthwhile? http://www.cadtutor.net/forum/images/smilies/rofl.gif

Would it not be preferable to simply implement basic code logic to throw one or more basic *LispExceptions (i.e., TooFewArgs, TooManyArgs, ArgumentType, etc.), in addition to any custom *LispException Types that derive from same, in order to scrub a given LispFunction Method's ResultBuffer for the required or allowed argument(s)? http://www.cadtutor.net/forum/images/smilies/unsure.gif

Never mind the fact that even if such an all-inclusive Exception handling Class existed, you'd still need sufficient code logic to feed this Type's constructors the appropriate parameters (where you'd normally just throw the aforementioned *LispExceptions).

irneb
2014-02-20, 04:04 PM
The principle is to get rid of as much noise as possible. E.g. say you create a LispFunction which should take a number (int/double) and a string. You spend most of that function on converting & testing the arguments (if you're using exceptions or not). E.g.
[LispFunction ("myfunction1")]
public object myfunction1(ResultBuffer arguments) {
try {
TypedValue[] args = arguments.AsArray();
if (args.Length != 2) throw new LispArgumentCountException(2, args.Length);
if (!((args[0].TypeCode == (int)LispDataType.Int16) ||
(args[0].TypeCode == (int)LispDataType.Int32) ||
(args[0].TypeCode == (int)LispDataType.Double)))
throw new LispArgumentException("Expecting int/double", args[0]);
if (args[1].TypeCode != (int)LispDataType.Text)
throw new LispArgumentException("Expecting string", args[1]);
double myNumber = (double)args[0].Value; // Get the actual value of the 1st argument
string myText = (string)args[1].Value; // Get the actual value of the 2nd argument
// Do some actual stuff
return "success";
} catch (LispException e) {
return e.Message;
}
}
The principle would be to have a handler class which would perform the tests and convert the TypedValues for you. Then you'd use it like this:
[LispFunction ("myfunction2")]
public object myfunction2(ResultBuffer arguments) {
try {
LispFunctionArgumentHandler args = new LispFunctionArgumentHandler
(arguments, new string[]{"Int16|Int32|Double", "Text"});
double myNumber = (double)args[0]; // Get the actual value of the 1st argument
string myText = (string)args[1]; // Get the actual value of the 2nd argument
// Do some actual stuff
return "success";
} catch (LispException e) {
return e.Message;
}
}See how the type checking is reduced to a single instruction? What would be nice is if such handler would use something like generics to do the conversion as well - thus the get items would be something like this:
double myNumber = args[0]; // Get the actual value of the 1st argument
string myText = args[1]; // Get the actual value of the 2nd argument
Or I could do something using argument overloads, but I think the use thereof would be less succinct:
double myNumber;
args.Get(0, out myNumber);
string myText;
args.Get(1, out myText);

BlackBox
2014-02-20, 07:30 PM
Please correct me where I'm wrong....

I'm understanding this discussion to have implicitly identified two entirely separate aspects, my zef-slang brotha' (who's having a birthday ehhhehehehehe).

The first, implementing a Class to what I'd call 'scrub' the ResultBuffer for allowed arguments, both in number, and type(s).

The second, implementing a Class to catch/handle all exceptions thereafter in the main code, in order to avoid empty catch statements.

The former is quite simple in concept, just will require myriad constructor overloads in order to support each possible ResultBuffer configuration (we'd have to add them as we go, etc.). The latter however, either isn't possible via exposed API features, or I'm utterly unaware of how to go about it (which is entirely possible). Does this make (more?) sense, or am I really just off-base here?

Cheers

peter
2014-02-21, 04:16 AM
In my view the first class (mentioned above) is relatively easy.

The arguments I see as necessary include...

1.) Array of typed values, Or a result buffer that is immediately converted to an array of typedvalues.

2.) Array of lispdatatypes for each argument specified, although some might accept multiple typed values like Double, int32 and int16. (see data conversion comment below)

3.) Array of typedvalues as default values. Those arguments that do not have default values would be indicated with a nothing.

4.) Bit Flags that instruct
0 ) The function to not do any correction
1 ) Instruct routine to try to convert argument to correct lispdatatype
2 ) Type error messages to command line in AutoCAD
4 ) Type error messages to console in development environment
8 ) etc...

The function would return an array of typedvalues that include the default values, or return nothing (after reporting the error)

As far as the second item, in lisp it is possible to capture the expression that causes an error. This is because it can be compiled during runtime (eval).

In conversations with Kean a couple years ago, he mentioned that compile at runtime (the evaluate expression) would be reintroduced in .net 5.0 after being removed in vb6 to vb.net conversion.

With its reintroduction it would be possible to capture the expression and the error message and report it.
It may be this second function might not be able to be coded until then.
As I see the second function it would need to report the error, the offending expression and continue on with the routine.

Or something like that.

In conversations with a friend who is a professional programmer using Pearl, datatypes are very forgiving, allowing you to use "0" and 0 and the program knows that you are referring to 0.

Conversion of datatypes to expected values has precedence in other languages, so enabling functions to convert data to strings,(if a string is required) isn't that different.

irneb
2014-02-21, 06:16 AM
In conversations with a friend who is a professional programmer using Pearl, datatypes are very forgiving, allowing you to use "0" and 0 and the program knows that you are referring to 0.Yes, well - Perl is known (similar to PHP) for it's even weaker typing than Lisp.

BTW, you can add some implied casting operators to give C# the ability to convert to/from strings into ints/reals quite easily. http://msdn.microsoft.com/en-us/library/z5z9kes2.aspx
Sorry, never tried this in VB before - might probably be possible there too. E.g. here's a to-and-from double/string:
public static implicit operator double(string text) {
double result;
return double.TryParse(text, out result) ? result : double.NaN;
}

public static implicit operator string(double number) {
return string.Format("{0:R}", number);
}
If that forms part of any public class you've loaded / referenced then the following would work:
double number = "1234.5678";
string text = 1234.5678;

BlackBox
2014-02-21, 01:23 PM
Grrr... I could have sworn that Tony posted a Class which specifically converted ResultBuffer to List<TypedValue> (using generics?), and then back again, etc., which I cannot seem to locate at the moment.

peter
2014-02-21, 08:45 PM
Yes, well - Perl is known (similar to PHP) for it's even weaker typing than Lisp.

It is funny that the innuendo on the word 'weaker' implies less than.

In my frame of reference protection of the routine/database is my number one imperative, so making arguments bullet proof would be stronger not weaker.

IMHO, and much to be desired not avoided.

I know many programmers who think everything is speed at run time, but in the production drafting world, speed for development usually governs.

Peter

peter
2014-02-21, 08:50 PM
Also I have been playing with a new routine, but I need to return a list of sublists ( as a result buffer).

I have been playing around with Lists, Arrays, Structures, but nothing has the versatility of a LISP List.

Suggestions or examples?

I want to be able to manipulate one of the above data types in .net and convert it to a result buffer as a return value.

I want each cell in the 2d array to contain any of the object types.

P=

BlackBox
2014-02-21, 09:01 PM
Also I have been playing with a new routine, but I need to return a list of sublists ( as a result buffer).

I have been playing around with Lists, Arrays, Structures, but nothing has the versatility of a LISP List.

Suggestions or examples?

I want to be able to manipulate one of the above data types in .net and convert it to a result buffer as a return value.

I want each cell in the 2d array to contain any of the object types.


I believe this is what you're after:




Returning nested list from .NET to LISP (http://adndevblog.typepad.com/autocad/2012/05/returning-nested-list-from-net-to-lisp.html)

...

You can find the ResultBuffer TypedValue related codes in the LispDataType enum. As you can see RTRESBUF/5023 is not in the list. You could place the nested part in a LispDataType.ListBegin/LispDataType.ListEnd section instead:



[LispFunction("GetNestedList")]
public static ResultBuffer GetNestedList(ResultBuffer resBufIn)
{
ResultBuffer resBufOut = new ResultBuffer();

resBufOut.Add(
new TypedValue((int)LispDataType.Text, "Main List Item 1"));

resBufOut.Add(new TypedValue((int)LispDataType.ListBegin));
resBufOut.Add(
new TypedValue((int)LispDataType.Text, "Nested List Item 1"));
resBufOut.Add(
new TypedValue((int)LispDataType.Text, "Nested List Item 2"));
resBufOut.Add(new TypedValue((int)LispDataType.ListEnd));

resBufOut.Add(
new TypedValue((int)LispDataType.Text, "Main List Item 2"));

return resBufOut;
}

peter
2014-02-21, 09:23 PM
What I was looking for is this


'(("ID" "Name" "Number")(1 "First" 1.0)(2 "Second" 2.0)(3 "Third" 3.0))


I know how to do this as a result buffer (similar to your example)
but I am looking for a compatible .net object other than a result buffer to hold it.

This is the list of sub-lists that I was referring to.

Most all of my data handling functions (in lisp) accept or return this kind of list.

Like (csvfiletolist strCSVFullName strDelimiter) would read a csv ascii text file and return a list of sub-lists parsed with the strDelimiter

BlackBox
2014-02-21, 09:34 PM
Oh okay... I only know of a couple of ways to do something like that; either use ResultBuffer (even if not being passed back to LISP), implement a custom FooCollection Type made up of one or more Foo Type, or use XML, etc.

Not sure if that helps really, but it's the best I have at the end of this very long, but awesome week.

Happy Friday! :beer:

peter
2014-02-21, 09:49 PM
I would be very interested in seeing a result buffer to XML function.

Or coding one.

irneb
2014-02-22, 05:47 AM
It is funny that the innuendo on the word 'weaker' implies less than.;)Sorry, that "weaker" is simply the official naming. Since the 60s there's been debates raging on which is better: weak/strong typing. The C-like languages (actualy a misnomer, it should rather be the Fortran-like languages: Ada/Basic/Pascal/C/C++/Java/C#/VB/etc.) tend to use strong typing (a few grey areas but only in special cases). If it's better depends on situation: if you need to type-check a lot, then having the compiler do it for you is obviously more efficient. If you want to mix types in a collection, then weak typing makes it a lot easier to work with.

My view of this is weak leaves types up to the programmer to manage. Strong restricts the programmer into what types to use where. There can be good and bad points on both sides.


I have been playing around with Lists, Arrays, Structures, but nothing has the versatility of a LISP List.That's exactly true. One of the difficult portions to accomplish using C#/VB.Net. The only true way to get around this is to use object inheritance in a strong typed language. If you want the closest matching structure to a Lisp-list from the default DotNet libs, then you might use something like:
System.Collections.Generic.LinkedList<object>But then due to the strong typing you have to check what actual type each of those items are and then type-cast accordingly. This is where strong typing makes the programmer's life difficult.

Lately the var type's been added to C#, but restricted to only be usable inside a method as a local variable - you can't use it as a type for a datastructure.

If you really want to use a Lisp-like list, then you could use IronScheme's implementation of the Cons type: https://github.com/leppie/IronScheme/blob/master/IronScheme/IronScheme/Runtime/Cons.cs But IMO it's not too much different to use than a LinkedList of objects, in fact it IS a linked list of objects, just implemented using CAR/CDR.

Even IronPython uses strong typing too (as does IronRuby), though they use dynamic typing (a sub-set of weak typing, unlike C#/VB/F#, but a lot like Lisp does) - i.e. a variable can contain anything, but it doesn't adjust to accommodate the use of a specific type inherently. I.e. the language itself doesn't add implied type casts for everything like Pearl/PHP/JavaScript does. Even Lisp is not fully weakly typed, e.g. you can't add a string containing numerals to an int and expect a result containing either a concatenated string or an int containing a total. That's actually why weak typing isn't fully implemented (in most cases) - there are usually ambiguity when 2 different types are combined, so either the language makes a call on which will always be used (like Pearl/PHP/JS does) or the language disallows such and makes the programmer specifically cast/parse to accommodate (like Lisp/Python/Ruby does). E.g. say you wanted to rather just append the integer's digits to the end of a string in Pearl instead of using its default of converting the string to an int and summing the 2 then returning a new int ... how would you do that?

In C# you can make an implied casting operator between an object and a list of objects (similar to the example I showed before between doubles and strings). You'd probably do one to an IEnumerable/ICollection type though after checking if the object actually contains such - else "throw an exception" (which is exactly what Lisp does, try to run nth on an atom value and see what happens).

irneb
2014-02-22, 06:07 AM
I would be very interested in seeing a result buffer to XML function.

Or coding one.IMO XML is nothing but a convoluted and restricted version of a Symbolic Expression.

http://c2.com/cgi/wiki?XmlIsaPoorCopyOfEssExpressions
http://rwmj.wordpress.com/2009/10/30/xml-or-s-expressions/

So you could say XML is the strong typed version of S-Expr. What I'd actually like to see is something like that Lisp-list being Serializable - then making a XML from that would be a very simple task, as would reconstructing the list from the XML.

kavitamarne34784000
2019-07-26, 10:22 AM
Excellent thread! Thank you!