7

When exceptions like EZeroDivide, EOverflow, EMathError... are raised, they appear with a default message.

For example, the following code raises an EZeroDivide with the following message:

Floating point division by zero

procedure TForm1.Button1Click(Sender: TObject);
var
  N : Extended;
  D : Extended;
begin
  N := 100;
  D := 0;
  Caption := FloatToStr(N/D);
end;

EZeroDivide Floating point division by zero

When I "manually" raise an EZeroDivide exception by code, I have to pass the Msg parameter to the constructor and it is raised as an EZeroDivide exception which have an empty string message:

procedure TForm1.Button2Click(Sender: TObject);
begin
  raise EZeroDivide.Create('');
end;

EZeroDivide without message

Where does default exception messages come from?

Fabrizio
  • 7,603
  • 6
  • 44
  • 104
  • 2
    The message is defined as resource string SZeroDivide in System.SysConst.pas which is part of the RTL source. – Brian Feb 20 '19 at 18:36
  • 1
    The message displayed is defined in SysConst.pas as `SZeroDivide`, which the RTL uses when it raises that exception. – Ken White Feb 20 '19 at 18:38
  • While this question is trivial to answer, we're left wondering why you care? Merely curiosity, or is there some purpose? Typically you should never need to create and raise your own `EZeroDivide`. Why are you doing this? – J... Feb 20 '19 at 18:40
  • Use `system.Error(reZeroDivide)` to raise one. [link](http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.Error), [link](http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.TRuntimeError). – Sertac Akyuz Feb 20 '19 at 18:41
  • @J...: It's for mere curiosity, I was just wondering how Delphi's fathers have chosen to implement exceptions raising. And maybe it could inspire me for implementing something like that – Fabrizio Feb 21 '19 at 07:42
  • 1
    @Fabrizio If curiosity, [this question](https://stackoverflow.com/q/41248963/327083) is loosely related. – J... Feb 21 '19 at 12:27

1 Answers1

12

Those exception instances are generated internally by the RTL. The specific string in the question can be found in the resourcestring section of SysConst.pas

 SZeroDivide = 'Floating point division by zero';

Internally the RTL uses the Error method to raise such exceptions. The full list of runtime errors is defined in the System unit in the following enum :

TRuntimeError = (reNone, reOutOfMemory, reInvalidPtr, reDivByZero,
  reRangeError, reIntOverflow, reInvalidOp, reZeroDivide, reOverflow,
  reUnderflow, reInvalidCast, reAccessViolation, rePrivInstruction,
  reControlBreak, reStackOverflow,
  { reVar* used in Variants.pas }
  reVarTypeCast, reVarInvalidOp,
  reVarDispatch, reVarArrayCreate, reVarNotArray, reVarArrayBounds,
  reAssertionFailed,
  reExternalException, { not used here; in SysUtils }
  reIntfCastError, reSafeCallError,
  reMonitorNotLocked, reNoMonitorSupport
{$IF defined(LINUX) or defined(MACOS) or defined(ANDROID)}
  , reQuit
{$ENDIF LINUX or MACOS or ANDROID}
{$IFDEF POSIX}
  , reCodesetConversion
{$ENDIF POSIX}
  , rePlatformNotImplemented, reObjectDisposed
  );

If you have a genuine reason for raising a runtime error yourself you can do it by calling, for example :

 System.Error(reZeroDivide);

If you're very observant, you'll notice that Error does not have a forward declaration in the System unit but exists only in the implementation section. Typically this would prevent you from having visibility to call the method outside of its containing unit, but the System unit is special in many ways and this method is accessible globally. Just beware that you will also often be including the Windows unit and this declares a const named ERROR which will usually hide System.Error, so scoping it explicitly is usually required.

J...
  • 30,968
  • 6
  • 66
  • 143