Your not guilty of anything ! Returning arrays of data is great stuff. Very usefull in fact - one can use them as collections in FOR EACH IN statements and count them with UBOUND.

Returning other things like extended error codes along with the data, i think, is not a good idea. In fact, it would make the UDF in-flexible in that it couldn't [easily] be plugged into collection-aware contructs.

Should error codes extend functionality beyond that of basic error processing? How extensively should error trapping be used?

I've seen many pieces of code that almost exclusively use error codes to make branch decisions. This code can be very cumbersome to understand or debug unless the error codes are well known and standardized or labelled well throughout the whole program/function/macro/etc. This problem seems to be more frequent when the same variable or macro is used to return the error code each time, like @error.

This may be a bit off-topic, and perhaps displays my inexperience at programming, but wouldn't it be nice to have each error returned in a variable like:

Function: SayHelloErrorDescription: $error.a.SayHello.bError: error.a.SayHello.b{a} would be the line number or some other identification internal if it's a UDF.{b) would be the line number of the function call. Obviously, "a" wouldn't be present for standard functions.

Perhaps this is a little too complex for what KIXtart is meant for, but most good error trapping systems give standard error codes AND line numbers.

Just something for everyone to chew on... If error trapping was like that, would it be easier or harder to use? It could still be evaluated just like @error. Would it make code simpler or more complex?

Function: SayHelloErrorDescription: $error.a.SayHello.bError: error.a.SayHello.b{a} would be the line number or some other identification internal if it's a UDF.{b) would be the line number of the function call. Obviously, "a" wouldn't be present for standard functions.

That doesn't make sense. lolThis is better:

Function: SayHelloErrorDescription: $Error.SayHelloError: Error.SayHelloLine Number of error: LError.SayHelloLine Number of function call: FError.SayHello...

Unless Ruud sees fit to provide FileNumOpen() and / or FreeFileNum() functions, this will be an issue.

However, all is not lost. If a function needs file system access, there are ways to do this with KiX functionality like Redirect, SHELL, or with File System Objects, thus bypassing the file number limitations.

It is a pain to have to bypass KiXtart file numbers in a function, but with only ten choices, I have a pretty good chance of trying to use an already open file number and causing the function to fail.

I'll pick up a few things based on the "object" return I proposed earlier.

If there are any object aware gurus listening, please

Shoot this idea in the head if it is simply not possible

Come up with a working object to demonstrate it if it is, heh

BrianTX

quote:I'm not sure how it would be possible to take both scenarios and mesh them together in one "standard" system of error coding. Some disadvantages for doing so are: (I know I'm repeating what has been said)

1. Making simple, straightforward UDF's harder to use.

2. Lengthening code unecessarily.

The point of meshing them using the object actually addresses both of these issues. If you use the return value as you currently do it will continue to work regardless of how it was assigned in the UDF. This means that there is no change in how you use the UDF. Use it the simple way or the advanced way, both will work transparently.This of course also answers your second point. You can have the "extended" code both in your calling script and in the UDF, in just one of them, or in neither of them. There is no more code unless you specifically want to include it.

Shawn

quote:Returning other things like extended error codes along with the data, i think, is not a good idea. In fact, it would make the UDF in-flexible in that it couldn't [easily] be plugged into collection-aware contructs

Yeah, this is what started me thinking about abstracting the return value using objects. The well constructed object would accept an array as the value assignment, so:

Hey guys, I have to apologize for stirring up this hornets nest. The example that cause the origination of thread can correctly return the COM error text in @serror if I exit(@error) immediately at the point or error. Originally I was storing @error in a variable and permitted other lines to execute which reset @serror. I then went down the path of string both @error and @serror in variable to return in an array.

I do believe though that my programmatic shortcoming has opened a great discussion and potential new paths for KiXtart programming. I just wish that my initial motivation had a deeper philosophical base than it did.

Just wanted to resurect this juggernaut thread because I think its important.

Richard,

imho you are spot on in terms of how com objects should return error codes. but there is no need to write a proof-of-concept object because your ideas are already being implemented. take for example MS ADO objects. correct me if im wrong but if one does a recordset query against a database - and the SQL is bad - one will still get a recordset object returned (returns an expected object) but an error will be flagged internally. to get at the error, you query an error object that has all the extended error information.

I guess to implement something similar in Kixtart, one could declare a global $ERROR variable in ones UDF library. then for example, if a function that is supposed to return a string fails, it will still return a string, but would set @ERROR to something meaningfull. then the person using the UDF could get the extended error info either directly with the global var, or through a library return-last-error type function, like the Windows GetLastError().

And lastly to my mind, I think Mark laid-down some darn good best-pratices for UDFs. Here they are again:

1. They are pretty much unbreakable. They prevent or handle internal errors to the greatest extent possible. They still return a value of the type expected if they break -- thus helping prevent type mismatches -- but the value is zero, empty, or null. If the values supplied should never return this data, the data itself can act as the error. Otherwise, the function should set an error number on exit to be tested.

2. They are independent. All internal variables are declared so they will not be confused with global variables. (Side note -- Howard, I notice you tend to 'Dim' your argument variables as well. Unless I'm misunderstanding the process, this happens automatically simply because they are arguments.) Also, files are opened and closed in a way that they can't interfere with file numbers in the calling script.

3. They return what I intuitively expect. This way I don't have to dig through the code to see what is going on. In other words, most numeric functions returns a number. Most string functions return a string. There are obvious exceptions, of course. I would expect instr() to return a number. But I would be very surprised if it returned an array!

4. They are well documented and tested. Even better if extensive testing code is available along with expected results so I can verify those results on my particular combination of HW/OS/KiX versions before implementing the function.

5. They are readable so I can customize, if so desired. (This is strictly a personal preference. Most people probably only want to use a UDF, not tinker with it.)

I would also add some words in terms of using (what ill call) global script resources. The only one I can think of off-hand are file handles. Might propose that main scripts use file handles starting at the number 1 and working upwards. And that UDF`s use file handles starting at number 10 and work downwards.

I removed the personal comments, refined the language a little, and added Shawn's comments. If this meets general approval, maybe it can go in the FAQ area.

KiXtart UDF "Ten Suggestions"

1. A function should be pretty much unbreakable. It should prevent or gracefully handle internal errors to the greatest extent possible. An end user should never have to waste their time troubleshooting a poorly written UDF.

2. A function should still return a value of the type expected even if it cannot finish normally -- thus helping prevent type mismatches -- but the value should be zero, empty, or null. If the values supplied should never be zero, empty, or null (i.e. MyBodyTempNowCelcius() should never return a zero under normal circumstances), the data itself can act as the error. Otherwise, the function should set an error number on exit for testing from the calling script.

3. A function should be independent. All internal variables should be declared to prevent confusion / conflict with global variables. (Variables in function arguments are automatically dimensioned by KiXtart.)

4. Global resources should be used with great caution. KiXtart settings changed within the function need to be reset to their previous state before exiting the function. Files opened by a function should be opened in a way that they won't interfere with file numbers in the calling script. One possible way to handle (no pun intended) file numbers when using the KiXtart open() function is to reserve file numbers 1-5 for main script files handles and 10-6 for UDF file handles.

5. A function should return what the average user would intuitively expect. In other words, most numeric functions return a number. Most string functions return a string. There are obvious exceptions, of course. Instr() *should* return a number. But it would be odd if that function returned an array.

6. Try to make the arguments and return types of functions as intuitive as possible. Ideally, any end user should be able to copy a UDF into any script and expect it to work with no modifications and without having to dig into the code to figure out why it returns a value of more than one type or works right when MyFunc(10) is supplied, but breaks when MyFunc('10') is supplied.

7. UDF's should be well documented and tested. Known limitations of the function should be documented. For instance, it may be OK if a function like Divide(A,B) can not return a correct value when B=0, but this should be documented. A real plus is to include extensive testing code along with expected results so the end user can verify the results on their particular combination of HW/OS/KiX versions before implementing the function. Ideally, include abbreviated documentation within the function itself to briefly describe general syntax, expected arguments, returned value/type, error handling, and point of contact for questions/problems.

8. A function should be readable enough for the end user to customize it, if so desired. (This is strictly a personal preference. Most people probably only want to use a UDF, not tinker with it.)

9. Function names should be as intuitive as possible. ReadProfileString() or GetFileSize() is preferable to mydatnumfunc(). (Note the action-object-type structure of most function names.) If you are aware of another UDF with the name you prefer, be graceful and give yours a different name to avoid confusion. Be cautious with function names you anticipate may be added to KiXtart as internal functions supercede UDF functions of the same name.

10. The KiXtart community is highly libertarian in coding practices. The previous suggestions are just that -- suggestions. Please do not use these suggestions to harrass, indimidate, or bully anyone writing KiXtart code, no matter how bad that code is or how much it makes you whimper when trying to decipher it. Instead, encourage one another. Make suggestions (like these). Show someone a better or faster way to do something. We will all benefit.

I'll just add another 2 cents. Function naming conventions can go a long way in making a UDF easy and intuitive to use. This may go without saying, but sometimes its helpful to embed a variable type name within ones UDF name, for example:

And sometimes certain keywords conventions can be used as a tipoff as to what the function does and what it returns.

InGroup - the keyword "In" usually denotes that this function returns a boolean.

IsUpper - same as "In" - returns a boolean

AsLower - "As" meaning that this function transforms the input parameter or returns a variable of a different type (eg, AsNumber("3"))

GetFileSize - "Size" meaning that this function returns a number

I`ve always liked the old "action-object-type" naming convention for functions, you know, function names that start with an action (get,is,check,read) then an object name (File,String,Profile) then optionally, an attribute of that object (Size,Handle,Upper), for example: