Recent

Author Topic: Custom Exception Class  (Read 1933 times)

nikel

  • Full Member
  • ***
  • Posts: 152
Custom Exception Class
« on: July 23, 2021, 05:26:48 am »
Hello, I'm trying to create a custom exception class. Here's my code so far:

Code: Pascal  [Select][+][-]
  1. unit CustomException;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. type
  11.  
  12.   {CustomException}
  13.  
  14.   TFileNotFound=class
  15.   private
  16.     FFile: String;
  17.   public
  18.     property F: String read FFile write FFile;
  19.     procedure PrintError;
  20.   end;
  21.  
  22.   TCannotReadFile=class
  23.   private
  24.     FFile: String;
  25.   public
  26.     property F: String read FFile write FFile;
  27.     procedure PrintError;
  28.   end;
  29.  
  30.   TCannotAccessDirectory=class
  31.   private
  32.     FDirectory: String;
  33.   public
  34.     property D: String read FDirectory write FDirectory;
  35.     procedure PrintError;
  36.   end;
  37.  
  38.   TExternalProgramError=class
  39.   private
  40.     FProgramName: ShortString;
  41.   public
  42.     property ProgramName: ShortString read FProgramName write FProgramName;
  43.     procedure PrintError;
  44.   end;
  45.  
  46. implementation
  47.  
  48. procedure TFileNotFound.PrintError;
  49. begin
  50.   WriteLn('File not found: ' + FFile);
  51. end;
  52.  
  53. procedure TCannotReadFile.PrintError;
  54. begin
  55.   WriteLn('Can not read file: ' + FFile);
  56. end;
  57.  
  58. procedure TCannotAccessDirectory.PrintError;
  59. begin
  60.   WriteLn('Can not access directory: ' + FDirectory);
  61. end;
  62.  
  63. procedure TExternalProgramError.PrintError;
  64. begin
  65.   WriteLn('Can not process external program: ' + FProgramName);
  66. end;
  67.  
  68. end.

Am I doing it right? What's the correct way of creating an exception class?

« Last Edit: July 23, 2021, 05:29:18 am by nikel »

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Custom Exception Class
« Reply #1 on: July 23, 2021, 06:34:00 am »
You should start by deriving your classes from the base class Exception and, preferred, name them starting with an "E" rather than a "T". Since all your exception are basically the same you should create a base class and derive the rest from them:
Code: Pascal  [Select][+][-]
  1. type
  2.   EFileException = class(Exception)
  3.   private
  4.     FFilename: String;
  5.   public
  6.     property Filename: String read FFilename write FFilename;
  7.     procedure PrintError;
  8.   end;

Now, rather than PrintError you should override the base method ToString and, while you're at it, set a new constructor to be able to set the filename at creation time:
Code: Pascal  [Select][+][-]
  1. type
  2.   EFileException = class(Exception)
  3.   private
  4.     FFilename: String;
  5.   public
  6.     constructor CreateWithName(const msg: string; const AName: String);
  7.     function ToString : String; override;  
  8.     property Filename: String read FFilename write FFilename;
  9.   end;
And now comes the implementation:
Code: Pascal  [Select][+][-]
  1. { EFileException }
  2.  
  3. constructor EFileException.CreateWithName(const msg: string;
  4.   const AName: String);
  5. begin
  6.   inherited Create(msg);
  7.   FFilename := AFilename;
  8. end;
  9.  
  10. function EFileException.ToString: String;
  11. begin
  12.   Result := ClassName + ': ' + FFilename + LineEnding + Message;
  13. end;

Unless you really want (or need) to, don't use constants to explain what the exception is about; instead, since it's used in ToString, let the exception name "explain" itself: for example it's quite clear what EFileNotFound means. That also you to add the user message without cluttering too much the display and, incidentally, scaring the user ;)

Note also that using that base class "as is" you don't have to declare anything else for the derived classes; just doing, e.g.
Code: Pascal  [Select][+][-]
  1. ECannotReadFile=class(EFileException);
takes care of all, since all have the same basic functionality and using ClassName in the method ToString takes care of diferentiating.

And that's all that comes to mind, though I'm sure I've left out some details :-[
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

nikel

  • Full Member
  • ***
  • Posts: 152
Re: Custom Exception Class
« Reply #2 on: July 23, 2021, 06:41:47 am »
I'm going to try these. Thanks for the detailed reply.

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Custom Exception Class
« Reply #3 on: July 23, 2021, 06:50:21 am »
I'm going to try these. Thanks for the detailed reply.

No thanks needed; I couldn't sleep and had nothing better to do :D
Tell me whether it all works for you or you have any doubt.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

nikel

  • Full Member
  • ***
  • Posts: 152
Re: Custom Exception Class
« Reply #4 on: July 23, 2021, 08:41:41 am »
I'm not goot at oop this gave me error.

Code: Pascal  [Select][+][-]
  1. unit CustomException;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. type
  11.   { EFileException }
  12.   EFileException=class(Exception)
  13.   private
  14.     FFileName: String;
  15.   public
  16.     constructor CreateWithName(const msg: String; const AName: String);
  17.     function ToString : String; override;
  18.     property FileName: String read FFileName write FFileName;
  19.   end;
  20.  
  21. implementation
  22.  
  23. [b]constructor EFileException.CreateWithName(const msg: String; AName: String);[/b]
  24. begin
  25.   inherited Create(msg);
  26.   FFileName:=AFileName;
  27. end;
  28.  
  29. function EFileException.ToString: String;
  30. begin
  31.   Result:=ClassName + ': ' + FFileName + LineEnding + Message;
  32. end;
  33.  
  34. end.

I got errors and I won't deal with this for some time.
Quote
Compile Project, Target: playlistprogram: Exit code 1, Errors: 3
customexception.pas(23,28) Error: function header doesn't match the previous declaration "constructor CreateWithName(const AnsiString;const AnsiString);"
customexception.pas(16,17) Error: Found declaration: constructor CreateWithName(const AnsiString;const AnsiString);
customexception.pas(26,14) Error: Identifier not found "AFileName"


Mimmo

  • New Member
  • *
  • Posts: 11
Re: Custom Exception Class
« Reply #5 on: July 23, 2021, 09:12:20 am »
Try to add const to the second parameter:


constructor EFileException.CreateWithName(const msg: String; const AName: String);

PascalDragon

  • Hero Member
  • *****
  • Posts: 3490
  • Compiler Developer
Re: Custom Exception Class
« Reply #6 on: July 23, 2021, 10:34:05 am »
Unless you really want (or need) to, don't use constants to explain what the exception is about; instead, since it's used in ToString, let the exception name "explain" itself: for example it's quite clear what EFileNotFound means. That also you to add the user message without cluttering too much the display and, incidentally, scaring the user ;)

One can argue that an identifier like EFileNotFound is just as cryptic for a user as an error code no matter what we developers think. In my opinion a nice exception message in prose without too many technical details (or if at all in a "Details" area) is better for users.

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Custom Exception Class
« Reply #7 on: July 23, 2021, 12:01:31 pm »
I'm not goot at oop this gave me error.
[...etc...]

Ah, no, it's not you; it was I, who copied code without testing it first ... :-[

It should be:
Code: Pascal  [Select][+][-]
  1. constructor EFileException.CreateWithName(const msg: String; const AName: String);
  2. begin
  3.   inherited Create(msg);
  4.   FFileName:=AName;
  5. end;
Thus it should work (or at least, compile). Again, sorry :-[

One can argue that an identifier like EFileNotFound is just as cryptic for a user as an error code no matter what we developers think. In my opinion a nice exception message in prose without too many technical details (or if at all in a "Details" area) is better for users.

Well, yes, maybe but:
1) That's how exceptions are build by default in the RTL; and
2) One might argue that exceptions are ... well, exceptional, so the user shoudn't see them unless something were very wrong with the program, in which case the message is not really meant for the user but for the developer. Otherwise, the developer would catch the exception to e.g. show a more user friendly message like: "Dear sir, the file you asked for doesn't seem to exist; are you sure that was the one wanted?" ;)

Remember that an exception class is meant to be used by a developer and there is always the possibility of setting a nicer message in the constructor.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

PascalDragon

  • Hero Member
  • *****
  • Posts: 3490
  • Compiler Developer
Re: Custom Exception Class
« Reply #8 on: July 23, 2021, 12:39:49 pm »
One can argue that an identifier like EFileNotFound is just as cryptic for a user as an error code no matter what we developers think. In my opinion a nice exception message in prose without too many technical details (or if at all in a "Details" area) is better for users.

Well, yes, maybe but:
1) That's how exceptions are build by default in the RTL; and
2) One might argue that exceptions are ... well, exceptional, so the user shoudn't see them unless something were very wrong with the program, in which case the message is not really meant for the user but for the developer. Otherwise, the developer would catch the exception to e.g. show a more user friendly message like: "Dear sir, the file you asked for doesn't seem to exist; are you sure that was the one wanted?" ;)

Remember that an exception class is meant to be used by a developer and there is always the possibility of setting a nicer message in the constructor.

While the exception classes of the RTL are indeed built like this, this does not mean that it's good. Exception.ToString is implemented like that, cause that's how Delphi does it.

And while I agree that exceptions should be exceptional I don't agree that they're not supposed to be for the user. Take the EFileNotFound example here: the user enters a path to a file that does not exists and clicks continue (or whatever). Now the code will trigger an EFileNotFound and thus display a dialog with the error message. It's much nicer if this message only displays the information that the file does not exist (including the offending filename) than also printing the name of the exception class.

 

TinyPortal © 2005-2018