Exceptions
Exceptions (unit SysUtils) signal errors without return codes.
22.1 try-except
Exceptions (unit SysUtils) signal errors without return codes. With SysUtils in the program, run-time errors (division by zero, range errors, access violations, I/O errors) also become exceptions.
Handlers are checked in order; put specific classes before general ones.
on E: T do binds the exception object; on T do without a variable is allowed.
A bare except ... end (without on) catches everything — use sparingly.
The exception object is freed automatically after the handler finishes.
program ExceptDemo;
{$mode objfpc}{$H+}
uses SysUtils;
var
A, B: Integer;
begin
A := 10;
B := 0;
try
Writeln(A div B);
except
on E: EDivByZero do
Writeln('Division by zero: ', E.Message);
on E: EConvertError do
Writeln('Bad number: ', E.Message);
on E: Exception do
Writeln(E.ClassName, ': ', E.Message); // catch-all for the rest
end;
end.22.2 try-finally
finally code runs whether or not an exception occurs — the standard way to release resources:
Combining both: nest them (Pascal has no single try/except/finally):
var
SL: TStringList;
begin
SL := TStringList.Create; // create BEFORE try
try
SL.LoadFromFile('input.txt');
Writeln(SL.Count, ' lines');
finally
SL.Free; // always runs
end;
end;try
try
DoWork;
finally
Cleanup;
end;
except
on E: Exception do
LogError(E.Message);
end;22.3 Raising exceptions
Re-raising inside a handler: raise; (keeps the original exception object). Abort raises the silent EAbort, which the GUI application loop ignores.
procedure Withdraw(var Balance: Currency; Amount: Currency);
begin
if Amount <= 0 then
raise EArgumentException.Create('Amount must be positive');
if Amount > Balance then
raise Exception.CreateFmt('Insufficient funds: balance %.2f, requested %.2f',
[Balance, Amount]);
Balance := Balance - Amount;
end;22.4 Custom exception classes
(Class syntax is explained in Chapter 24.)
type
EInsufficientFunds = class(Exception)
private
FShortfall: Currency;
public
constructor Create(AShortfall: Currency);
property Shortfall: Currency read FShortfall;
end;
constructor EInsufficientFunds.Create(AShortfall: Currency);
begin
inherited CreateFmt('Insufficient funds: %.2f missing', [AShortfall]);
FShortfall := AShortfall;
end;
// usage
try
raise EInsufficientFunds.Create(25.50);
except
on E: EInsufficientFunds do
Writeln(E.Message, ' (shortfall = ', E.Shortfall:0:2, ')');
end;22.5 Common exception classes
base class of all standard exceptions StrToInt('abc'), invalid conversions EDivByZero / EZeroDivide integer / floating-point division by zero range check failure with {$R+} overflow with {$Q+} invalid memory access (nil dereference)
I/O errors with {$I+}; ErrorCode field failed as cast invalid pointer operation (double free)
EArgumentException, EArgumentOutOfRangeException ENotImplemented, ENotSupportedException Abort — silent exception EListError, EStringListError invalid list index, duplicates EFOpenError, EFCreateError file stream errors
22.6 Assertions
A failed assertion raises EAssertionFailed (with SysUtils). Disable assertions in release builds.
{$ASSERTIONS ON} // or {$C+}; compile with -Sa in FPC
procedure SetAge(Age: Integer);
begin
Assert((Age >= 0) and (Age < 150), 'Age out of range');
// ...
end;22.7 Guidelines
Use exceptions for exceptional conditions, not normal control flow (use TryStrToInt instead of catching EConvertError in a loop).
Never swallow exceptions silently (except end;) — log at least.
Free resources in finally, not in except.
Raise the most specific class; include context in messages.
In FPC, compile with -gl and use lineinfo to get line numbers in stack traces (DumpExceptionBackTrace). In Delphi, use tools such as madExcept, EurekaLog, or JclDebug for detailed stack traces.