Lazarus

Free Pascal => Beginners => Topic started by: BrainChemistry on May 21, 2020, 06:15:02 pm

Title: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: BrainChemistry on May 21, 2020, 06:15:02 pm
Hello everybody,

simple question, see title:

Code: Pascal  [Select][+][-]
  1. //interface
  2. Test = class
  3.   constructor Create(APar: Pointer);
  4. //...
  5.  
  6. // implementation
  7. constructor Test.Create(APar: Pointer);
  8. begin
  9.   inherited Create;
  10.   if not Assigned(APar) then // then how to proceed?
  11.   begin
  12.     Exit;
  13.     FreeAndNil(Self);
  14.   end;
  15. //...
  16.  

I made a long research on the internet on this and I get to believe perhaps my design is flawed because all I found was how to handle exceptions in the constructor.

Thanks for your help in advance.
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: GetMem on May 21, 2020, 06:25:42 pm
Check if the pointer is nil before you create an instance of TTest class, like this:
Code: Pascal  [Select][+][-]
  1. var
  2.   Test: TTest;
  3. begin
  4.   if APar <> nil then
  5.   begin
  6.      Test := TTest.Create(APar);
  7.      //...
  8.   end
  9.   else
  10.     DoSomethingElse;
  11. end;
  12.  
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: eljo on May 21, 2020, 06:37:15 pm
Hello everybody,

simple question, see title:

Code: Pascal  [Select][+][-]
  1. //interface
  2. Test = class
  3.   constructor Create(APar: Pointer);
  4. //...
  5.  
  6. // implementation
  7. constructor Test.Create(APar: Pointer);
  8. begin
  9.   inherited Create;
  10.   if not Assigned(APar) then // then how to proceed?
  11.   begin
  12.     Exit;
  13.     FreeAndNil(Self);
  14.   end;
  15. //...
  16.  

I made a long research on the internet on this and I get to believe perhaps my design is flawed because all I found was how to handle exceptions in the constructor.

Thanks for your help in advance.
raise an exception. Any exception raised in a constructor will clean up any memory allocation. It is taken care by the framework.
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: Thaddy on May 21, 2020, 06:37:41 pm
"nice" piece of coding:
what will happen here?
Code: Pascal  [Select][+][-]
  1. constructor Test.Create(APar: Pointer);
  2. begin
  3.   inherited Create;
  4.   if not Assigned(APar) then // then how to proceed?
  5.   begin
  6.     Exit; // <=== Huh?
  7.     FreeAndNil(Self); // dead code...
  8.   end;
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: Thaddy on May 21, 2020, 06:49:58 pm
more idiocy ( I am mad enough thank you..(true))
Quote
raise an exception
In a constructor?, really???
Then you should also handle the exception inside the constructor....
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: eljo on May 21, 2020, 06:55:50 pm
more idiocy ( I am mad enough thank you..(true))
Quote
raise an exception
In a constructor?, really???
Then you should also handle the exception inside the constructor....
Yes in the constructor. Prove me wrong if you can. I'll report the bug my self if you do.
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: Thaddy on May 21, 2020, 07:27:00 pm
That is not even close to a serious reply eljo
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: eljo on May 21, 2020, 07:47:11 pm
That is not even close to a serious reply eljo
Yes, because calling me an idiot mandates a far better response.

As I said, raising an exception in the constructor, will exit the constructor with out creating the object. It is what the OP asked for. If you have more info on the subject that I missed, please fill free to share with us.
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: julkas on May 21, 2020, 08:37:32 pm
I agree with @GetMem.
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: eljo on May 21, 2020, 08:44:43 pm
I agree with @GetMem.
Me too but that assumes that the OP has control over the code that creates the object what happens if he is responsible for the class but not how its used?
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: julkas on May 21, 2020, 08:55:36 pm
https://stackoverflow.com/questions/39110021/delphi-raise-exception-in-constructor
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: GetMem on May 21, 2020, 09:05:04 pm
@eljo
Quote
Me too but that assumes that the OP has control over the code that creates the object what happens if he is responsible for the class but not how its used?
Good point. I did not think about this particular situation. In my opinion raising an exception inside the constructor is a perfectly valid solution.
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: Bart on May 21, 2020, 09:10:50 pm
You can call Fail inside a constructor.
The constructor will return Nil in that case.

Code: Pascal  [Select][+][-]
  1. constructor TMyClass.Create;
  2. begin
  3.   if SomeCriticalConditionIsNotMet then
  4.     Fail;
  5.   ...
  6.   //rest of constructor code
  7. end;
  8.  
  9. ...
  10. var
  11.   C: TMyClass;
  12. begin
  13.   C := nil;
  14.   C := TMyClass.Create;
  15.   if C = nil then ErrorMessageAndHaltApplication;
  16.   //Do something with C
  17.   C.Free;
  18. end..

There wil be no memory leak (for the instance of C, that is).

Bart
Title: Re: How to leave constructor early (and destroy instance) if condition is met?
Post by: BrainChemistry on May 21, 2020, 09:18:52 pm
Check if the pointer is nil before you create an instance of TTest class, like this:
Code: Pascal  [Select][+][-]
  1. var
  2.   Test: TTest;
  3. begin
  4.   if APar <> nil then
  5.   begin
  6.      Test := TTest.Create(APar);
  7.      //...
  8.   end
  9.   else
  10.     DoSomethingElse;
  11. end;
  12.  

Quote from: eljo
but that assumes that the OP has control over the code that creates the object what happens if he is responsible for the class but not how its used?

That is exactly the case, hence it is unfortunately no option to check the condition outside the constructor.

@eljo: I read about having an exception raised to stop the instance from being created (and memory cleaned up) in my research before. Maybe it is just me, but this feels like a dirty trick and an abuse of the exception mechanism.

Is there a "clean way" of interrupting the instance creation?

REMARK: While writing this, this came in:

You can call Fail inside a constructor.
The constructor will return Nil in that case.

Code: Pascal  [Select][+][-]
  1. constructor TMyClass.Create;
  2. begin
  3.   if SomeCriticalConditionIsNotMet then
  4.     Fail;
  5.   ...
  6.   //rest of constructor code
  7. end;
  8.  
  9. ...
  10. var
  11.   C: TMyClass;
  12. begin
  13.   C := nil;
  14.   C := TMyClass.Create;
  15.   if C = nil then ErrorMessageAndHaltApplication;
  16.   //Do something with C
  17.   C.Free;
  18. end..

There wil be no memory leak (for the instance of C, that is).

Bart

That's what I was looking for! Thanks Bart and everybody!


Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: julkas on May 21, 2020, 09:24:43 pm
http://docwiki.embarcadero.com/Libraries/Rio/en/System.TObject.Destroy
http://docwiki.embarcadero.com/Libraries/Rio/en/System.Fail
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: BrainChemistry on May 21, 2020, 09:28:06 pm
https://www.freepascal.org/docs-html/rtl/system/fail.html
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: GetMem on May 21, 2020, 09:30:35 pm
@ BrainChemistry
Quote
That is exactly the case, hence it is unfortunately no option to check the condition outside the constructor.
OK. I know this is not your fault, however the whole logic seems strange to me. After all set and done, the programmer who calls your class, still has to check if the instance is nil. What is the point? I'm I missing something?
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: julkas on May 21, 2020, 09:44:16 pm
https://www.freepascal.org/docs-html/rtl/system/fail.html
If you don't want Delphi compatibility it's ok.
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: BrainChemistry on May 21, 2020, 10:10:02 pm
@ BrainChemistry
Quote
That is exactly the case, hence it is unfortunately no option to check the condition outside the constructor.
OK. I know this is not your fault, however the whole logic seems strange to me. After all set and done, the programmer who calls your class, still has to check if the instance is nil. What is the point? I'm I missing something?

I do not fully understand your confusion about that. The programmer using the unit may accidently use wrong arguments (nil pointer, integers out of bounds, and so on) for my classes, in these cases, my class will react accordingly and testable. What the programmer makes from this, is his own responsibility, I guess. :D

Although, in the constructor, C functions from external libraries (which I do not maintain) are called , so even if the input is checked to seem perfectly valid, the constructor may lead to a fail. And this is testable now.

https://www.freepascal.org/docs-html/rtl/system/fail.html
If you don't want Delphi compatibility it's ok.

I want Delphi compatibility, and to be honest, on examining your links, I was kind of disappointed. Maybe I will introduce a compiler switch for Delphi later. For now, I will stay with FPC's way of handling it, which seems more convenient to me. Thanks for the hint though!
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: GetMem on May 21, 2020, 10:34:43 pm
@BrainChemistry
The programmer using the unit has two solutions:
1. Check if the pointer is valid then create your class
2. Feed invalid pointers to the constructor, then check if the class explodes

Which is better? I mean why burn down the house when you can read the label:" high explosive".
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: BrainChemistry on May 21, 2020, 11:58:59 pm
@GetMem
Well, of course the programmer should make sure to input valid data for the class beforehand. But what if the programmer misses something at the validity check, or the seemingly valid data is rejected by the external C functions because they unexpectedly consider it as invalid for some reason? In these (hopefully very rare) cases the creation process should be aborted in a clean way.
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: eljo on May 22, 2020, 12:56:03 am
@GetMem
Well, of course the programmer should make sure to input valid data for the class beforehand. But what if the programmer misses something at the validity check, or the seemingly valid data is rejected by the external C functions because they unexpectedly consider it as invalid for some reason? In these (hopefully very rare) cases the creation process should be aborted in a clean way.
the disagreement is what is considered a "clean way". In my opinion a hard exception at creation with a clear message eg "Par1 is empty" is far easier to track and debug than a silent fail with no indication what or why it failed. I think that is what GetMem says as well.
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: PascalDragon on May 22, 2020, 09:14:38 am
@GetMem
Well, of course the programmer should make sure to input valid data for the class beforehand. But what if the programmer misses something at the validity check, or the seemingly valid data is rejected by the external C functions because they unexpectedly consider it as invalid for some reason? In these (hopefully very rare) cases the creation process should be aborted in a clean way.

The clean, Object Pascal way is indeed raising an exception. While Fail will work, it will only work inside the constructor itself. If you have a call to some setup method (for example shared between multiple constructors), you can't use Fail.

more idiocy ( I am mad enough thank you..(true))
Quote
raise an exception
In a constructor?, really???
Then you should also handle the exception inside the constructor....

No, raising an exception inside the constructor is an explicitly supported concept of Object Pascal. The compiler inserts implicit exception handling code to handle this case gracefully (and have the constructor return Nil). It's a major improvement compared to TP style objects which only had Fail.
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: Thaddy on May 22, 2020, 10:11:42 am
No, raising an exception inside the constructor is an explicitly supported concept of Object Pascal. The compiler inserts implicit exception handling code to handle this case gracefully (and have the constructor return Nil). It's a major improvement compared to TP style objects which only had Fail.
That may be the case but those exceptions should still be handled inside the constructor, since all kinds of managed code may be initialized. With all the mayhem associated with it. Add a try/finally is enough to blow that up.
As Allen Bauer wrote (paraphrased) handle as soon as possible, not raise..
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: BrunoK on May 22, 2020, 10:41:53 am
Concerning the original poster code
there is a code error in the sense that FreeAndNil will nether be executed because exit precedes it.

given APar cannot be modified, since there is no way to change it as per constructor definition, the following code will cover the situation.
Code: Pascal  [Select][+][-]
  1. constructor Test.Create(APar: Pointer);
  2. begin
  3.   if not Assigned(APar) then begin
  4.     FreeAndNil(Self);
  5.     exit;
  6.   end
  7.   else
  8.     inherited Create;
  9.   //...
  10. end;
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: Thaddy on May 22, 2020, 01:15:33 pm
Correct. Tnx for this example.
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: PascalDragon on May 22, 2020, 02:59:44 pm
No, raising an exception inside the constructor is an explicitly supported concept of Object Pascal. The compiler inserts implicit exception handling code to handle this case gracefully (and have the constructor return Nil). It's a major improvement compared to TP style objects which only had Fail.
That may be the case but those exceptions should still be handled inside the constructor, since all kinds of managed code may be initialized. With all the mayhem associated with it. Add a try/finally is enough to blow that up.
As Allen Bauer wrote (paraphrased) handle as soon as possible, not raise..

It makes no sense to handle an exception that you yourself raised to leave the constructor... I agree about exceptions that some called method raises, but this is about something like the following:

Code: Pascal  [Select][+][-]
  1. constructor TMyClass.Create(aArg: TSomeOtherClass);
  2. begin
  3.   if not Assigned(aArg) then
  4.     raise EArgumentNilException.Create('aArg is Nil');
  5.   // ...
  6. end;
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: ASerge on May 22, 2020, 09:08:32 pm
This is very, very bad if the constructor returns nil when problems occur, and does not raise an exception. Everywhere and everywhere it is assumed that if the constructor was completed, then the object was created. If you still decide to do this, then write comment with large letters "HERE A BAD CONSTRUCTION IS USED, ATTENTION, THE OBJECT MAY BE NIL!"
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: PascalDragon on May 23, 2020, 11:38:35 am
This is very, very bad if the constructor returns nil when problems occur, and does not raise an exception. Everywhere and everywhere it is assumed that if the constructor was completed, then the object was created. If you still decide to do this, then write comment with large letters "HERE A BAD CONSTRUCTION IS USED, ATTENTION, THE OBJECT MAY BE NIL!"
This as well. ;D
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: MarkMLl on May 23, 2020, 03:21:53 pm
This is very, very bad if the constructor returns nil when problems occur, and does not raise an exception. Everywhere and everywhere it is assumed that if the constructor was completed, then the object was created. If you still decide to do this, then write comment with large letters "HERE A BAD CONSTRUCTION IS USED, ATTENTION, THE OBJECT MAY BE NIL!"
This as well. ;D

HERE BE PASCALDRAGONS! :-)

Seriously, though, what else would one expect to happen if there isn't provision for an explicit success or failure result? It's been the case for a long time that something like an out-of-memory error results in a nil pointer result from heap allocation... the only real alternative would be for TObject to be extended with a property that is false until the constructor has returned successfully.

MarkMLl


Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: PascalDragon on May 23, 2020, 04:09:14 pm
Ah, correction: the constructor returning Nil is in most cases caught by the following code accessing the object and which is usually part of a resource protection block:

Code: Pascal  [Select][+][-]
  1. var
  2.   o: TMyClass;
  3. begin
  4.   o := TMyClass.Create;
  5.   try
  6.     o.DoSomething;
  7.   finally
  8.     o.Free;
  9.   end;
  10. end;

Under the assumption that DoSomething accesses the instance in some way (e.g. VMT call, accessing a field, etc.) this will crash if the constructor returned Nil, so all is nice and well. And if the constructor decides to raise an exception that is fine as well as the whole resource protection block will be skipped then. What is problematic however is this:

Code: Pascal  [Select][+][-]
  1. procedure Test;
  2. var
  3.   o: TMyClass;
  4. begin
  5.   try
  6.     o := TMyClass.Create;
  7.     o.DoSomething;
  8.   finally
  9.     o.Free;
  10.   end;
  11. end;

If the constructor raises an exception (which is allowed after all!) this will result in o having a random value (it's a variable on the stack after all) and thus will result in a nested exception when the code in the finally block is executed. The solution is to either move the constructor call before the resource protection block or to manually initialize o to Nil as the assignment won't be executed if the constructor raises an exception.

However it is the case that Object Pascal code usually does not check if a valid class instance was returned, relying on the above concepts instead.
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: MarkMLl on May 23, 2020, 05:31:36 pm
Even worse:

Code: [Select]
    procedure Test;

    begin
        with TMyClass.Create do
            DoSomething() and Free
    end;

MarkMLl

Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: PascalDragon on May 23, 2020, 09:45:04 pm
Even worse:

Code: [Select]
    procedure Test;

    begin
        with TMyClass.Create do
            DoSomething() and Free
    end;

The only part that is worse here is the missing resource protection block. Otherwise it's equivalent to the standard case:

Code: Pascal  [Select][+][-]
  1. with TMyClass.Create do
  2.   try
  3.     DoSomething;
  4.   finally
  5.     Free;
  6.   end;
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: MarkMLl on May 23, 2020, 10:07:42 pm
Even worse:

Code: [Select]
    procedure Test;

    begin
        with TMyClass.Create do
            DoSomething() and Free
    end;

The only part that is worse here is the missing resource protection block. Otherwise it's equivalent to the standard case:

Code: Pascal  [Select][+][-]
  1. with TMyClass.Create do
  2.   try
  3.     DoSomething;
  4.   finally
  5.     Free;
  6.   end;

Except that the use of  with  means that there isn't an instantiation that can be checked by a debugger. I trust that you don't condone that style.

MarkMLl

Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: PascalDragon on May 23, 2020, 10:42:53 pm
Except that the use of  with  means that there isn't an instantiation that can be checked by a debugger. I trust that you don't condone that style.

This is only a question of correctly generated debug information and the debugger/IDE handling it correcty (currently either one or both are not the case, at least with DWARF) as internally there is a temporary variable.

And as I said it's a valid concept that's even used in the RTL here and there.
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: MarkMLl on May 23, 2020, 11:00:20 pm
Except that the use of  with  means that there isn't an instantiation that can be checked by a debugger. I trust that you don't condone that style.

This is only a question of correctly generated debug information and the debugger/IDE handling it correcty (currently either one or both are not the case, at least with DWARF) as internally there is a temporary variable.

Well, you might possibly recall that I've been saying for years that  with  would benefit from an explicit "shorthand" name, and possibly having the implicit name standardised would be an advantage.

Code: [Select]
procedure Test;

    begin
        with newInstance= TMyClass.Create do
            newInstance.DoSomething() and newInstance.Free
    end;

// Not really a good example, it's obviously no advantage until the  with  applies to a complex sequence of fields and dereferences.



Quote
And as I said it's a valid concept that's even used in the RTL here and there.

"Even"? I can assure you that I'm keenly aware of that, having had to single-step through bits of it.

MarkMLl
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: PascalDragon on May 23, 2020, 11:29:30 pm
Except that the use of  with  means that there isn't an instantiation that can be checked by a debugger. I trust that you don't condone that style.

This is only a question of correctly generated debug information and the debugger/IDE handling it correcty (currently either one or both are not the case, at least with DWARF) as internally there is a temporary variable.

Well, you might possibly recall that I've been saying for years that  with  would benefit from an explicit "shorthand" name, and possibly having the implicit name standardised would be an advantage.

Code: [Select]
procedure Test;

    begin
        with newInstance= TMyClass.Create do
            newInstance.DoSomething() and newInstance.Free
    end;

// Not really a good example, it's obviously no advantage until the  with  applies to a complex sequence of fields and dereferences.


An explicitely declared, local variable fullfills the same purpose. And we won't make an exception for inline variables only for with.
Title: Re: [SOL.]How to leave constructor early (and destroy instance) if condition is met?
Post by: MarkMLl on May 23, 2020, 11:33:01 pm
An explicitely declared, local variable fullfills the same purpose. And we won't make an exception for inline variables only for with.

Shucks. I was hoping you wouldn't notice :-) :-) :-)

MarkMLl
TinyPortal © 2005-2018