Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2009-06

If an Assert fails in constructor, then destructor called (?)

11 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Tom Verhoeff

While tracing a nasty bug (?), I discovered the hard way that when an Assert is done in a constructor, and it fails, then the destructor (Destroy) is automatically called.

However, because the constructor failed, the object did not satisfy the class invariants.

So, the destructor needs to be pessimistic about the state. Mine was not.

Since the Assert in the constructor is there to protect against parameter values that do not statisfy the constructor's precondition, there are no additional dynamic veriables and objects created, and hence they need not be destroyed.

How can the destructor know that it is called in such a state?

Below is a simple example to illustrate the problem. My destructor assumed that the class invariants hold, and it uses some of the class methods to aid in tearing down the object. Except that the object does not exist if the destructor gets called because of the failed Assert in the constructor.

A painful lesson in Design By Contract...

Is there any place in the documentation, where one can find about this implicit call of the destructor? Should it be added?

The program below writes

FAIL: Raised unexpected exception Access violation

which is raised in the destructor, after the EAssertionFailed excpetion is raised in the constructor.

When uncommenting the if statement in the destructor, it writes:

OK: Raised EAssertionFailedTRing.Create pre failed: n = -1 (assert_in_constructor.pas, line 44)

In that case, one needs to rely on the fact that fields in a class get initialized in a predefined way, e.g. pointers are set to nil.

Best regards,

Tom Verhoeff

program Assert_in_constructor; {$Mode Delphi} {$Assertions on} uses SysUtils, Classes; type PCell = ^ TCell; TCell = record // singly linked list of integers FData: Integer; FNext: PCell; end; TRing = class ( TObject ) private FList: PCell; // the elements in the ring // Invariants: // NonEmpty: FList nil; // IsRing: repeated application of ^.FNext to FList leads to FList, // i.e., the last cell points to the head cell // Unique: all p^.FData for p: PCell appearing in FList are different public constructor Create ( n: Integer ); { pre: 0 < n; post: ring consists of 0 through n-1 } function IsSingleton: Boolean; function Current: Integer; procedure Step; procedure RemoveSuccessor; { pre: not IsSingleton } destructor Destroy; override; end; constructor TRing.Create ( n: Integer ); var i: Integer; q: PCell; // points to last cell, with FData = i-1 begin inherited Create; Assert ( 0 < n, Format ( 'TRing.Create pre failed: n = %D', [ n ] ) ); New(FList); q := FList; q^.FData := 0; i := 1; while i n do begin New ( q^.FNext ); q := q^.FNext; q^.FData := i; Inc ( i ); end; q^.FNext := FList; // ring closed end; function TRing.IsSingleton: Boolean; begin Result := FList^.FNext = FList; end; function TRing.Current: Integer; begin Result := FList^.FData; end; procedure TRing.Step; begin FList := FList^.FNext; end; procedure TRing.RemoveSuccessor; var q: PCell; // cell to be removed begin q := FList^.FNext; FList^.FNext := q^.FNext; // q unlinked Dispose ( q ); end; destructor TRing.Destroy; begin //if FList nil then while not IsSingleton do RemoveSuccessor; Dispose ( FList ); inherited Destroy; end; var v: TRing; begin try v := TRing.Create ( -1 ); writeln ( 'FAIL: Should have raised EAssertionFailed' ); except on e : EAssertionFailed do begin writeln ( 'OK: Raised EAssertionFailed' + e.Message ) end; on e : Exception do begin writeln ( 'FAIL: Raised unexpected exception ' + e.Message ) end else writeln ( 'FAIL: Raised an unrecognized exception' ) end; { try } end.

Reply by email

Inoussa OUEDRAOGO
to Tom Verhoeff

2009/6/29 Tom Verhoeff :

While tracing a nasty bug (?), I discovered the hard way that when an Assert is done in a constructor, and it fails, then the destructor (Destroy) is automatically called.

Indeed, when an exception is raised in the constructor, be it an "assert" exception or not, the destructor is called to allow the developer to clean up the "in-construction" instance's members he has already initialized.

Show quoted text 5 lines
However, because the constructor failed, the object did not satisfy the class invariants. So, the destructor needs to be pessimistic about the state. Mine was not.

It has to, see below.

Show quoted text 5 lines
Since the Assert in the constructor is there to protect against parameter values that do not statisfy the constructor's precondition, there are no additional dynamic veriables and objects created, and hence they need not be destroyed. How can the destructor know that it is called in such a state?

At object creation, the instance's memory is "zeroed" so that integers are zero, pointers are nil, boolean are false, strings are empty strings, dynamic arrays are nil(zero length) ...
So in your case if "FList" is not nil, you filled it.

Below is a simple example to illustrate the problem. My destructor assumed that the class invariants hold,

The destructor has not to. It must prepare to handle both cases ( invariants hold and not ).

of the class methods to aid in tearing down the object.  Except that the object does not exist if the destructor gets called because of the failed Assert in the constructor.

Wrong. The object _does exists_ : its memory has been allocated and zeroed. With regard to the compiler/RTL, at the constructor entry point, the instance is valid. Please remember that the compiler is not aware of _your_ class invariants.
Other important point : Object Pascal instance construction is not done the same way as in C++ where the object is completely constructed at the end of the constructor. In Object Pascal it is even valid to call virtual methods in the constructor, that is possible because at the constructor entry point the instance actual type is definitely fixed.
This blog* by Herb Sutter explains the C++/C#/Java object construction mecanism. Make a special attention to the first** comment by Barry Kelly*** where he explains the Delphi way and its difference with regard to C++/C#/Java.

foot note :
* http://herbsutter.wordpress.com/2008/07/25/constructor-exceptions-in-c-c-and-java/ ** http://herbsutter.wordpress.com/2008/07/25/constructor-exceptions-in-c-c-and-java/#comment-716 *** Delphi compiler engineer http://barrkel.blogspot.com/

Inoussa O.

Reply by email

Tom Verhoeff
to Inoussa OUEDRAOGO
Inoussa OUEDRAOGO wrote 9 lines
2009/6/29 Tom Verhoeff : > While tracing a nasty bug (?), I discovered the hard way that when > an Assert is done in a constructor, and it fails, then the destructor > (Destroy) is automatically called. Indeed, when an exception is raised in the constructor, be it an "assert" exception or not, the destructor is called to allow the developer to clean up the "in-construction" instance's members he has already initialized.

A colleague of mine pointed out that the Delphi 7 documentation for TObject.Create actually says so (see final Note):

Constructs an object and initializes its data before the object is first used.

Delphi syntax:

  constructor Create;
  Description
  Create constructs an object. The purpose, size, and behavior of objects
  differ greatly. The Create constructor defined by TObject allocates memory
  but does not initialize data.
  Descendant objects usually define a constructor that creates the
  particular kind of object and initializes its data.
  Note: If an exception escapes from a constructor, the object's
  destructor is called to clean up the failed instance.

Zoiets zou ook in de TObject.Create (RTL) doc. van FreePascal passen (lees: thuishoren).

Show quoted text 8 lines
> of the class methods to aid in tearing down the object.  Except > that the object does not exist if the destructor gets called because > of the failed Assert in the constructor. Wrong. The object _does exists_ : its memory has been allocated and zeroed. With regard to the compiler/RTL, at the constructor entry point, the instance is valid. Please remember that the compiler is not aware of _your_ class invariants.

My apologies; it exists "technically" but may be "invalid" (i.e. not a usable object, "semantically").

Show quoted text 6 lines
Other important point : Object Pascal instance construction is not done the same way as in C++ where the object is completely constructed at the end of the constructor. In Object Pascal it is even valid to call virtual methods in the constructor, that is possible because at the constructor entry point the instance actual type is definitely fixed.

Thanks for the additional information.

Tom

E-MAIL: T.Verhoeff @ TUE.NL | Dept. of Math. & Comp. Science PHONE: +31 40 247 41 25 | Technische Universiteit Eindhoven FAX: +31 40 247 54 04 | PO Box 513, NL-5600 MB Eindhoven http://www.win.tue.nl/~wstomv/ | The Netherlands

Reply by email

Michael Van Canneyt
to Tom Verhoeff
Tom Verhoeff wrote 35 lines
On Tue, Jun 30, 2009 at 07:58:47PM +0100, Inoussa OUEDRAOGO wrote: > 2009/6/29 Tom Verhoeff : >> While tracing a nasty bug (?), I discovered the hard way that when >> an Assert is done in a constructor, and it fails, then the destructor >> (Destroy) is automatically called. > > Indeed, when an exception is raised in the constructor, be it an > "assert" exception or not, the destructor is called to allow the > developer to clean up the "in-construction" instance's members he has > already initialized. A colleague of mine pointed out that the Delphi 7 documentation for TObject.Create actually says so (see final Note): Constructs an object and initializes its data before the object is first used. Delphi syntax: constructor Create; Description Create constructs an object. The purpose, size, and behavior of objects differ greatly. The Create constructor defined by TObject allocates memory but does not initialize data. Descendant objects usually define a constructor that creates the particular kind of object and initializes its data. Note: If an exception escapes from a constructor, the object's destructor is called to clean up the failed instance. Zoiets zou ook in de TObject.Create (RTL) doc. van FreePascal passen (lees: thuishoren).

For non-dutch speakers: It says that this kind of information should be included in the FPC documentation. I will do so. But in my opinion, it does not belong in the unit reference documentation, but in the language documentation.

Michael.

Reply by email

Tom Verhoeff
to Michael Van Canneyt
Michael Van Canneyt wrote 9 lines
On Wed, 1 Jul 2009, Tom Verhoeff wrote: > > Zoiets zou ook in de TObject.Create (RTL) doc. van FreePascal passen > (lees: thuishoren). For non-dutch speakers: It says that this kind of information should be included in the FPC documentation. I will do so. But in my opinion, it does not belong in the unit reference documentation, but in the language documentation.

My apologies for the Dutch part (I got interrupted when writing the message). The translation is OK :-)

I have looked in the Language Reference Guide to see where it would be appropriate to include this information.

Section 6.2 is about Class instantiation, using a constructor.

I have no idea how the exception mechanism in constructors is actually implemented. Does it have anything to do with TObject, or is it completely independent of that?

I did not find a discussion about the distinctions between the various kinds of methods (function, procedure, constructor, destructor). They are just named (syntactically) in 6.3.1 Classes > Methods > Declaration.

In particular, the issue concerns both constructors as well as the destructor.

For constructors it is important to realize what the consequences will be when they are interrupted with an exception. For destructors it is important to keep in mind what one may or may not assume when they are called.

Best regards,

Tom

E-MAIL: T.Verhoeff @ TUE.NL | Dept. of Math. & Comp. Science PHONE: +31 40 247 41 25 | Technische Universiteit Eindhoven FAX: +31 40 247 54 04 | PO Box 513, NL-5600 MB Eindhoven http://www.win.tue.nl/~wstomv/ | The Netherlands

Reply by email

Martin Friebe
to Inoussa OUEDRAOGO: 2009/6/29 Tom Verhoeff : Indeed, when an exception is raised in the constructor, be it an…
Inoussa OUEDRAOGO wrote 12 lines
2009/6/29 Tom Verhoeff : > While tracing a nasty bug (?), I discovered the hard way that when > an Assert is done in a constructor, and it fails, then the destructor > (Destroy) is automatically called. > Indeed, when an exception is raised in the constructor, be it an "assert" exception or not, the destructor is called to allow the developer to clean up the "in-construction" instance's members he has already initialized.

I just cam across this thread. While I am not opposing the behaviour as it's stand (it could be useful anyway), it raises another question.

Does that mean that an implicit exception-frame (or whatever this is called) is inserted *each* time you create/instantiate an object?

Now I understand, object instantiation comes at a cost anyway (memory allocation), this does add to the cost of instantiation. And the only error/exception, which really *all* classes can encounter in a constructor, is out of memory for the object itself => In which case create is never called (because NewInstance fails), and Destroy should not be called (as there is no instance that could be passed to destroy). So There could be calls to create where a developer does not want (and not need) any exception stack frame.

I also wonder why this special kind of "garbage collector"? Pascal has automatic behaviour to handle/free resources for:
- strings
- open array
- apparently classes/objects, but only inside the constructor ?

The last one does of course not apply to objects hold by the failed object, but I assume that, if Destroy is called, the instance is also freed? So why are objects handled special in the constructor? If they raise an exception anywhere else in there live, then you need to care yourself about catching it, and freeing them?

Anyway, as I said it's not necessarily bad behaviour. but given that
exception handling may add to runtime cost, is there a way to switch
this off?
Like
 {$AutoDestroty off}
 try
   foo := TFoo.create;
 except
   if foo  nil then foo.free;
 end;

Martin

Reply by email

Michael Van Canneyt
to Martin Friebe
Martin Friebe wrote 39 lines
Inoussa OUEDRAOGO wrote: > 2009/6/29 Tom Verhoeff : > >> While tracing a nasty bug (?), I discovered the hard way that when >> an Assert is done in a constructor, and it fails, then the destructor >> (Destroy) is automatically called. >> > > Indeed, when an exception is raised in the constructor, be it an > "assert" exception or not, the destructor is called to allow the > developer to clean up the "in-construction" instance's members he has > already initialized. > I just cam across this thread. While I am not opposing the behaviour as it's stand (it could be useful anyway), it raises another question. Does that mean that an implicit exception-frame (or whatever this is called) is inserted *each* time you create/instantiate an object? Now I understand, object instantiation comes at a cost anyway (memory allocation), this does add to the cost of instantiation. And the only error/exception, which really *all* classes can encounter in a constructor, is out of memory for the object itself => In which case create is never called (because NewInstance fails), and Destroy should not be called (as there is no instance that could be passed to destroy). So There could be calls to create where a developer does not want (and not need) any exception stack frame. I also wonder why this special kind of "garbage collector"? Pascal has automatic behaviour to handle/free resources for: - strings - open array - apparently classes/objects, but only inside the constructor ? The last one does of course not apply to objects hold by the failed object, but I assume that, if Destroy is called, the instance is also freed? So why are objects handled special in the constructor? If they raise an exception anywhere else in there live, then you need to care yourself about catching it, and freeing them?

Because you cannot handle it yourself. Assume the following statement:

   MyClass:=TSomeClass.Create;

if an exception happens inside the constructor, then no instance pointer is assigned to MyClass, since execution after the exception is raised is continued at the next except/finally block.

That means you cannot handle destruction in the except block (assuming you wrote one) since you have no pointer to work with.

Therefor the constructor code must call the destructor in case of an error.

Construction is handled very specially by the compiler. The special exception frame is in the constructor helper code. Your own routine does not get an extra exception frame.

For instance:

procedure DOCreate;
begin
  TMyObject.Create;
end;

Results in the following (64-bit) assembler:

.globl P$TESTCF_DOCREATE
.type P$TESTCF_DOCREATE,@function
P$TESTCF_DOCREATE:
pushq %rbp
movq %rsp,%rbp
movq $VMT_P$TESTCF_TMYOBJECT,%rsi
movq $0,%rdi
call P$TESTCF_TMYOBJECT_$__CREATE$$TMYOBJECT
leave
ret

As you can see, there is no extra exception frame.

Michael.

Reply by email

Martin Friebe
to Michael Van Canneyt
Michael Van Canneyt wrote 49 lines
On Thu, 2 Jul 2009, Martin Friebe wrote: > Inoussa OUEDRAOGO wrote: >> Indeed, when an exception is raised in the constructor, be it an >> "assert" exception or not, the destructor is called to allow the >> developer to clean up the "in-construction" instance's members he has >> already initialized. >> > I just cam across this thread. While I am not opposing the behaviour > as it's stand (it could be useful anyway), it raises another question. > > Does that mean that an implicit exception-frame (or whatever this is > called) is inserted *each* time you create/instantiate an object? > > Now I understand, object instantiation comes at a cost anyway (memory > allocation), this does add to the cost of instantiation. And the only > error/exception, which really *all* classes can encounter in a > constructor, is out of memory for the object itself => In which case > create is never called (because NewInstance fails), and Destroy > should not be called (as there is no instance that could be passed to > destroy). > So There could be calls to create where a developer does not want > (and not need) any exception stack frame. > > I also wonder why this special kind of "garbage collector"? Pascal > has automatic behaviour to handle/free resources for: > - strings > - open array > - apparently classes/objects, but only inside the constructor ? > > The last one does of course not apply to objects hold by the failed > object, but I assume that, if Destroy is called, the instance is also > freed? > So why are objects handled special in the constructor? If they raise > an exception anywhere else in there live, then you need to care > yourself about catching it, and freeing them? Because you cannot handle it yourself. Assume the following statement: MyClass:=TSomeClass.Create; if an exception happens inside the constructor, then no instance pointer is assigned to MyClass, since execution after the exception is raised is continued at the next except/finally block. That means you cannot handle destruction in the except block (assuming you wrote one) since you have no pointer to work with. Therefor the constructor code must call the destructor in case of an error.

Yes, of course, you are right. It is impossible to handle it in the calling code. As the order is:
- instantiation (NewInstance)
- Create
- exception
- assignment to local var => never executed

But you can handle it yourself inside the constructor. No one stops the developper from wrtiting

Constructor TFoo.Create;
begin
  try
   ...
  except
    self.Destroy; // Or Free ?
  end;
end;

- If an error/exception (out of mem) happens in NewInstance, neither Create nor Destroy are called (and neither can they be called, because they need an (empty) instance and there is none)
- If an error in Create happens, self is assigned (inside Create). So it can be acted on.
Even if, in the above example, this Create was called by a child-class via inherited Create. There is no worry (not even if self.Free is used), that the inherited constructor will find an invalid pointer in self (because/if it has been freed). The code above, never returns to the inherited constructor, unless this inherited constructor itself catches the exception too (in which case it must check ; It would need to be flagged on the exception object)

Anyway, while possible, it would be far to complicated. So I agree, the automated handling, ensuring the instance is Destroyed and freed once only, is probably the best

Leaves question 2: Is there a way to switch it off, to avoid the cost of creating an exception frame? (if you are either sure your constructor does not throw an exception, or for some reason do not care)?

Thanks for the explanation so far
Martin

Reply by email

Michael Van Canneyt
to Martin Friebe
Martin Friebe wrote 82 lines
Michael Van Canneyt wrote: > On Thu, 2 Jul 2009, Martin Friebe wrote: >> Inoussa OUEDRAOGO wrote: >>> Indeed, when an exception is raised in the constructor, be it an >>> "assert" exception or not, the destructor is called to allow the >>> developer to clean up the "in-construction" instance's members he has >>> already initialized. >>> >> I just cam across this thread. While I am not opposing the behaviour as >> it's stand (it could be useful anyway), it raises another question. >> >> Does that mean that an implicit exception-frame (or whatever this is >> called) is inserted *each* time you create/instantiate an object? >> >> Now I understand, object instantiation comes at a cost anyway (memory >> allocation), this does add to the cost of instantiation. And the only >> error/exception, which really *all* classes can encounter in a >> constructor, is out of memory for the object itself => In which case >> create is never called (because NewInstance fails), and Destroy should not >> be called (as there is no instance that could be passed to destroy). >> So There could be calls to create where a developer does not want (and not >> need) any exception stack frame. >> >> I also wonder why this special kind of "garbage collector"? Pascal has >> automatic behaviour to handle/free resources for: >> - strings >> - open array >> - apparently classes/objects, but only inside the constructor ? >> >> The last one does of course not apply to objects hold by the failed >> object, but I assume that, if Destroy is called, the instance is also >> freed? >> So why are objects handled special in the constructor? If they raise an >> exception anywhere else in there live, then you need to care yourself >> about catching it, and freeing them? > > Because you cannot handle it yourself. Assume the following statement: > > MyClass:=TSomeClass.Create; > > if an exception happens inside the constructor, then no instance pointer is > assigned to MyClass, since execution after the exception is raised is > continued at the next except/finally block. > That means you cannot handle destruction in the except block (assuming you > wrote one) since you have no pointer to work with. > > Therefor the constructor code must call the destructor in case of an error. Yes, of course, you are right. It is impossible to handle it in the calling code. As the order is: - instantiation (NewInstance) - Create - exception - assignment to local var => never executed But you can handle it yourself inside the constructor. No one stops the developper from wrtiting Constructor TFoo.Create; begin try ... except self.Destroy; // Or Free ? end; end; - If an error/exception (out of mem) happens in NewInstance, neither Create nor Destroy are called (and neither can they be called, because they need an (empty) instance and there is none) - If an error in Create happens, self is assigned (inside Create). So it can be acted on. Even if, in the above example, this Create was called by a child-class via inherited Create. There is no worry (not even if self.Free is used), that the inherited constructor will find an invalid pointer in self (because/if it has been freed). The code above, never returns to the inherited constructor, unless this inherited constructor itself catches the exception too (in which case it must check ; It would need to be flagged on the exception object) Anyway, while possible, it would be far to complicated. So I agree, the automated handling, ensuring the instance is Destroyed and freed once only, is probably the best

Yes, because there is no way to know what happens in the constructor/destuctor in a descendent/parent class...

Show quoted text 5 lines
Leaves question 2: Is there a way to switch it off, to avoid the cost of creating an exception frame? (if you are either sure your constructor does not throw an exception, or for some reason do not care)?

Not that I am aware of. But someone familiar with the compiler internals should confirm this.

Michael.

Michael.

Show quoted text 8 lines
Thanks for the explanation so far Martin _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Jonas Maebe
to Martin Friebe: Yes, of course, you are right. It is impossible to handle it in the calling code. As the…
Martin Friebe wrote
Leaves question 2: Is there a way to switch it off, to avoid the cost of creating an exception frame? (if you are either sure your constructor does not throw an exception, or for some reason do not care)?

http://www.freepascal.org/docs-html/prog/progsu32.html

It also applies to the exception frames in constructors.

Note that while that page says it is set to OFF by default, it's obviously set to ON by default. That error has already been corrected in the svn version of the documentation.

Jonas

Reply by email