Jump to content

Since "Goto" is "Evil"


texan
 Share

Recommended Posts

Since AutoIt does not have a Goto statement (It is "Evil" according to FAQ), then how would a good programmer code the following.

I have a function that opens files and create objects during normal execution of the function. There are various locations in the function in which I may encounter an error that prevents me from successfully completing the function. When I encounter these errors I normally would "goto" some cleanup code at the end of the function. The cleanup code would make sure I closed all files and killed any objects.

How do I properly do this without Goto? I don't want to include 10 lines of cleanup code in every location I check for errors.

I am admittedly a little bit of an 'old school' programmer at times and I don't think the Goto is "evil" but I have worked with some older school programmers that can miss use it.

Edited by texan
Link to comment
Share on other sites

Just put whatever is in your goto code inside a function.

;Doing stuff

;Doing stuff

;Oh crap thats not good

cleanup()

Func cleanup()

;remove objects

EndFunc

Good answer...

here is a small example

; COM Error Handler example
; ------------------------- 

$oIE=ObjCreate("InternetExplorer.Application.1")    ; Create Internet Explorer application
$oMyError = ObjEvent("AutoIt.Error","MyErrFunc")    ; Initialize a COM error handler

$oIE.UnknownMethod      ; Deliberately call an undefined method

If @error then
  Msgbox (0,"AutoItCOM test","Test passed: We got an error number: " & @error)
  Clean_Up($oIE)
Else
  Msgbox (0,"AutoItCOM test","Test failed!")
Endif

Exit

; This is my custom defined error handler
Func MyErrFunc()

  Msgbox(0,"AutoItCOM Test","We intercepted a COM Error !"      & @CRLF  & @CRLF & _
             "err.description is: "    & @TAB & $oMyError.description    & @CRLF & _
             "err.windescription:"     & @TAB & $oMyError.windescription & @CRLF & _
             "err.number is: "         & @TAB & hex($oMyError.number,8)  & @CRLF & _
             "err.lastdllerror is: "   & @TAB & $oMyError.lastdllerror   & @CRLF & _
             "err.scriptline is: "     & @TAB & $oMyError.scriptline     & @CRLF & _
             "err.source is: "         & @TAB & $oMyError.source         & @CRLF & _
             "err.helpfile is: "       & @TAB & $oMyError.helpfile       & @CRLF & _
             "err.helpcontext is: "    & @TAB & $oMyError.helpcontext _
            )
            
    Local $err = $oMyError.number
    If $err = 0 Then $err = -1
    
    SetError($err)  ; to check for after this function returns
Endfunc

Func Clean_Up(ByRef $Input)
    $Input = ""
    Msgbox (0,"Clean Up","The Object has been Cleaned     " & @CRLF & IsObj($Input), 3)
    ; do something else ??
EndFunc

there are MANY answers to your question

8)

NEWHeader1.png

Link to comment
Share on other sites

Since AutoIt does not have a Goto statement (It is "Evil" according to FAQ), then how would a good programmer code the following.

I have a function that opens files and create objects during normal execution of the function. There are various locations in the function in which I may encounter an error that prevents me from successfully completing the function. When I encounter these errors I normally would "goto" some cleanup code at the end of the function. The cleanup code would make sure I closed all files and killed any objects.

How do I properly do this without Goto? I don't want to include 10 lines of cleanup code in every location I check for errors.

I am admittedly a little bit of an 'old school' programmer at times and I don't think the Goto is "evil" but I have worked with some older school programmers that can miss use it.

Your error handler would be a function. Then, when the error was detected, you would call the handler - then return:

_DoMyStuff()

Func _DoMyStuff()
    Local $sSource = "C:\Temp\Test1.txt"
    Local $sDestination = "C:\Temp\Test2.txt"
    If FileExists($sDestination) Then
        If Not FileCopy($sDestination, $sDestination & ".BAK", 1) Then
            _FileCopyError($sDestination, $sDestination & ".BAK")
            Return SetError(1, 1, 0)
        EndIf
    EndIf
    If Not FileCopy($sSource, $sDestination) Then
        _FileCopyError($sSource, $sDestination)
        Return SetError(1, 2, 0)
    EndIf
EndFunc   ;==>_DoMyStuff

Func _FileCopyError($sSrc, $sDest)
    FileDelete($sDest)
    MsgBox(16, "Error", "An error occured while copying:" & @CRLF & _
            "From:  " & $sSrc & @CRLF & _
            "To:  " & $sDest & @CRLF & _
            "Destination file deleted.")
    Return
EndFunc   ;==>_FileCopyError

And keep your list current.

Evil: Goto, Assign, and Eval

Like loud song in the Mead Hall, too much whining about GOTO will bring The Grendel Valik out of his cave. So beware your careless ways...

:)

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

Yeah, I figured the answer was to use a function. I guess in my old school way of thinking, using a function requires more code so I don't like it.

Instead of this:

If ErrorCondition Then

Goto CleanupCode

EndIf

Now I must use this:

If ErrorCondition Then

Call CleanupCode()

Exit Function

EndIf

there are MANY answers to your question

What other answers are there?

Link to comment
Share on other sites

Yeah, I figured the answer was to use a function. I guess in my old school way of thinking, using a function requires more code so I don't like it.

Instead of this:

If ErrorCondition Then

Goto CleanupCode

EndIf

Now I must use this:

If ErrorCondition Then

Call CleanupCode()

Exit Function

EndIf

What other answers are there?

Not even that much code is needed.

If NOT FileExists ( "XYZ" ) Then Cleanup()

Func Cleanup()

EndFunc

There is no need to "Call" or "Exit" Function

Link to comment
Share on other sites

Not even that much code is needed.

If NOT FileExists ( "XYZ" ) Then Cleanup()

Func Cleanup()

EndFunc

There is no need to "Call" or "Exit" Function

Doesn't exit the function that had the error, which I thought he wanted.

Like if the error happens on the third of five steps in a function, do cleanup and then exit the function.

I could be wrong.

:)

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

Doesn't exit the function that had the error, which I thought he wanted.

Like if the error happens on the third of five steps in a function, do cleanup and then exit the function.

I could be wrong.

:)

I would think the OP needed a registered error handler due to working with objects

... Call() is evil too.... ^_^

8)

NEWHeader1.png

Link to comment
Share on other sites

Instead of this:

If ErrorCondition Then

Goto CleanupCode

EndIf

Now I must use this:

If ErrorCondition Then

Call CleanupCode()

Exit Function

EndIf

Could even do it like this:

If ErrorCondition Then
    Exit CleanupCode()
EndIf

Func CleanupCode()
   ; Do your cleanup stuff here
EndFunc

So it's really only one more line (EndFunc). Alternatively, you can use multiple functions with Return statements to simulate Goto.

Link to comment
Share on other sites

What other answers are there?

Well, I think you have been shown a few... hope one is right for your use!

...and I made a Valik funny, and nobody noticed. Hrumph!

^_^

I noticed but, I figured if you want to go out on a limb talking about Valik... You're on your own!

... :)

8)

Edited by Valuater

NEWHeader1.png

Link to comment
Share on other sites

Could even do it like this:

If ErrorCondition Then
    Exit CleanupCode()
EndIf

Func CleanupCode()
  ; Do your cleanup stuff here
EndFunc

So it's really only one more line (EndFunc). Alternatively, you can use multiple functions with Return statements to simulate Goto.

Oh, I like that! Here's my version:

_DoMyStuff()

Func _DoMyStuff()
    Local $sSource = "C:\Temp\Test1.txt"
    Local $sDestination = "C:\Temp\Test2.txt"
    Local $sBak = $sDestination & ".BAK"
    If FileExists($sDestination) Then
        If Not FileCopy($sDestination, $sBak, 1) Then Return SetError(1, _FileCopyError($sDestination, $sBak), 0)
    EndIf
    If Not FileCopy($sSource, $sDestination) Then Return SetError(1, _FileCopyError($sSource, $sDestination), 0)
EndFunc   ;==>_DoMyStuff

Func _FileCopyError($sSrc, $sDest)
    FileDelete($sDest)
    MsgBox(16, "Error", "An error occured while copying:" & @CRLF & _
            "From:  " & $sSrc & @CRLF & _
            "To:  " & $sDest & @CRLF & _
            "Destination file deleted.")
    Return
EndFunc   ;==>_FileCopyError

:)

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

Hi,

I think a good way can be also:

Opt("OnExitFunc", "CleanUp")

Global ErrorCondition = 0

; Do whatever that set ErrorCondition if an error is encountered
If ErrorCondition Then Exit

Exit

Func Cleanup()
  If ErrorCondition Then
; Do your cleanup stuff here
  EndIf
EndFunc
Edited by Jango
Link to comment
Share on other sites

Hi,

I think a good way can be also:

Opt("OnExitFunc", "CleanUp")

Global ErrorCondition = 0

; Do whatever that set ErrorCondition if an error is encountered
If ErrorCondition Then Exit

Exit

Func Cleanup()
  If ErrorCondition Then
; Do your cleanup stuff here
  EndIf
EndFunc
Thanks for all of the good ideas. I think this OnExitFunc method is my favorite and I did not know this feature existed or I would have used it to begin with.

Historically, when I code any function, I always had a "cleanup" section at the bottom of the function. If there were no errors in the function you would end up "falling through" the cleanup section. If there were errors, I would "goto" the cleanup section.

For some functions the cleanup section would be empty but I always left it in just because it was my standard method of coding functions.

I really like this OnExitFunc feature because it accomplishs the exact same thing I was always doing with "goto cleanup".

Link to comment
Share on other sites

I'm sure you'll abuse this:

Func MyFunc()
    Local $nError, $nExtended, $vResult
  ; Pseudo Loop
    Do
        ... Stuff
        If @error Then
            $nError = 1
            $vResult = False
            ExitLoop
        EndIf
        ... Stuff
        If @error Then
            $nError = 2
            $vResult = False
            ExitLoop
        EndIf
      ; More stuff, no more errors
        $vResult = True
    Until True
  ; Cleanup
    Return SetError($nError, $nExtended, $vResult)
EndFunc

It implements a Do...Until loop which will only execute once. Since the bulk of the code is contained inside a loop, when you encounter an error and need to go to the cleanup section, set the variables for the returned values and ExitLoop.

I use this when I need to clean up handles or something and there are multiple points of failure. I do not recommend writing every single function this way as the number of functions which require cleanup is few. Your "I always had a cleanup label" you mentioned was just pure laziness.

Edited by Valik
Link to comment
Share on other sites

just some food for thought as an addendum

this post has a code example from ChrisL

GUIRegisterMsg windows message handler for system shutdown message WM_QUERYENDSESSION

if program running during system initiated shutdown, apparently at times OnAutoItExit doesn't run

I use both _ShutdownInitiated and OnAutoItExit

OnAutoItExit() during Shutdown isn't processed

I think $WM_QUERYENDSESSION declaration can be left out if using 3.2.10.0

Global $WM_QUERYENDSESSION = 0x0011

GUICreate("ShutDownNotificationGui")
GUIRegisterMsg($WM_QUERYENDSESSION, "_ShutdownInitiated")
GUISetSTate(@SW_HIDE)

While 1
    sleep(10)
WEnd

Func _ShutdownInitiated($hWndGUI, $MsgID, $WParam, $LParam)
    ; cleanup
    ProcessClose("the other process that is relaunching this app.exe");or the net stop thing you posted above
    RunWait("net stop Login-restarter","",@SW_HIDE)
    Return True ; reports back to system
EndFunc

I see fascists...

Link to comment
Share on other sites

How do I properly do this without Goto? I don't want to include 10 lines of cleanup code in every location I check for errors.

My way of coding this is to put the procedure into a function, and use a Return($value) statement to break-out of the function if anything goes wrong. You can of course have as many Return() statements as you like.

Back in the main prog, we examine the returned value to see if the process succeeded or not, and if not, where it failed. We then run the cleanup routine based on this info. Only possible issue is that the cleanup routine will not have access to any private values which the function held. Whether that matters depends on the situation.

e.g.

func DoWhateverJob()
; Perform stage 1
 if <error occurred> then Return 1  
; Perform stage 2
 if <error occurred> then Return 2  
; Perform stage 3
 if <error occurred> then Return 3  
 Return 0
endfunc

; main prog: 
$returncode=DoWhateverJob()
if $returncode > 0 then 
 msgbox(0, "Error" , "Procedure failed at stage " & $returncode)
; do cleanup here
endif

HST, I reckon we should campaign for the Fortran multiple-calculated-value GOTO statement. :)^_^:)

Edited by Anteaus
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...