Posts: 2,696
Threads: 327
Joined: Apr 2022
Reputation:
217
Now, I know this might seem a little strange, but hear me out: I think folks need to start adopting the strategy of placing DECLARE LIBRARY routines at the end of their code, rather than at the beginning of it.
Now, hold up a moment there! Before you go saying, "Welp, Steve's finally lost his marbles, everybody's always placed those declarations at the top of their source code!", give me a chance to showcase why I'll probably be adopting this new coding style from now on:
Code: (Select All)
Print BorderWidth, TitleBarHeight
Function BorderWidth&
$Let GLUTGET = TRUE
BorderWidth = glutGet(506)
End Function
Function TitleBarHeight&
$Let GLUTGET = TRUE
TitleBarHeight = glutGet(507)
End Function
Sub ScreenMove_Middle
'Moves to the absolute middle of the desktop, ignoring border and title, so the program window is centered without
'taking them into consideration.
$Let GLUTGET = TRUE
_ScreenMove (_DesktopWidth - _Width - BorderWidth) / 2 + 1, (_DesktopHeight - _Height - BorderWidth) / 2 - TitleBarHeight + 1
End Sub
Sub ScreenMove (x, y)
'Moves to the absolute coordinates of the desktop, ignoring border and title, so the program window is
' positioned with the program window at the desired position, without taking them into consideration.
$Let GLUTGET = TRUE
_ScreenMove x - BorderWidth, y - BorderWidth - TitleBarHeight
End Sub
$If GLUTGET = TRUE Then
$If GLUTGET_DECLARED = UNDEFINED Then
$Let GLUTGET_DECLARED = TRUE
Declare Library
Function glutGet& (ByVal what&)
End Declare
$End If
$End If
As you can see from the code above, I can now write my SUB and FUNCTION in such a manner that they make certain that the DECLARE LIBRARY in question is included in my source.
Now, what's the point to such tomfoolery, you ask?
In this case, it allows me to wrap everything up nice and neat in one *.BM file. I don't need to turn the above into two different libraries, with one just for the *.BI Declare Library. It also allows me to write these routines and wrap them up so that I can limit which ones I require in a program. (See the recent Github Toolbox here https://github.com/SteveMcNeill/QB64-Pho...on-Toolbox for an example of this SUB/FUNCTION inclusion/exclusion at work.)
Now, using this style, I can include one of those routines uniquely in my code as needed, and it'll automatically add in the DECLARE with it. And, without me manually including one of those routines which need this particular DECLARE LIBRARY, it's simply excluded and not used at all in my program, keeping EXE filesize and memory usage as small as possible for whatever app I end up building.
DECLARE LIBRARY at the end of your code, instead of at the front. It might not be just as crazy as you'd think it is at first glance.
Posts: 1,272
Threads: 119
Joined: Apr 2022
Reputation:
100
I like this.
I've been thinking for a while now that a lesson on "best practices" would be a good addition to the tutorial. I'll need to scour this and previous forums for good little juicy tidbits like this one.
New to QB64pe? Visit the QB64 tutorial to get started.
QB64 Tutorial
Posts: 1,586
Threads: 59
Joined: Jul 2022
Reputation:
52
You guys have to be careful. There are a couple of Freebasic programmers that have absolutely no regard for "togetherness" of main-level code, putting bits of code here and there between subprogram definitions. Then there are other people exclusively using QB64 who deeply wish they could put `DATA` statements wherever they please. "Because I could do that to UDT definition" and "Because Steve now says I could DECLARE at the end" and other excuses.
Posts: 207
Threads: 13
Joined: Apr 2022
Reputation:
52
(10-13-2023, 03:53 AM)TerryRitchie Wrote: I like this.
I've been thinking for a while now that a lesson on "best practices" would be a good addition to the tutorial. I'll need to scour this and previous forums for good little juicy tidbits like this one.
Hi Terry,
we (Steve, you and me) already had a "best library practice" discussin about 5yrs. ago. Here's a conclusion about all suggestions and my final thougths about it. It's the "defacto" rules I build all my libraries. Also read the following posts by Fellippe regarding OPTION _EXPLICIT.
https://qb64forum.alephc.xyz/index.php?t...39#msg4039
Posts: 2,696
Threads: 327
Joined: Apr 2022
Reputation:
217
@RhoSigma Aye, I remember all those rules which we came up with, and like you, I try to always build my libraries up to those standards. The thing is, as time passes -- that discussion was about 5 years ago -- you sometimes pick up new tricks on how to do things. Let me showcase a few new tricks that I've started to add to my habits for library creation:
Code: (Select All)
$IF INCLUDE_ALL = TRUE OR INCLUDE_SCREEN = TRUE OR INCLUDE_SCROLLDOWN = TRUE THEN
$IF SCROLLDOWN_BM = UNDEFINED THEN
$LET SCROLLDOWN_BM = TRUE
SUB ScrollDown (ImageHandle AS LONG)
$CHECKING:OFF
DIM m AS _MEM
DIM AS LONG p, w
DIM AS STRING t
m = _MEMIMAGE(ImageHandle)
p = _PIXELSIZE
IF p = 0 THEN w = _WIDTH * 2 ELSE w = _FONTHEIGHT * _WIDTH * p
t$ = SPACE$(m.SIZE - w)
_MEMGET m, m.OFFSET, t$
CLS
_MEMPUT m, m.OFFSET + w, t$
_MEMFREE m
$CHECKING:ON
END SUB
$END IF
$END IF
$IF INCLUDE_ALL = TRUE OR INCLUDE_SOUND = TRUE OR INCLUDE_SPEAK = TRUE THEN
$IF SPEAK_BM = UNDEFINED THEN
$LET SPEAK_BM = TRUE
$IF WIN THEN
SUB Speak (text AS STRING, Speaker AS INTEGER, Speed AS LONG)
DIM AS STRING message, remove
DIM out$
DIM AS LONG j, i
message = text
'some symbols and such can't be used with Powershell like this, as they're command symbols
'we need to strip them out of our text. (Like apostrophes!)
remove$ = "'" + CHR$(34) 'add to remove$ here, if more symbols need to be removed as future testing showcases problems
FOR j = 1 TO LEN(remove$)
DO
i = INSTR(message, MID$(remove$, j, 1))
IF i THEN message = LEFT$(message, i - 1) + MID$(message, i + 1)
LOOP UNTIL i = 0
NEXT
out$ = "Powershell -Command " + CHR$(34)
out$ = out$ + "Add-Type -AssemblyName System.Speech; "
out$ = out$ + "$Speech = New-Object System.Speech.Synthesis.SpeechSynthesizer; "
IF Speaker = 0 THEN out$ = out$ + "$Speech.SelectVoice('Microsoft David Desktop'); "
IF Speaker = 1 THEN out$ = out$ + "$Speech.SelectVoice('Microsoft Zira Desktop'); "
IF Speed THEN out$ = out$ + "$Speech.Rate =" + STR$(Speed) + "; "
out$ = out$ + "$Speech.Speak('" + message + "');" + CHR$(34)
SHELL _HIDE out$
END SUB
$ELSE
Sub Speak (dummy as string, dummy1 as Integer, dummy2 as long)
BEEP
_MESSAGEBOX "Warning!", "Notice: SUB Speak is a Windows-Only routine, as it relies upon Powershell to do its work. This routine does not work on Linux or Mac.", "warning"
End Sub
$END IF
$END IF
$END IF
$IF INCLUDE_ALL = TRUE OR INCLUDE_SCREEN = TRUE OR INCLUDE_BORDERWIDTH = TRUE THEN
$IF BORDERWIDTH_BM = UNDEFINED THEN
$LET BORDERWIDTH_BM = TRUE
FUNCTION BorderWidth&
$LET GLUTGET = TRUE
BorderWidth = glutGet(506)
END FUNCTION
$END IF
$END IF
Now, the above is from the new toolbox which I'm assembling on github. The idea here is to allow the user the flexibility to ONLY add what routines they want into their code, to reduce size and memory usage as much as possible.
Want all three routines? $LET INCLUDE_ALL = TRUE <-- one line of code at the top of your program and they're all included.
Only want the two screen routines? $LET INCLUDE_SCREEN = TRUE <-- again, one line of code which only enables those two routines.
Only want one particular routine? $LET INCLUDE_SPEAK = TRUE <-- and now we're only including the powershell text-to-speech routine.
And another trick which I've learned over the last few years?
Code: (Select All)
$IF BORDERWIDTH_BM = UNDEFINED THEN
$LET BORDERWIDTH_BM = TRUE
FUNCTION BorderWidth&
$LET GLUTGET = TRUE
BorderWidth = glutGet(506)
END FUNCTION
$END IF
With the above wrapped around the sub/function, it doesn't matter if multiple libraries include the routine. It doesn't matter if it's in half a dozen $INCLUDE files. The first one that calls it will set the precompiler flag for it, and it'll be excluded when it comes to the rest of those routines.
No more "Duplicate Definitions" or "Name already in use" or whatnot errors.
And take this practice, along with the idea of moving your DECLARE LIBRARY code to the end of your source as I suggested in the first post here, and you can easily use one library to hold a whole collection of subs and functions, and only include the specific ones you need into your program. Just make certain that if one of the required programs uses a different routine inside the library, that it sets the independent flag to make certain that requite routine is also enabled. (Like with the glutget routines in my first post.)
I'm *still* sticking to the old set and true rules that we invented all those years ago -- I'm just learning to expand my tricks and building upon those rules in an attempt to make my stuff both more flexible and less error prone in the future.
Posts: 207
Threads: 13
Joined: Apr 2022
Reputation:
52
10-13-2023, 08:50 AM
(This post was last modified: 12-03-2023, 09:11 AM by RhoSigma.)
(10-13-2023, 08:06 AM)SMcNeill Wrote: @RhoSigma Aye, I remember all those rules which we came up with, and like you, I try to always build my libraries up to those standards. The thing is, as time passes -- that discussion was about 5 years ago -- you sometimes pick up new tricks on how to do things. Let me showcase a few new tricks that I've started to add to my habits for library creation:
Code: (Select All)
$IF INCLUDE_ALL = TRUE OR INCLUDE_SCREEN = TRUE OR INCLUDE_SCROLLDOWN = TRUE THEN
$IF SCROLLDOWN_BM = UNDEFINED THEN
$LET SCROLLDOWN_BM = TRUE
SUB ScrollDown (ImageHandle AS LONG)
$CHECKING:OFF
DIM m AS _MEM
DIM AS LONG p, w
DIM AS STRING t
m = _MEMIMAGE(ImageHandle)
p = _PIXELSIZE
IF p = 0 THEN w = _WIDTH * 2 ELSE w = _FONTHEIGHT * _WIDTH * p
t$ = SPACE$(m.SIZE - w)
_MEMGET m, m.OFFSET, t$
CLS
_MEMPUT m, m.OFFSET + w, t$
_MEMFREE m
$CHECKING:ON
END SUB
$END IF
$END IF
$IF INCLUDE_ALL = TRUE OR INCLUDE_SOUND = TRUE OR INCLUDE_SPEAK = TRUE THEN
$IF SPEAK_BM = UNDEFINED THEN
$LET SPEAK_BM = TRUE
$IF WIN THEN
SUB Speak (text AS STRING, Speaker AS INTEGER, Speed AS LONG)
DIM AS STRING message, remove
DIM out$
DIM AS LONG j, i
message = text
'some symbols and such can't be used with Powershell like this, as they're command symbols
'we need to strip them out of our text. (Like apostrophes!)
remove$ = "'" + CHR$(34) 'add to remove$ here, if more symbols need to be removed as future testing showcases problems
FOR j = 1 TO LEN(remove$)
DO
i = INSTR(message, MID$(remove$, j, 1))
IF i THEN message = LEFT$(message, i - 1) + MID$(message, i + 1)
LOOP UNTIL i = 0
NEXT
out$ = "Powershell -Command " + CHR$(34)
out$ = out$ + "Add-Type -AssemblyName System.Speech; "
out$ = out$ + "$Speech = New-Object System.Speech.Synthesis.SpeechSynthesizer; "
IF Speaker = 0 THEN out$ = out$ + "$Speech.SelectVoice('Microsoft David Desktop'); "
IF Speaker = 1 THEN out$ = out$ + "$Speech.SelectVoice('Microsoft Zira Desktop'); "
IF Speed THEN out$ = out$ + "$Speech.Rate =" + STR$(Speed) + "; "
out$ = out$ + "$Speech.Speak('" + message + "');" + CHR$(34)
SHELL _HIDE out$
END SUB
$ELSE
Sub Speak (dummy as string, dummy1 as Integer, dummy2 as long)
BEEP
_MESSAGEBOX "Warning!", "Notice: SUB Speak is a Windows-Only routine, as it relies upon Powershell to do its work. This routine does not work on Linux or Mac.", "warning"
End Sub
$END IF
$END IF
$END IF
$IF INCLUDE_ALL = TRUE OR INCLUDE_SCREEN = TRUE OR INCLUDE_BORDERWIDTH = TRUE THEN
$IF BORDERWIDTH_BM = UNDEFINED THEN
$LET BORDERWIDTH_BM = TRUE
FUNCTION BorderWidth&
$LET GLUTGET = TRUE
BorderWidth = glutGet(506)
END FUNCTION
$END IF
$END IF
Now, the above is from the new toolbox which I'm assembling on github. The idea here is to allow the user the flexibility to ONLY add what routines they want into their code, to reduce size and memory usage as much as possible.
Want all three routines? $LET INCLUDE_ALL = TRUE <-- one line of code at the top of your program and they're all included.
Only want the two screen routines? $LET INCLUDE_SCREEN = TRUE <-- again, one line of code which only enables those two routines.
Only want one particular routine? $LET INCLUDE_SPEAK = TRUE <-- and now we're only including the powershell text-to-speech routine.
And another trick which I've learned over the last few years?
Code: (Select All)
$IF BORDERWIDTH_BM = UNDEFINED THEN
$LET BORDERWIDTH_BM = TRUE
FUNCTION BorderWidth&
$LET GLUTGET = TRUE
BorderWidth = glutGet(506)
END FUNCTION
$END IF
With the above wrapped around the sub/function, it doesn't matter if multiple libraries include the routine. It doesn't matter if it's in half a dozen $INCLUDE files. The first one that calls it will set the precompiler flag for it, and it'll be excluded when it comes to the rest of those routines.
No more "Duplicate Definitions" or "Name already in use" or whatnot errors.
And take this practice, along with the idea of moving your DECLARE LIBRARY code to the end of your source as I suggested in the first post here, and you can easily use one library to hold a whole collection of subs and functions, and only include the specific ones you need into your program. Just make certain that if one of the required programs uses a different routine inside the library, that it sets the independent flag to make certain that requite routine is also enabled. (Like with the glutget routines in my first post.)
I'm *still* sticking to the old set and true rules that we invented all those years ago -- I'm just learning to expand my tricks and building upon those rules in an attempt to make my stuff both more flexible and less error prone in the future.
No problem with all that, I like the idea with the glut stuff. I usually solved that problem by using ALIAS names, eg.
In LibOne:
DECLARE CUSTOMTYPE LIBRARY
SUB LibOne_memset ALIAS memset (BYVAL dest%&, BYVAL value&, BYVAL size&)
SUB LibOne_memcpy ALIAS memcpy (BYVAL dest%&, BYVAL sour%&, BYVAL size&)
SUB LibOne_strncpy ALIAS strncpy (BYVAL dest%&, BYVAL sour%&, BYVAL size&)
END DECLARE
In LibTwo:
DECLARE CUSTOMTYPE LIBRARY
SUB LibTwo_memset ALIAS memset (BYVAL dest%&, BYVAL value&, BYVAL size&)
SUB LibTwo_memcpy ALIAS memcpy (BYVAL dest%&, BYVAL sour%&, BYVAL size&)
SUB LibTwo_strncpy ALIAS strncpy (BYVAL dest%&, BYVAL sour%&, BYVAL size&)
END DECLARE
and then using the alias names in the respective libraries where I declared it, but finally they end up using all the same function in the linked EXE.
another thing I came up with, is to implement version strings into all my libraries and tools,
see here: https://qb64phoenix.com/forum/showthread.php?tid=2220
Posts: 734
Threads: 30
Joined: Apr 2022
Reputation:
43
10-18-2023, 11:07 AM
(This post was last modified: 10-18-2023, 11:08 AM by SpriggsySpriggs.)
Depending on the kind of library, I like to put the declarations at the top, if multiple functions will be pulling from the same DLL. However, if I have tons of functions and each one can be used basically independent of another, then I will put the declarations of the libraries inside the functions themselves. That way it is a simple copy and paste from `Function` to `End Function`. Otherwise, no. I'm not putting declarations at the end of my code. Ugly as Hell.
P.S.
Did y'all remove the backtick formatting?
Tread on those who tread on you
Posts: 207
Threads: 13
Joined: Apr 2022
Reputation:
52
(10-18-2023, 11:07 AM)SpriggsySpriggs Wrote: P.S.
Did y'all remove the backtick formatting?
Yes it's gone, caused different problems when using backticks inside of code examples, such as the _BIT type suffix or ASCII Art like stuff.
In some cases it just eat the backticks, in other cases it messed up highlighting, and in worst cases it did build nested codeboxes.
|