For the demonstrated purpose setjmp()-longjmp() should be better, because it saves/restores registers and gives a return value.
non-local-gotos are implemented using setjmp/longjmp.
I have never tried nonlocal gotos. Theoretically these could be used to jump into a loop or into a procedure, which is a horrible idea for me.
With
setjmp/
longjmp you can do that as well, but unlike with non-local-gotos the compiler can't forbid critical uses, because these two functions are just ordinary functions and the compiler does not know what they do.
With setjmp/longjmp you must initialize jmp_buf, which stores the location and registers and you can return and you get a return value. You can only jump back to a location, where you have been before you cannot jump to arbitrary locations.
This is very different.
Of course you can use
setjmp/
longjmp to jump from one function to another, that's what makes them so dangerous:
program tsetjmp;
var
buf: jmp_buf;
procedure Test;
begin
Writeln('Setting jmp_buf');
if setjmp(buf) <> 0 then
Writeln('Jumped')
else
Writeln('No jump');
Writeln('In Test');
end;
procedure Test2;
begin
Writeln('And... Jump!');
LongJmp(buf, 1);
Writeln('This won''t be visible');
end;
begin
Test;
Test2;
end.
This will print:
C:\fpc\git>.\testoutput\tsetjmp.exe
Setting jmp_buf
No jump
In Test
And... Jump!
Jumped
In Test
Experimental code example: Variable "pos" is the jmp_buf, which stores registers and execution point.
while True do
try
while (true) do
begin
if setjmp(pos) > 0 then //"pos" stores registers and execution point here into jmp_buf record.
writeln('Error! Please input integer number!');
readln(N);
...... //code which may raise exception can modify "pos" so
......//the exception always returns to the point where it was raised.
end;
except
on E: EInOutError do
begin
longjmp(pos, 1);
// Writeln(E.ClassName, ': ', E.Message);
end;
end;
Simplifies error handling, if you have multiple "readln" lines which can raise exceptions in the code.
Your code results in a memory leak, because the exception instance won't be freed:
{$mode objfpc}
uses
SysUtils;
var
buf: jmp_buf;
begin
try
if setjmp(buf) <> 0 then begin
Writeln('Jumped');
Exit;
end;
raise Exception.Create('Test');
except
on E: Exception do begin
Writeln('Caught exception, jumping');
longjmp(buf, 1);
end;
end;
end.
Compiled with
-ghlw2 this will be the result:
C:\fpc\git>.\testoutput\texcept.exe
Caught exception, jumping
Jumped
Heap dump by heaptrc unit of C:\fpc\git\testoutput\texcept.exe
97 memory blocks allocated : 3454/3752
95 memory blocks freed : 3390/3688
2 unfreed memory blocks : 64
True heap size : 163840 (160 used in System startup)
True free heap : 163168
Should be : 163232
Call trace for block $0000000000118A40 size 40
$0000000100008BBB
$000000010000E046
$000000010000E4F5
$00007FFECA6323DF
$00007FFECA5E14A4
$00007FFECA5E11F5
$00007FFEC821CF19
$000000010000DEFB
$0000000100001798 main, line 14 of fpctests/texcept.pp
$0000000100001806 main, line 21 of fpctests/texcept.pp
$000000010000DBB0
$0000000100001700
$00007FFEC9BC7614
$00007FFECA5E26A1
$00007FFECA5E26A1
Call trace for block $0000000000118B40 size 24
$0000000100008B42
$000000010000736A
$000000010001693A
$0000000100001786 main, line 14 of fpctests/texcept.pp
$0000000100001806 main, line 21 of fpctests/texcept.pp
$000000010000DBB0
$0000000100001700
$00007FFEC9BC7614
$00007FFECA5E26A1
$000000010001889A
$00000001000080BC
$0000000100001722 main, line 8 of fpctests/texcept.pp
$0000000100001806 main, line 21 of fpctests/texcept.pp
$000000010000DBB0
$0000000100001700
$00007FFEC9BC7614
Line 14 is the line where the exception is raised.
Can you do this with nonlocal goto?
No, because for the above reason the compiler forbids a non-local-goto out of a control-flow statement like a
try … finally- or
try …except-block and thus is in fact
safer than using
setjmp/
longjmp directly (though I would use neither for something like this).
non-local-gotos are implemented using setjmp/longjmp.
What about exceptions? They are also implemented in this way?
At least on targets that don't have their own exception handling mechanism (
i386-win32,
x86_64-win64,
aarch64-win64 and in trunk as an option also some POSIX targets like Linux)
setjmp/
longjmp are used to implement it, however the compiler ensures that they're only used in a correct way (e.g. no jumping to a foreign stack frame).