Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
On Exit question
#21
(06-02-2023, 09:33 PM)Dimster Wrote:
Quote:and ironically Option _Explicit makes no difference in the example Dimster gave us

Not sure what you mean B .... The example with NO Option _Explicit runs fine with no error, but if you place Option _Explicit at the beginning of that code you get an error warning of an undefined variable. 

...

Really, this runs fine for you???
Code: (Select All)
Const i = 1

for i = 1 to 10
print i
next

Let me know what version QB64 you have LOL ;-))


I was just commenting on Terry's preaching of Option _Explicit, yes it is good but this case of yours Dimster is not helped by Option _Explicit. You do Dim the variable in a way with a Const declaration, Option _Explicit won't call you out for using Const instead of Dim. 

Again Terry's preaching is in no way wrong, and great to advocate Option _Explicit usage!
b = b + ...
Reply
#22
Sorry if I sounded preachy, didn't mean to. Since I run the tutorial I gets lots of questions and requests for help behind the scenes. It's sometimes near impossible to help because of sudden variable (and "magic number", another issue entirely) creation. Declaring everything in my opinion really does help.
There are two ways to write error-free programs; only the third one works.
QB64 Tutorial
Reply
#23
Hey Terry I am just using "preachy" to be dramatic but yes what is more evil waste of time than a typo! Smile
b = b + ...
Reply
#24
1st I apologize to NasaCow, hope the discussion on Option _Explicit may help with the search for/correction of the error you were looking for help on.

2nd ... the issue/learning lesson, for me in this thread is Dimensioned variables v's UnDimensioned variables. In particular a control variable. I have often seen the use of "i" or "j" or "x" being used in many books on Qbasic where these control variables are not Dimmed. They appear to be throw away variables in terms of their use is not material to math formulas or capture of a results of a process. They can be used over and over again as a control variable because their type is defined by default and their value is clearly defined in a range (ie 1 to 10). The only time I will Dim a control variable like "x" is if I need it to carry over into a subroutine or back to the main module.

The Dimming of a control variable, in my experience just added to multiple lines of Dim and Dim Shared in a large program. Plus it doesn't help with the ending value , or limiting the control range. For example 

Code: (Select All)
Dim Shared y(1 To 10)
Dim x

For x = 1 To 10
    Print "Hello World"
Next
Print
Print x
Print
Print
For y = 1 To 10
    Print "Hello back at ya"
Next
Print
Print y

In each case x and y finished outside of the control range, so I have to be careful when I do Dim x or y to use in another sub or function. 

Not sure I see the value in Option _Explicit for control variables, nor how Dimensioning all control variables is a "Best Practice" but this old dog is definitely learning new tricks by trial and error especially when some masterfully programmers are highlighting the new trick.
Reply
#25
@Dimster Where Option _Explicit really shines is when you make a simple typo in your 10,000+ lines of code.

Let's say we have a variable like so:   

DIM cheetos AS _Byte

Now, we write a line of code like so:

cheetos = (cheotes + 1) Mod 2

Now, is the syntax checker going to find anything wrong with that line of code?  

NOPE!  It's perfectly valid code!

Are YOU going to be able to see that you misspelled that word at 2AM in the morning, when you finally compile it and start getting unexpected results from the program?  Or is your poor eyes going to just glaze over it as you scan your code and look for mistakes, skipping line 9213 repeatedly as your scroll from lines 1 to 11423 looking for a glitch??

A lot of times, the answer is: NOPE!!  <--This is one of the most common type programmer errors, and one of the hardest to self-diagnose and correct.  We *KNOW* what should be correct.  Our brains tells us what should be correct.  And our eyes just glaze over when skimming and assume that we're correct -- even if we're not.

Option _Explicit would save you a lot of hair pulling and frustration in this instance.  The moment you type CHEOTES instead of CHEETOS, it'll scream at you:  RAHHR!!!  YOU NEVER DEFINED THAT VARIABLE!!  BAD BOY!!  BAD, BAD BOY!!!  RAHHHHHRRRRRRR!!!!

Well... It might be a little more polite than that -- I wouldn't know, as I'm always a perfect coder and never make such mistakes and as such never have to use Option _Explicit to find any glitches in my perfect code.   Big Grin
Reply
#26
Hi Steve - I do see the value in Option _Explicit catching those misspelled variables, however my control variables (x,y,i etc) are difficult to misspell. Terry has an interesting approach to nomenclature of variables and as B and you have pointed out, you guys use it all the time. So I'm guessing here you have specific control variables which you use all the time solely for loop/iterations? You would need a least two for sort routines and perhaps a 3rd as a backup. These 3 or so control variables would be Dimensioned when? On the fly as you need them or because you know they will probably be needed some time, they are Dimensioned as a routine course after Option _Explicit?
Reply
#27
That reminds me of an error that apparently occurred quite often in the days of 8 and 16 bit computers (from Code Complete First Edition): The programmer didn't take into account that the result of a calculation no longer fits into the relevant variable.

The program works correctly as long as the sum stays within the bounds of an integer. A nice logical mistake!  Big Grin

Code: (Select All)
'Beispiel fuer Ueberlauf - 3. Juni 2023

Option _Explicit

Dim As Integer wert1, wert2, ergebnis

Locate 2, 2
Print "Example for an overflow"

Locate 4, 2
Input "Number between 1 to 32768: ", wert1

Locate 5, 2
Input "Number between 1 to 32768: ", wert2

ergebnis = wert1 + wert2

Locate CsrLin + 2, 2
Print Using "Das Ergebnis = ###,###"; ergebnis

End

Code Complete 2 (Github)

Code Complete 2

And the First Edition (Book), in English:
Code Complete First Edition
Reply
#28
(06-03-2023, 03:10 PM)Dimster Wrote: Hi Steve - I do see the value in Option _Explicit catching those misspelled variables, however my control variables (x,y,i etc) are difficult to misspell. Terry has an interesting approach to nomenclature of variables and as B and you have pointed out, you guys use it all the time. So I'm guessing here you have specific control variables which you use all the time solely for loop/iterations? You would need a least two for sort routines and perhaps a 3rd as a backup. These 3 or so control variables would be Dimensioned when? On the fly as you need them or because you know they will probably be needed some time, they are Dimensioned as a routine course after Option _Explicit?

Steve highlighted the perfect example for the use case of OPTION _EXPLICIT. Having to DIM all variables is not a negative side effect of using it. Here is an example I coded in the last half hour. Both of these code listings do the exact same thing. The first example is reminiscent of what was found in those early BASIC books. The second example is what can be done using QB64 and a bit of variable planning. Imagine being handed the first example and your task is either to find a bug or modify the code in some significant way. Now you're handed the second listing and given the same task. Which would you prefer?

Code: (Select All)
'My cool program

DIM x(59)
DIM y(59)

r = 120
FOR i = 0 TO 6.178465447 STEP .104719753
    x(s) = COS(i - 1.5707963) * r + 320
    y(s) = SIN(i - 1.5707963) * r + 240
    s = s + 1
NEXT i

SCREEN _NEWIMAGE(640, 480, 32)
DO
    CLS
    _LIMIT 60
    ps = s
    s = VAL(RIGHT$(TIME$, 2))
    IF ps <> s THEN
        s6 = 0
    ELSE
        s6 = s6 + 1
    END IF
    CIRCLE (x(s6), y(s6)), 10
    PAINT (x(s6), y(s6))
    CIRCLE (x(s), y(s)), 10, _RGB32(255, 255, 254)
    PAINT (x(s), y(s)), _RGB32(255, 255, 254), _RGB32(255, 255, 254)
    _DISPLAY
LOOP UNTIL _KEYDOWN(27)
SYSTEM

Code: (Select All)
' 60th of a second sweeping clock
' Circles sweep around a clock face at 1 second and 60th second intervals

OPTION _EXPLICIT '                     force variable declaration

CONST PI = 3.1415926 '                 value of PI
CONST PI2 = 2 * PI '                   2 times PI (one full radian sweep)
CONST Radian60 = PI2 / 60 '            2 times PI divided by 60
CONST Radian4 = PI2 / 4 '              one quarter of the value of PI times 2
CONST SWIDTH = 640 '                   screen width
CONST SHEIGHT = 480 '                  screen height
CONST CCOLOR = _RGB32(255, 255, 254) ' one second sweep circle color

TYPE Tick_Coordinate '                 location of each clock tick coordinate
    x AS SINGLE '                      x coordinate
    y AS SINGLE '                      y coordinate
END TYPE

DIM Tick(59) AS Tick_Coordinate '      tick coordinates
DIM Radian AS SINGLE '                 loop counter
DIM Radius AS INTEGER '                radius of clock face
DIM Second AS INTEGER '                counter: one second
DIM Second60 AS INTEGER '              counter: 60th of a second
DIM pSecond AS INTEGER '               previous second

Radius = SHEIGHT / 4 '                                              calculate clock face radius
Second = 0 '                                                        reset second counter
FOR Radian = 0 TO PI2 - Radian60 STEP Radian60 '                    cycle through 60 radian points
    Tick(Second).x = COS(Radian - Radian4) * Radius + SWIDTH / 2 '  calculate x coordinate
    Tick(Second).y = SIN(Radian - Radian4) * Radius + SHEIGHT / 2 ' calculate y coodinate
    Second = Second + 1 '                                           increment second counter
NEXT Radian

SCREEN _NEWIMAGE(SWIDTH, SHEIGHT, 32) '                             graphics screen
DO '                                                                begin main loop
    CLS '                                                           clear screen
    _LIMIT 60 '                                                     update frame every 60th of a second
    pSecond = Second '                                              save previous second
    Second = VAL(RIGHT$(TIME$, 2)) '                                get current second
    IF pSecond <> Second THEN '                                     has a second elapsed?          <-- These lines synch the 60th
        Second60 = 0 '                                              yes, reset 60th second timer   <-- second circle to the top
    ELSE '                                                          no, still within same second   <-- of the sweep during second
        Second60 = Second60 + 1 '                                   increment 60th second counter  <-- value changes.
    END IF
    CIRCLE (Tick(Second60).x, Tick(Second60).y), 10 '               draw 60th second sweep
    PAINT (Tick(Second60).x, Tick(Second60).y)
    CIRCLE (Tick(Second).x, Tick(Second).y), 10, CCOLOR '           draw one second sweep
    PAINT (Tick(Second).x, Tick(Second).y), CCOLOR, CCOLOR
    _DISPLAY '                                                      update screen
LOOP UNTIL _KEYDOWN(27) '                                           leave when ESC pressed
SYSTEM '                                                            return to operating system
There are two ways to write error-free programs; only the third one works.
QB64 Tutorial
Reply
#29
I leave for one day and y'all go down a rabbit hole.... Great read. I will have to play more with the EXIT in something simplier before I try to start inserting it into my main project. $NOPREFIX and OPTION EXPLICIT are my first two lines whenever I start a new program for lots of reason discussed. Not only it helps prevent silly errors but keeps from being lazy with my DIM statements.

(06-02-2023, 03:14 PM)bplus Wrote: @NasaCow Search can't find ON EXIT anywhere in your code you posted.

This is what I do find:

Code: (Select All)
'Disabling the default exit routinue
ExitFlag = EXIT
ON TIMER(1) GOSUB ShutDown
TIMER ON


Code: (Select All)
ShutDown:
ExitFlag = EXIT
IF ExitFlag THEN SYSTEM
RETURN

Does not look good to set ExitFlag Twice nor putting Shutdown on Timer.

When you know the user wants to quit from _Exit then you run the save work code if not saved and quit or just quit if work already saved. 

I don't see need for Timer sub here.

This is the example in the Wiki more or less, and where I decided to start to try to learn something new. This the example code for _EXIT (function) - https://qb64phoenix.com/qb64wiki/index.p...(function)

You can see On Exit it isn't used here either but checks every 5 seconds with ON TIMER. The first ExitFlag is used to disable the X and setup the check with the timer. Then we evaluate it every so oftern. I will have to play with ON EXIT and see if I can get that to work (on first try, it was a no go so...)
Code: (Select All)
q = _EXIT 'function read prevents any program exit at start of program
ON TIMER(5) GOSUB quit
TIMER ON
PRINT "  The Timer will check for exit request every 5 seconds."
PRINT "Click the X box and/or Ctrl - Break to see the _EXIT return!"
PRINT "                    Any Key Quits"
PRINT
DO: _LIMIT 30
    '                    ' simulated program loop
LOOP UNTIL INKEY$ <> ""
END

quit:
q = _EXIT
IF q THEN PRINT q;
SELECT CASE q
    CASE 1: PRINT "= X button was clicked"
    CASE 2: PRINT "= Ctrl + Break keypress"
    CASE 3: PRINT "= Both X and Ctrl + Break!"
END SELECT
RETURN

I am all for doing it a different way, just don't know how to go about it since I never tried to control how a program closes before.



Thank you for remembering about me and I don't mind a discussion goes off track as well as helps. I ALWAYS use Option _Explicit in all my programs for that reason alone. I find it lazy programing to not DIM what you need. It makes it so much easier for other people to read the code and know what's going on. Personally, I DIM my throw away variables and give them a name like Counter or CounterX or CounterY, I just find it easy for coding when everything is named in such a way that when I come back a few months later, I still can understand it.
(06-03-2023, 01:29 PM)Dimster Wrote: 1st I apologize to NasaCow, hope the discussion on Option _Explicit may help with the search for/correction of the error you were looking for help on.

2nd ... the issue/learning lesson, for me in this thread is Dimensioned variables v's UnDimensioned variables. In particular a control variable. I have often seen the use of "i" or "j" or "x" being used in many books on Qbasic where these control variables are not Dimmed. They appear to be throw away variables in terms of their use is not material to math formulas or capture of a results of a process. They can be used over and over again as a control variable because their type is defined by default and their value is clearly defined in a range (ie 1 to 10). The only time I will Dim a control variable like "x" is if I need it to carry over into a subroutine or back to the main module.

The Dimming of a control variable, in my experience just added to multiple lines of Dim and Dim Shared in a large program. Plus it doesn't help with the ending value , or limiting the control range. For example 

Code: (Select All)
Dim Shared y(1 To 10)
Dim x

For x = 1 To 10
    Print "Hello World"
Next
Print
Print x
Print
Print
For y = 1 To 10
    Print "Hello back at ya"
Next
Print
Print y

In each case x and y finished outside of the control range, so I have to be careful when I do Dim x or y to use in another sub or function. 

Not sure I see the value in Option _Explicit for control variables, nor how Dimensioning all control variables is a "Best Practice" but this old dog is definitely learning new tricks by trial and error especially when some masterfully programmers are highlighting the new trick.

Thanks for all the suggestions and this became an interesting thread in general!
WHILE NOT EndOfLife(1)
    HappyLife = HappyWife - (Money * Time * Travel * Gifts)
    Kids = (NoTime * LackOfLove) MOD NumOfKids
    IF Retirement <> Rich THEN YearsOnJob = YearsOnJob + 1 ELSE SeeTheWorld
WEND
Reply
#30
@NasaCow
A timer is not needed nor desired! with _Exit checking.

I am pretty sure you have a main menu loop in your program so here is how to check if user pressed an exit sequence or the very common alternate, the escape key:
Code: (Select All)
Dim Shared As Long Saved ' variable that tracks work has been saved

' loadWork
' Saved = -1 ' you are saved right after you load the work data into arrays
' once exit has been called you can't use top right x to quit

' put checkQuitSave in Main loop of your program
Do ' run main menu of program here
    ' show menu
    ' catch user choice
    ' do it
    ' return to menu

    Saved = 0 ' signal we made a change to work we want saved

    ' just to test code to see
    i = i + 1
    Print i
    checkQuitSave
    _Limit 30 ' slow down print numbers
Loop Until _KeyDown(27) ' escape
If Saved = 0 Then SaveWork ' because escape key does not run through checkQuitSave
Print "Goodbye zzz... press any to end" ' signal ending of program
Sleep
End

End

Sub SaveWork
    ' put everything you need to save users work here
    Print "Work has been saved, zzz... press any to continue"
    Sleep
    Saved = -1 ' last line
End Sub

Sub checkQuitSave ' does the user wants to quit
    If _Exit Then ' only when user is tryying to quit with top right click or Ctrl+break whatever break key is stops alt + F4 too
        If Saved = 0 Then
            SaveWork
            Print "Goodbye zzz... press any to end" ' signal ending of program
            Sleep
            End
        End If
    End If
End Sub

Of course you don't have to signal user the work is saved or you could use a message box. This is just roughed out way to use _Exit.

Notice at end, while sleeping the top right X box does not work because _Exit is still forcing a coded exit method, in this case any keypress after Sleep.
b = b + ...
Reply




Users browsing this thread: 60 Guest(s)