Jump to content

Recommended Posts

I was sure I got rid of all Goto usage in my code. But I found this example today :

 

for i := 0 to Length(vArray) - 1 do
begin
  if vArray[i].SkipThisLine then
    goto labelSkipThisLine;

  ...
  ...

  labelSkipThisLine: // no code, do nothing with this data line
end;

This is probably close to 20 years old code, and never failed!

 

I almost want to leave it there, just as a reminder it took me almost 20 years to get past the beginner level 🙂 

 

Edited by Mike Torrettinni
  • Thanks 2

Share this post


Link to post

I actually used a goto for debugging today. The reason was that at the end of a function I check the result and for a particular result I want to repeat the code in the function in order to step through it:

function whatever: SomeType;
label
  RepeatMe;
begin
RepeatMe:
  // some code that generates the result
  if Result = ResultIWantToDebug then
    goto RepeatMe; // <== put a breakpoint here
  // some more code
 end;

 

  • Like 3

Share this post


Link to post

If the code still makes sense to you years latter and you can follow it without difficulty then there should be no complaints.   

 

Applying a rule or guideline too rigidly before the skill or knowledge to do so has been acquired often makes things worse. In your example learning how to use continue; or break; to control the flow of a for loop would be best but applying a no goto rule and using multiple cascading IF's around a large blocks of code could be worse than just using a goto

 

The top hall of shame code I have ever seen was due to developers rigidly applying rules before the skill and knowledge to do so was acquired. It made the code more complex and a lot harder to follow to the point is was often easier to just re-write it from scratch. 

  • Like 4

Share this post


Link to post
29 minutes ago, dummzeuch said:

The reason was that at the end of a function I check the result and for a particular result I want to repeat the code in the function in order to step through it

 

That's pretty cool idea, avoiding exiting and running project again.

Edited by Mike Torrettinni

Share this post


Link to post
4 hours ago, dummzeuch said:

I actually used a goto for debugging today. The reason was that at the end of a function I check the result and for a particular result I want to repeat the code in the function in order to step through it

I would have just wrapped the logic in a loop instead:

function whatever: SomeType;
begin
  repeat
    // some code that generates the result
  until Result <> ResultIWantToDebug;
end;

Or, renamed the original function and introduced a new function with the original name to call the new function in a loop:

function actual_whatever: SomeType;
begin
  // some code that generates the result
end;

function whatever: SomeType;
begin
  repeat
    // some code that generates the result
  until Result <> ResultIWantToDebug;
end;

 

Edited by Remy Lebeau

Share this post


Link to post

No, a loop wouldn't have worked there. It's a function that is called multiple (many) times from several places, one of these calls (I didn't even know which one) was creating the result I was interested in.

 

A wrapper function world also have worked, something like:

function whatever: SomeType;
  
  /// this is the original function
  function innerwhatever: SomeType;
  begin
    // some code that generates the result
  end;

// the wrapper function calls it
begin
  Result := innerwhatever;
  if Result = ResultIWantToDebug then
    Result := innerwhatever; // <== put a breakpoint here
end;

I probably should have mentioned that the function has parameters which I omitted in the example because it didn't matter.

Edited by dummzeuch

Share this post


Link to post
On 11/4/2022 at 9:08 PM, dummzeuch said:

I actually used a goto for debugging today. The reason was that at the end of a function I check the result and for a particular result I want to repeat the code in the function in order to step through it:

Interesting application; however, I'd set a breakpoint on a line after condition (the line itself could be totally useless, like "gettickcount;" or similar) and use great IDE feature "Set next statement" feature

Share this post


Link to post
1 hour ago, Fr0sT.Brutal said:

Interesting application; however, I'd set a breakpoint on a line after condition (the line itself could be totally useless, like "gettickcount;" or similar) and use great IDE feature "Set next statement" feature

I usually do the same, with an

asm nop end;

on that line.

 

Unfortunately that Set next statement feature doesn't always work as expected. Sometimes the stack gets corrupted (at least that's what I think what happens). That's why I had to resort to a different approach here.

 

Also, that feature was introduced with Delphi 2007 (or was it 2006?) so for earlier versions you had to change the Program Counter in the CPU window (Yeah, I know, nobody cares any more about these ancient versions. I'm the only freak who still sometimes uses them.).

 

Share this post


Link to post
46 minutes ago, dummzeuch said:

Yeah, I know, nobody cares any more about these ancient versions. I'm the only freak who still sometimes uses them

You underestimate how ancient Delphi legacy is 🙂 I have 2 alive projects written in D7

  • Like 1

Share this post


Link to post

Another one from the way back:

 

this was a long function (500+ loc) and to skip all the unnecessary code and exit on first possible condition, I used ToExit label

function Process: boolean;
var vList: TStringList;
label ToExit;
begin
  Result := false;  
  vList := TStringList.Create; 
  ...
  // for fast execution, exit asap when condition is right
  if condition_to_exit then
  begin
    Result := True;
    goto ToExit;
  end;
  ...

  ToExit:
  FreeAndNil(vList);
end;

 

So many mistakes here, but It's been working 15+ years.  Ready for a makeover!

 

 

Share this post


Link to post
7 hours ago, Mike Torrettinni said:

Another one from the way back:

 

this was a long function (500+ loc) and to skip all the unnecessary code and exit on first possible condition, I used ToExit label


function Process: boolean;
var vList: TStringList;
label ToExit;
begin
  Result := false;  
  vList := TStringList.Create; 
  ...
  // for fast execution, exit asap when condition is right
  if condition_to_exit then
  begin
    Result := True;
    goto ToExit;
  end;
  ...

  ToExit:
  FreeAndNil(vList);
end;

 

So many mistakes here, but It's been working 15+ years.  Ready for a makeover!

 

 

I guess you know to use try/finally for this 

  • Like 1

Share this post


Link to post
57 minutes ago, David Heffernan said:

I guess you know to use try/finally for this 

Yes, I do today, that code is part of my 'learn to code just enough to sell' time, so the focus was not on learning to how to do things right.

Share this post


Link to post

In most cases try-finally is OK but for time-critical functions try block in Delphi is damn slow. That's where goto's remain the only option. Or, if possible, inlined sub-functions could help

Edited by Fr0sT.Brutal

Share this post


Link to post
40 minutes ago, Fr0sT.Brutal said:

In most cases try-finally is OK but for time-critical functions try block in Delphi is damn slow. That's where goto's remain the only option. Or, if possible, inlined sub-functions could help

It's fine until you get an exception, which will result in a memory leak.

Share this post


Link to post
3 hours ago, Fr0sT.Brutal said:

In most cases try-finally is OK but for time-critical functions try block in Delphi is damn slow. That's where goto's remain the only option. Or, if possible, inlined sub-functions could help

Really? Which architecture are you talking about?

Share this post


Link to post
6 hours ago, Lajos Juhász said:

It's fine until you get an exception, which will result in a memory leak.

Such critical stuff by definition doesn't deal with memory allocations.

 

3 hours ago, David Heffernan said:

Really? Which architecture are you talking about?

My benchmark shows 97% slowdown on x32 and 86% on x64. However the delays become noticeable after 1.5-2M iterations.

Share this post


Link to post
44 minutes ago, Fr0sT.Brutal said:

Such critical stuff by definition doesn't deal with memory allocations.

 

My benchmark shows 97% slowdown on x32 and 86% on x64. However the delays become noticeable after 1.5-2M iterations.

Presumably it all depends on what is inside the try.

Share this post


Link to post

David, Stefan, I was testing with simple assignment of integer loop variable to another integer. Glad to see "try" has been fixed in 10.4

Share this post


Link to post

I use "break" unstead of "goto" in a loop. With an additionnal boolean to manage double loop.

And I use try finaly to be sure to liberate memory that I have reserved.

Of course, Try finaly must not be used in time critical procedure.

Share this post


Link to post

I have a handful of gotos in my code.  Mostly they have this form

if compare1 then

   begin

       if compare2 then

        ...

       else

        goto label1

  end

else

....

label1:

IOW jumping out of a multilevel IF block is faster than just letting it run its course. It is still active code, but I haven't revalidated the decision in ages (XE 1 or 3 times or so). Which is a problem with all low-level optimizations.

 

The code searches for aggregates in an image (blob) so is quite RDS.
 

Share this post


Link to post

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

×