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.