Recent

Author Topic: Inherited fail  (Read 5028 times)

jollytall

  • Sr. Member
  • ****
  • Posts: 303
Inherited fail
« on: November 30, 2021, 06:24:21 pm »
Classes have a rarely used but nice feature in their constructor: fail. If anywhere in the constructor fail is called it means an immediate exit AND the fact that the class instance is not created and the return value is set to nil.
So if I have:
Code: Pascal  [Select][+][-]
  1. MyClass := tMyClass.Create; // exit with a fail
  2. if not assigned(MyClass) then
  3.   writeln('Instance not created');
it works as it should.

Now I have:
Code: Pascal  [Select][+][-]
  1. constructor tMyClass2;
  2.   begin
  3.   inherited Create; // this one exits with a fail
  4.   // How can I catch it here????
  5.   end;
In my tests I found that (a) the constructor of tMyClass exits with fail all right, (b) the constructor of tMyClass2 does NOT exit immediately (i.e. it does not behave like a raised exception that goes up in the calling chain), but continues with the next instruction (c) the Self parameter is not nil after the inherited Create (not even if I explicitly set it to nil in the inherited constructor before fail).

So, I could not figure out if the inherited Create was successful or not. Any solution?

ASerge

  • Hero Member
  • *****
  • Posts: 2212
Re: Inherited fail
« Reply #1 on: November 30, 2021, 07:59:21 pm »
So, I could not figure out if the inherited Create was successful or not. Any solution?
Use exceptions.

1. The Fail procedure in Delphi is valid only for old objects (in FPC also for classes).
2. The Fail procedure fails %) with inheritance in FPC. Example:
Code: Pascal  [Select][+][-]
  1. {$IFDEF FPC}
  2.   {$MODE OBJFPC}
  3.   {$LONGSTRINGS ON}
  4. {$ENDIF}
  5. {$APPTYPE CONSOLE}
  6.  
  7. uses SysUtils;
  8.  
  9. type
  10.   PBase = ^TBase;
  11.   TBase = object
  12.     constructor Init;
  13.     destructor Done;
  14.   end;
  15.  
  16.   PDescendant = ^TDescendant;
  17.   TDescendant = object(TBase)
  18.     constructor Init;
  19.   end;
  20.  
  21. constructor TDescendant.Init;
  22. begin
  23.   Writeln('  begin TDescendant.Create');
  24.   inherited;
  25.   Writeln('  TDescendant.Create end');
  26. end;
  27.  
  28. constructor TBase.Init;
  29. begin
  30.   Writeln('    begin TBase.Create');
  31.   Fail;
  32.   Writeln('    TBase.Create end');
  33. end;
  34.  
  35. destructor TBase.Done;
  36. begin
  37.   Writeln('  TBase.Done');
  38. end;
  39.  
  40. procedure Test;
  41. var
  42.   P: PBase;
  43. begin
  44.   WriteLn('begin');
  45.   P := New(PDescendant, Init);
  46.   if P = nil then
  47.     Writeln('  P is nil')
  48.   else
  49.     try
  50.       Writeln('  P is not nil!');
  51.     finally
  52.       Dispose(P, Done);
  53.     end;
  54.   WriteLn('end');
  55. end;
  56.  
  57. begin
  58.   Test;
  59.   Readln;
  60. end.

In FPC:
Quote
begin
  begin TDescendant.Create
    begin TBase.Create
  TDescendant.Create end
  P is not nil!
  TBase.Done
end

In Delphi:
Quote
begin
  begin TDescendant.Create
    begin TBase.Create
  TDescendant.Create end
  P is nil
end

jollytall

  • Sr. Member
  • ****
  • Posts: 303
Re: Inherited fail
« Reply #2 on: November 30, 2021, 08:56:21 pm »
 @jamie: I am not sure I understand your reply. As ASerge details, base classes (and base objects) can have Create and it can be inherited. Self is indeed set, but that is actually the problem.

@ASerge: Thanks for the detailed answer.
Exceptions: Yes i can use that, but I can also have a boolean field to check, but I thought that fail is so elegant.

1. Fail in Classes: It works in FPC even in mode delphi. If it does not work in "real" Delphi, it is a bit of compatibility issue, although to the right direction (more allowed).

2. If Fail fails, isn't it a failure (OK, a bug, but could not resist, sorry)? However i do not know what would the correct way be to work. As your example shows even the Delphi way is wrong. 'TDescendant.Create end' is reached, meaning that code after the inherited line is executed, while the pointer is probably already zero. I cannot check (I do not use Windows / Delphi), but I can imagine that as the pointer is nil, any access of fields there is a GP fault. The correct probably would be that not even 'TDescendant.Create end' is reached, like with exceptions.

Last, but not least, your Delphi approach is not the same in FPC Delphi mode even for old objects (another small compatibility difference).

ASerge

  • Hero Member
  • *****
  • Posts: 2212
Re: Inherited fail
« Reply #3 on: December 01, 2021, 11:02:58 pm »
Sorry, my example was incorrect. The object size is zero, so Delphi always returns nil in this case (unlike FPC). If you add some field, the behavior of Delphi will be the same as FPC. Fail will work only for one level of inheritance: for PBase, but not for PDescendant.

 

TinyPortal © 2005-2018