Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using 'GoTo' command in delphi?

Tags:

delphi

I read that using the Goto command is very bad and can mess up your code. I also read that there was a poll saying that 40% of Delphi developers will be very cross if they see the goto command and delphi together and the other 40% don't even know of the goto command, why is that?

But anyways, I was making a program that checks if you qualify for a bursary by getting the two marks and getting the avarage of those two marks. In order to get the Bursary, you need to have a 90% average or above and it will display your average in a label and if you qualify in another label. I recently also learned about the If command and now I am just busy playing around with it.

Here's my code:

procedure TForm1.btnCalcClick(Sender: TObject);
var
  iMaths, iScience, iAvarage   :   integer;

begin
  iScience := sedScience.value;
  iMaths   := sedMath.value;
  iAvarage := round((iMaths+iScience)/2);

  if iMaths = 0
    then
      begin
      showmessage ('Sorry, please put a propper value in the Maths and Science box!');
    end;

    if iAvarage >= 90
     then
        begin
          lblAvarage.caption := 'Your avarage is: ' + IntToStr (iAvarage);
          lblOutput.caption := 'You qualify for an Einstein Bursary!';
        end
    else
        begin
          lblAvarage.Caption := 'Your avarage is ' + IntToStr (iAvarage);
          lblOutput.caption := 'Sorry, you do not qualify for an Einstein bursary.';
        end;
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
  begin
    sedMath.value := 0;
    sedScience.value := 0;
    lblAvarage.caption := ' ';
    lblOutput.caption := ' ';
    sedMath.setfocus
  end;

end. 

Where I say if iMaths = 0 thats just making sure that there is a value in the SpinEdit, if there isn't, it must restart at the ButtonClick handler with a message saying Please insert a proper value. That all works fine, but it still displays the average in the labels (lblOutput and lblAvarage) which I don't want it to do!

If I use the goto command, this is how I think it should look:

procedure TForm1.btnCalcClick(Sender: TObject);
var
  iMaths, iScience, iAvarage   :   integer;
label
  lRestart;
begin
  lRestart;
  iScience := sedScience.value;
  iMaths   := sedMath.value;
  iAvarage := round((iMaths+iScience)/2);

  if iMaths and iScience = 0
    then
      begin
      showmessage ('Sorry, please put a propper value in the Maths and Science box!');
      goto lRestart;
    end;

(BTW, I know the above code it wrong, I tried!)

I googled goto but I find things that are different to what I need.

Any help or advice on how to use the goto command would be greatly appreciated!

like image 813
Aidan Quinn Avatar asked Dec 08 '25 22:12

Aidan Quinn


2 Answers

Your syntax is not correct for goto. It should be:

procedure TForm1.btnCalcClick(Sender: TObject);
....
label
  lRestart;
begin
lRestart:
  ....
  goto lRestart;
  ....
end;

But if you ever wanted to do something like this you'd surely avoid the goto by writing it thus:

repeat
  // do something
until OkToContinue;

That said, your code should be:

procedure TForm1.btnCalcClick(Sender: TObject);
var
  iMaths, iScience, iAverage: integer;
begin
  iScience := sedScience.value;
  iMaths   := sedMath.value;
  iAverage := round((iMaths+iScience)/2);

  if (iMaths=0) or (iScience=0) then
  begin
    ShowMessage('Sorry, please put a propper value in the Maths and Science box!');
    exit;
  end;

  // do the calculation
end;

There's no need for looping or goto in your event handler. You have to exit when you find an error and give the user opportunity to fix the mistake and click the button again. So it's just a complete mis-think on your part that you would need a goto.

I guess you have not yet fully grasped the concept of event driven programming. Were you to go back to the beginning instead of exiting the procedure, the user would have no opportunity to modify the values of the spin edit controls and you would show the message again and again and again. Try it and see what I mean.

Note also that I fixed a number of other errors in your code.


As for goto, I'm sure that you won't need it ever. I've only ever found goto to be useful in languages that don't support structured exceptions. Delphi does not fall into that camp.

like image 136
David Heffernan Avatar answered Dec 11 '25 14:12

David Heffernan


You should not need to use goto in your programs.

Some reasons not to use it, since in most cases:

  • It will make your code less readable;
  • It can be easily converted into nested repeat .. until or while .. structures, which are usually with the same exact timing (will be compiled as assembler jmp .. opcode.

Sometimes, it may be slightly faster to put the conditional expression of the repeat .. until or while .. structures within the loop, and use break and continue and repeat .. until false or while true do ..:

function GotoEndOfJSONString(P: PUTF8Char): PUTF8Char;
begin // P^='"' at function call
  inc(P);
  repeat
    if P^=#0 then
      break else
    if P^<>'\' then
      if P^<>'"' then
        inc(P) else
        break else
      inc(P,2);
  until false;
  result := P;
end; // P^='"' at function return

Which will be compiled with very optimized assembler:

    inc eax
@s: mov dl,[eax]
    test dl,dl
    jz @e
    cmp dl,$5c
    je @2
    cmp dl,$22
    je @e
    inc eax
    jmp @s
@2: add eax,2
    jmp @s
@e: ret

But this is perhaps worth it only for very low-level code, and will make it less readable. Writing such code is very close to writing directly the assembler code: you know that the break and continue will be converted into direct jmp .. assembler opcodes.

The only case when I use goto is in some well-defined conditions:

  • Identified low-level process of data (e.g. text handling);
  • Only for performance reasons;
  • With proper unit testing (since code is less readable);
  • When branching is needed in-between case .. of inner blocks, or from one loop to another;
  • When I want to avoid calling a sub-procedure in older versions of Delphi (e.g. Delphi 7) - but in modern Delphi, goto can be replaced by an inline local procedure.

Some example:

procedure TTextWriter.AddJSONEscape(P: Pointer; Len: PtrInt);
var c: PtrUInt;
label Esc, nxt;
begin
  if P=nil then exit;
  if Len=0 then
    Len := MaxInt;
  if B>=BEnd then
    Flush;
  repeat
    inc(B);
    // escape chars, according to http://www.ietf.org/rfc/rfc4627.txt
    c := PByte(P)^;
    if c>=32 then begin
      if c in [ord('\'),{ord('/'),}ord('"')] then goto Esc;
      B^ := AnsiChar(c);
nxt:  if Len=1 then
        break;
      dec(Len);
      inc(PByte(P));
      if B<BEnd then
        continue;
      Flush;
    end else
    case c of
    0: begin
      dec(B); break; end;
    8: begin
      c := ord('b'); goto Esc; end;
    9: begin
      c := ord('t'); goto Esc; end;
    $a: begin
      c := ord('n'); goto Esc; end;
    $c: begin
      c := ord('f'); goto Esc; end;
    $d: begin
      c := ord('r');
Esc:  B^ := '\';
      if B>=BEnd then  // inlined: avoid endless loop
        Flush;
      B[1] := AnsiChar(c);
      inc(B);
      goto nxt;
    end;
    else begin // characters below ' ', #7 e.g. -> // 'u0007'
      B^ := '\';
      AddShort('u00');
      Add(HexChars[c shr 4],HexChars[c and $F]);
      goto nxt;
    end;
    end;
  until false;
end;

As a conclusion, outside the FastCode challenge in pure pascal or some low-level code, you should not see any goto any more.

like image 20
Arnaud Bouchez Avatar answered Dec 11 '25 14:12

Arnaud Bouchez



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!