Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Best Practice for Library Building and Toolboxes
#1
@SMcNeill, @RhoSigma and @TerryRitchie

I don't remember seeing this discussion for Libraries and would very much like to review the results you agreed upon (if you did) actually the thinking and reasoning behind the guidelines would be best for review.

A review might aid Terry's tutorial as well.

I do have some ideas for guidelines in building Toolboxes.

1. Have code that tests each sub and function perhaps against other methods. I have a folder called Test just for that purpose each file is a routine or related few. Make notes why you did this and that. If something goes wrong using the routine go back to this test file and update the routine. Rule of thumb question: "Can you ever test enough?"

2. All the best subs and function are pasted into 000Handy at the top of my QB64 Work Folder for easy access and helps see if they can coexist ie no name conflicts.

3. If the routine needs another sub or function be sure to make note of it.

4. Avoiding dependence of constants from Main program but list inside sub if you are using a UDT or constant or anything that needs inclusion in main code so routine works.

That's a few from the top of my head for toolboxes, I am big fan of copy / paste from toolbox to get just the routines I need into the app I am working so no extra library files needed. But I don't write giant programs either except GUI, oh Interpreter and FVal$ is getting there specially when finish adding String Math and Astring Functions.

Toolbox Advantage: after pasted into app, you can modify as needed for app without screwing up the toolbox code. That is pretty hard to do with Library Code, I found out over and over with GUI stuff.
b = b + ...
Reply
#2
(10-18-2023, 12:10 PM)bplus Wrote: I am big fan of copy / paste from toolbox to get just the routines I need into the app I am working so no extra library files needed. But I don't write giant programs either except GUI, oh Interpreter and FVal$ is getting there specially when finish adding String Math and Astring Functions.

Have you taken a look at my latest attempt to create a modern toolbox?  https://github.com/SteveMcNeill/QB64-Pho...on-Toolbox

The idea here is to have one simple toolbox that I can grab and paste into any QB64 folder and then just add an $INCLUDE pointing to the two files inside it.  After that, I can pick and choose what routines I want to add to my code, and exclude everything else.

For example, at the moment let's say I just want to make use of the ExtendedTimer function.  It's this simple in the newest setup:

Code: (Select All)
$LET INCLUDE_EXTENDEDTIMER = TRUE
$INCLUDE:'./Library Files/Toolbox.BI'

Print "My ExtendedTimer is currently: "; ExtendedTimer

$INCLUDE:'./Library Files/Toolbox.BM'

One $LET to declare what I want to use.  $INCLUDE the files from wherever they sit in relation to QB64 (in this case, inside the QB64PE folder, in their own Library Files subfolder.)  Then just use them as desired.  Smile

No need to constantly open a library file, copy out some partial content of it, paste it in another program...  Just use the $LET statement and declare what parts you want from it (or don't want from it, with something such as:  $LET INCLUDE_CONST = FALSE.) 

Wink
Reply
#3
Quote:1) Subs/Functions should start with creator initial.  A Steve(tm) library would use SM_ for naming convention.  Sure, there might be more than one guy with the SM initials out there, but if there is, he hasn't posted much here on the forums, and all conflicts with SM_Xpos and TR_Xpos are instantly cleared up...

Although I mentioned this kind of ID earlier too here in this topic, after rethinking I came to the conclusion that we don't necessarily need IDs for SUB/FUNC names. Why?? - Every library has a specific purpose, eg. imageprocessing, spritesheets, menu, database, sorting etc., from that point of view the SUB/FUNC names should be unique enough between the different library types, as the names are usually chosen having the context of the respective library in mind. Even if there may exist more than one library for the same purpose, eg. three different spritesheet libs from three different authors, then still the user will most probably decide to use just one of it, the one which fits best for his needs.

Quote:Quote
2) GLOBAL Names should be self descriptive as much as possible.  SM_TextToImage is much easier to understand, remember, and use than SM_TTI, and chances are, it's also more resistant against accidental duplication.
Agreed, however for me it doesn't matter if the initials are representing the authors name or the library name/type. Every author should just be consistent on his preferred naming scheme.

Quote:Quote
3) COMMON variable names (such as any single digit character) should *always* be LOCAL and TEMPORARY in scope.  Don't DIM SHARED X, Y, I, K, M, ect...  Keep them reserved as temp variables and they should never interfere with another user's variables.
Totally agreed.

Quote:Quote
4) Unless NECESSARY, Subs/Functions should never pass values back via the parameter.  Instead of SUB SM_MathJunk (X, Y), values should be passed as SUB SM_MathJunk (TempX, TempY), with the next line reading X = TempX: Y = TempY.  This practice prevents all instances of accidental value corruption.
Agreed, however it seems you chose the easy way for you as library implementor. I personally would leave it as SUB SM_MathJunk (X, Y) for the public user and rather do tmpX = X: tmpY = Y internally, as any unexperienced user could be confused, if he reads to give "temporary" arguments to a SUB/FUNC call.
  Very important, if argument side effects are necessary/intended, then this MUST be explicitly mentioned in the respective function description. Also remind the potential user of such a SUB/FUNC that he should not use extra parenthesis to make a BYVAL parameter transfer. Also he can not give any literal values, but only variables of the very same variable type as specified in the parameter list, because QB64 would internally create a temporary parameter for literals or wrong variable types, which would compromise the intended side effect.

Quote:Quote
5) Arrays should be hard indexed.  Don't DIM SHARED Array(10); instead DIM SHARED Array(0 To 10).  This prevents incompatibility if the user has OPTION BASE 1 in effect.
Indeed, I've never thought about this one Steve, agreed.

Quote:Quote
6) Subs/Functions should *always* restore settings before exit.  If your sub needs a _DISPLAY to show text on the screen (such as for a message box), check to see if the user had _AUTODISPLAY on, and if so, restore it on exit.  _DEST, _SOURCE, _BLEND, COLOR, Visible Screen, and lots more *can* be altered while in a sub, but unless that's the intended purpose of the sub, it shouldn't happen in Library code.
Totally agreed.

Quote:Quote
7) Useage of Global Flags/Metacommands should be avoided.  DON'T count on DEFLNG A-Z to be set, or OPTION BASE to be 0/1, or $DYNAMIC to be in use.  Once you share a library publicly, you'll run into somebody who has the option set completely backwards of what you always ASSUMED was standard.
Indeed, never count on it and never ever even think on defining it in your library. Also, just as Terry mentioned, don't ever think on defining CONSTs for true/false, consider it would be the same as doing a DEFLNG A-Z in your library. Instead use type suffixes in your library code and literal booleans 0/-1 as suggested by Terry.

Ready for further revision of the rules....

The above is from our last conversation where we kicked this topic around and mulled over what would be the best practices for things.
Reply
#4
(10-18-2023, 12:25 PM)SMcNeill Wrote:
(10-18-2023, 12:10 PM)bplus Wrote: I am big fan of copy / paste from toolbox to get just the routines I need into the app I am working so no extra library files needed. But I don't write giant programs either except GUI, oh Interpreter and FVal$ is getting there specially when finish adding String Math and Astring Functions.

Have you taken a look at my latest attempt to create a modern toolbox?  https://github.com/SteveMcNeill/QB64-Pho...on-Toolbox

The idea here is to have one simple toolbox that I can grab and paste into any QB64 folder and then just add an $INCLUDE pointing to the two files inside it.  After that, I can pick and choose what routines I want to add to my code, and exclude everything else.

For example, at the moment let's say I just want to make use of the ExtendedTimer function.  It's this simple in the newest setup:

Code: (Select All)
$LET INCLUDE_EXTENDEDTIMER = TRUE
$INCLUDE:'./Library Files/Toolbox.BI'

Print "My ExtendedTimer is currently: "; ExtendedTimer

$INCLUDE:'./Library Files/Toolbox.BM'

One $LET to declare what I want to use.  $INCLUDE the files from wherever they sit in relation to QB64 (in this case, inside the QB64PE folder, in their own Library Files subfolder.)  Then just use them as desired.  Smile

No need to constantly open a library file, copy out some partial content of it, paste it in another program...  Just use the $LET statement and declare what parts you want from it (or don't want from it, with something such as:  $LET INCLUDE_CONST = FALSE.) 

Wink

I see what you are saying but I am not a fan of github nor of carrying around a library file. Imagine having to post link to github or attaching SM_Toolbox.bi and SM_Toolbox.bm to code at forums, eh... zip files solves all that I guess!

BTW is it SM_Toolbox ? ;-))
b = b + ...
Reply
#5
Points 1 and 2
Initialing every routine? Yikes!
It should be enough to Author the library and more important IMHO to date even time the revisions for fast and furious development. Then 3 years later you can easily find what the most updated version was. A lesson I learned going back to GUI or String Math or oh Interpreter projects.

And see this stuff doesn't matter when copy/paste from toolbox.
Another advantage of toolbox you can see and update the routines to your hearts content and app needs without worry of messing up or being compatible with old code.

Points 3 and 4 ehhh...
There are times when you need to use the ByRef default to get more than 1 return value from a Sub or function because QB64 Functions are limited to primitive types and there is no ByVal in QB64!

Rule of Sub or Function: If you change the incoming value of parameter and you don't want the routine returning that value make a copy of it and use that in routine. So check for a change of parameter value when building routine. That is good hard rule to follow in my book.

#5 yep! hard code the array indexes

#6 restore? absolutely for general dialogue functions coming from outside app.
Or check _dest, col, row, color and _display status before every PRINT or graphics to screen.

#7 might be restated use Option _Explicit as best practice principle and avoid Options that defeat optimal use of Option _Explicit. _NoPrefix is a bad practice IMHO along with Option Base and Dynamic.
b = b + ...
Reply
#6
(10-18-2023, 12:43 PM)bplus Wrote: I see what you are saying but I am not a fan of github nor of carrying around a library file. Imagine having to post link to github or attaching SM_Toolbox.bi and SM_Toolbox.bm to code at forums, eh... zip files solves all that I guess!

BTW is it SM_Toolbox ? ;-))

One idea is to simply start bundling user libraries in with QB64 itself eventually -- then everyone would always have a copy right there at hand and not need to grab updates from github.  Only thing that would need to happen before we could go that route, is we'd need to get a dedicated group of people who are willing to keep those libraries updated and working, so we're not including dead junk in with the main releases.

As for sharing, what you're talking about is basically the same as it is now -- anyone who writes any real code in QB64PE has to simply zip up the files and share them like that.  After all, what are you going to do with your sound, font, image, and other resources?  Small programs, you can always grab out the routine and copy/paste it, if you want, to save the lazy folks the effort of downloading your library itself, but you'd still have to offer up a zip file which would hold all those resource files.

As for SM_Toolbox, I'm not using that in this case.  Honestly, in time, I hope more folks will help contribute their own stuff to the toolbox and it'll become a community resource, and not just a personal one for yours truly.  Smile
Reply
#7
(10-18-2023, 01:33 PM)bplus Wrote: Points 1 and 2
Initialing every routine? Yikes!
It should be enough to Author the library and more important IMHO to date even time the revisions for fast and furious development. Then 3 years later you can easily find what the most updated version was. A lesson I learned going back to GUI or String Math or oh Interpreter projects.

$LET INCLUDE_ALL = TRUE

^There.  Now you've included everything in your code.

$LET INCLUDE_IMAGE = TRUE

^ And with that, you've narrowed it down to just including the routines which are image related (filled circle, filled triangle, rounded rectangles, ect) into your code.

And by keeping it all on a github, you can easily grab the latest release (or whichever past release you need for whatever reason).  Wink
Reply
#8
This ought to be a Bible chapter ?
Reply
#9
Quote:@bplus -  5) Arrays should be hard indexed.  Don't DIM SHARED Array(10); instead DIM SHARED Array(0 To 10).  This prevents incompatibility if the user has OPTION BASE 1 in effect.
Wether that makes sense? If you declare an array with A( 0 To 10) you have eleven array elements, despite OptionBase 1.
To prevent this you have to fill the array with 1 to 10. With it a declaration with A(0 To 10) has no practical use.

Code: (Select All)

'18. Okt. 2023

Option _Explicit

Option Base 1

Dim As Integer feld(0 To 10)
Dim As Integer feld2(0 To 10)
Dim As Integer feld3(10)

Dim As Integer a, b, c, d

Locate 3, 3
For a = 0 To 10
  feld(a) = a
  Print Using "## "; feld(a),
Next

Locate 5, 3
For b = 1 To 10
  feld2(b) = b
  Print Using "## "; feld2(b),
Next

Locate 7, 3
For c = 1 To 10
  feld3(c) = c
  Print Using "## "; feld3(c),
Next

'Error - Out of range.
Locate 9, 3
For d = 0 To 10
  feld3(d) = d
  Print Using "## "; feld3(d),
Next

End
Reply
#10
(10-18-2023, 03:58 PM)Kernelpanic Wrote:
Quote:@bplus -  5) Arrays should be hard indexed.  Don't DIM SHARED Array(10); instead DIM SHARED Array(0 To 10).  This prevents incompatibility if the user has OPTION BASE 1 in effect.
Wether that makes sense? If you declare an array with A( 0 To 10) you have eleven array elements, despite OptionBase 1.
To prevent this you have to fill the array with 1 to 10. With it a declaration with A(0 To 10) has no practical use.

Code: (Select All)

'18. Okt. 2023

Option _Explicit

Option Base 1

Dim As Integer feld(0 To 10)
Dim As Integer feld2(0 To 10)
Dim As Integer feld3(10)

Dim As Integer a, b, c, d

Locate 3, 3
For a = 0 To 10
  feld(a) = a
  Print Using "## "; feld(a),
Next

Locate 5, 3
For b = 1 To 10
  feld2(b) = b
  Print Using "## "; feld2(b),
Next

Locate 7, 3
For c = 1 To 10
  feld3(c) = c
  Print Using "## "; feld3(c),
Next

'Error - Out of range.
Locate 9, 3
For d = 0 To 10
  feld3(d) = d
  Print Using "## "; feld3(d),
Next

End

Ah yeah, no. Not for generic subs and functions that can be used in any ones app! You don't want to guess or check if Option Base is used.

Steve, Rho, Terry will tell ya the same I think.
b = b + ...
Reply




Users browsing this thread: 6 Guest(s)