Page 2 of 2 <12
Topic Options
#64553 - 2002-04-17 10:09 PM Re: UDF Design
Shawn Administrator Offline
Administrator
*****

Registered: 1999-08-13
Posts: 8611
Bryce,

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.

-Shawn

[im thinking Howard wishes he never started this thread [Wink] ]

[ 17 April 2002, 22:10: Message edited by: Shawn ]

Top
#64554 - 2002-04-17 10:44 PM Re: UDF Design
BrianTX Offline
Korg Regular

Registered: 2002-04-01
Posts: 895
This brings me to some other thoughts/questions...

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: SayHello
ErrorDescription: $error.a.SayHello.b
Error: 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?

Brian

Top
#64555 - 2002-04-17 10:50 PM Re: UDF Design
BrianTX Offline
Korg Regular

Registered: 2002-04-01
Posts: 895
I messed up a bit on that.. was thinking too abstractly i suppose:

Function: SayHello
ErrorDescription: $error.a.SayHello.b
Error: 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. lol
This is better:

Function: SayHello
ErrorDescription: $Error.SayHello
Error: Error.SayHello
Line Number of error: LError.SayHello
Line Number of function call: FError.SayHello
...

Ok.. I think that works better. lol

Brian (I feel like a dummy now.)

Top
#64556 - 2002-04-17 11:15 PM Re: UDF Design
Howard Bullock Offline
KiX Supporter
*****

Registered: 2000-09-15
Posts: 5809
Loc: Harrisburg, PA USA
This thread is going great guns. [Smile]

Shawn, I have wondered how many worms are going to crawl out of the can opened by this thread but don't regret it one bit.

I know that I will be re-visiting some of my code after this duscussion dies down.
_________________________
Home page: http://www.kixhelp.com/hb/

Top
#64557 - 2002-04-17 11:34 PM Re: UDF Design
New Mexico Mark Offline
Hey THIS is FUN
****

Registered: 2002-01-03
Posts: 223
Loc: Columbia, SC
On the file handles thing:

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.

Here is an example using KiXtart file numbers to avoid this:

code:
FUNCTION Unique(OPTIONAL $sDS)
; Create a unique-named file in a given (or current) directory. Supports up to
; 32767 unique files in one directory.
; Syntax: Unique(DirectorySpec)
; Returns: Directory and file spec of created file or empty string if no
; file was created.
; Error level returned is that of the Open() return value
DIM $RC, $iFN
SRnd(@MSECS)
IF $sDS = '' $sDS = @CURDIR ENDIF
IF SubStr($sDS,Len($sDS),1) <> '\' $sDS=$sDS + '\' ENDIF
IF GetFileAttr($sDS) & 16 ; If sDS is a directory
DO
$Unique=DecToHex(Rnd())
$Unique=$sDS+'~KIX'+Right('0000'+$Unique,4)+'.TMP'
UNTIL NOT Exist($Unique)
$iFN=1
$RC=Open($iFN,$Unique,1)
WHILE $RC = -3 AND $iFN <= 10
$iFN=$iFN+1 $RC=Open($iFN,$Unique,1)
LOOP
IF $RC
$Unique=''
ELSE
IF Close($iFN) $Unique='' ENDIF
ENDIF
ENDIF
EXIT $RC
ENDFUNCTION

Here is the same function, but using the file system object to avoid the problem entirely.
code:
FUNCTION Unique(OPTIONAL $sDS)
; Create a unique-named file in a given (or current) directory. Supports up to
; 32767 unique files in one directory.
; Syntax: Unique(DirectorySpec)
; Returns: Directory and file spec of created file or empty string if no
; file was created.
; Error level returned is that of CreateObject() or OpenTextFile()
DIM $RC, $iFN
SRnd(@MSECS)
IF $sDS = '' $sDS = @CURDIR ENDIF
IF SubStr($sDS,Len($sDS),1) <> '\' $sDS=$sDS + '\' ENDIF
IF GetFileAttr($sDS) & 16 ; If sDS is a directory
DO
$Unique=DecToHex(Rnd())
$Unique=$sDS+'~KIX'+Right('0000'+$Unique,4)+'.TMP'
UNTIL NOT Exist($Unique)
$iFN=1
$RC=Open($iFN,$Unique,1)
WHILE $RC = -3 AND $iFN <= 10
$iFN=$iFN+1 $RC=Open($iFN,$Unique,1)
LOOP
$oFS = CreateObject("Scripting.FileSystemObject")
IF @ERROR EXIT @ERROR ENDIF
$oFileOpen = $oFS.OpenTextFile($Unique,2,1,0)
$RC=@ERROR
ENDIF

New Mexico Mark

Top
#64558 - 2002-04-18 09:21 AM Re: UDF Design
Richard H. Administrator Offline
Administrator
*****

Registered: 2000-01-24
Posts: 4945
Loc: Leatherhead, Surrey, UK
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 [Wink]
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:
code:
$udfReturn=$avMyDataArray

would continue to work, and the dataset would be kept clean.

Top
#64559 - 2002-04-18 01:29 PM Re: UDF Design
Howard Bullock Offline
KiX Supporter
*****

Registered: 2000-09-15
Posts: 5809
Loc: Harrisburg, PA USA
Hey Mark, a little like to WriteLog2(). [Wink] As long as you do not keep opening file handles and keeping them open for the duration of your script should be able to avoid the the limitation
_________________________
Home page: http://www.kixhelp.com/hb/

Top
#64560 - 2002-04-18 02:51 PM Re: UDF Design
Bryce Offline
KiX Supporter
*****

Registered: 2000-02-29
Posts: 3164
Loc: Houston TX
how about the ability to set the exit error code like this.

code:
seterror("1 2 56")

for $i = 0 to ubound(@error)

? @error[$i]
? @serror[$i]

next


function seterror($error)
$error = split($error," ")
exit($error)
endfunction

Bryce

Top
#64561 - 2002-04-18 03:09 PM Re: UDF Design
Howard Bullock Offline
KiX Supporter
*****

Registered: 2000-09-15
Posts: 5809
Loc: Harrisburg, PA USA
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. [Embarrassed]

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. [Frown]
_________________________
Home page: http://www.kixhelp.com/hb/

Top
#64562 - 2002-04-18 04:08 PM Re: UDF Design
Richard H. Administrator Offline
Administrator
*****

Registered: 2000-01-24
Posts: 4945
Loc: Leatherhead, Surrey, UK
Howard,
No apologies required. This type of thread is how some of the neatest KiXtart tricks have evolved.

In fact the SetError() trick came about because a very similar thread way back in May 2001. Anyone else remember this thread?

KiXcrypt only happened because of a long thread about securing scripts.

The KiXtart forums are great places for brain-storming ideas, and you never know when someone will surprise you with a simple way of achieving something you thought was impossible.

And personally I find these tricky "how can I" threads entertaining and educational. It sure beats working.

Top
#64563 - 2002-04-18 04:10 PM Re: UDF Design
BrianTX Offline
Korg Regular

Registered: 2002-04-01
Posts: 895
So, are you trying to close off the discussion? lol

Maybe we should be requesting that KIXtart have an optional error queue that will add functionality to the @error and @serror macros.

Maybe like this:

First activate:

ERRORTRAP=ON

Then have additional error functions that had a queue of errors:

$error1 = ERROR[a]
$lineerror1 = LERROR[a]
$error1string = SERROR[a]
[a] represents the nth @error code returned.

$error1 would return the errorcode.
$lineerror1 would return the line number of or function generating the errorcode.
$error1string would return text of the error.

In addition, have a command called:

ERRORLIST that would return a list of return codes and functions in column form:

errornum linenum textmessage
0 1
0 2
1 3 This is an error 1 message

.....
Yeah, this is totally off topic, but wouldn't it be cool? Maybe i should drop this in the suggestion box.

Brian

Top
#64564 - 2002-04-18 04:18 PM Re: UDF Design
Howard Bullock Offline
KiX Supporter
*****

Registered: 2000-09-15
Posts: 5809
Loc: Harrisburg, PA USA
This topic is a juggernaut. I don't know if I could close it if I tried. [Smile]
_________________________
Home page: http://www.kixhelp.com/hb/

Top
#64565 - 2002-04-23 06:41 PM Re: UDF Design
Shawn Administrator Offline
Administrator
*****

Registered: 1999-08-13
Posts: 8611
Guys,

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.

-Shawn

Top
#64566 - 2002-04-23 09:35 PM Re: UDF Design
New Mexico Mark Offline
Hey THIS is FUN
****

Registered: 2002-01-03
Posts: 223
Loc: Columbia, SC
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.

finis

[ 24 April 2002, 16:36: Message edited by: New Mexico Mark ]

Top
#64567 - 2002-04-23 10:49 PM Re: UDF Design
Shawn Administrator Offline
Administrator
*****

Registered: 1999-08-13
Posts: 8611
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:

ReadProfileString - returns a string
CreateObject - returns an object
SortArray - sorts an array and returns an array

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:

GetFileHandle()
IsStringUpper()
ReadProfileString()

Anyway, im just rambling again, anyone have anything to add about function naming conventions ?

-Shawn

[ 23 April 2002, 22:52: Message edited by: Shawn ]

Top
#64568 - 2002-04-24 04:32 PM Re: UDF Design
New Mexico Mark Offline
Hey THIS is FUN
****

Registered: 2002-01-03
Posts: 223
Loc: Columbia, SC
Good point. I'd never seen the action-object-type thing formally presented before, but it makes sense. I'll just edit the previous post to include this suggestion.

NMM

[ 24 April 2002, 16:34: Message edited by: New Mexico Mark ]

Top
#64569 - 2002-06-07 12:40 AM Re: UDF Design
Anonymous
Unregistered


Just saying Hi to Jack and all, I am about I just don't have the spare time I used to [Smile] My head hurts after reading this topic though :lol
Top
#64570 - 2002-06-06 03:32 PM Re: UDF Design
Jack Lothian Offline
MM club member
*****

Registered: 1999-10-22
Posts: 1169
Loc: Ottawa,Ontario, Canada
Hi Don [Smile]
_________________________
Jack

Top
Page 2 of 2 <12


Moderator:  Glenn Barnas, NTDOC, Arend_, Jochen, Radimus, Allen, ShaneEP, Mart 
Hop to:
Shout Box

Who's Online
0 registered and 159 anonymous users online.
Newest Members
emnipetro, Hirze, thequeen, ameliaryan, shalomsalami
17641 Registered Users

Generated in 0.043 seconds in which 0.012 seconds were spent on a total of 13 queries. Zlib compression enabled.

Search the board with:
superb Board Search
or try with google:
Google
Web kixtart.org