Recent

Author Topic: Hiding the destructor in class - Somehow possible?  (Read 1224 times)

soerensen3

  • Full Member
  • ***
  • Posts: 211
Hiding the destructor in class - Somehow possible?
« on: May 25, 2020, 09:51:59 pm »
I think I know what the answer will be already but maybe someone knows alternatives.

What I would like to do is prevent the user of a library from calling the destructor of a certain classes. The use case is when the ownership is not on the user's end at all or when the user should not destroy the instance themselves but only mark it for deletion. In both cases it would be possible to at least give appropriate run time errors but better would be to have compiler errors.
Lazarus 1.9 with FPC 3.0.4
Target: Manjaro Linux 64 Bit (4.9.68-1-MANJARO)

Loesje

  • Full Member
  • ***
  • Posts: 163
    • Lazarus Support website
Re: Hiding the destructor in class - Somehow possible?
« Reply #1 on: May 25, 2020, 10:01:56 pm »
You could mark it 'deprecated'. But that only helps when a user calls ClassInstance.Destroy. No-one does that, everyone uses ClassInstance.Free.

What you could do, however, is warn at run-time. Just declare a private/protected field like 'FMayBeDestroyed'. Then in the destructor look if FMayBeDestroyed is true. If not, raise an exception.

When you want to free the class itself. all you have to do is set FMayBeDestroyed to true.

eljo

  • Sr. Member
  • ****
  • Posts: 408
Re: Hiding the destructor in class - Somehow possible?
« Reply #2 on: May 26, 2020, 01:58:43 am »
I think I know what the answer will be already but maybe someone knows alternatives.

What I would like to do is prevent the user of a library from calling the destructor of a certain classes. The use case is when the ownership is not on the user's end at all or when the user should not destroy the instance themselves but only mark it for deletion. In both cases it would be possible to at least give appropriate run time errors but better would be to have compiler errors.
Destroy is used internally by the framework it is called automatically when a exception is raised inside a constructor for example. I would suggest to use interfaces instead of classes for your purposes.

PascalDragon

  • Hero Member
  • *****
  • Posts: 2259
  • Compiler Developer
Re: Hiding the destructor in class - Somehow possible?
« Reply #3 on: May 26, 2020, 09:35:25 am »
I think I know what the answer will be already but maybe someone knows alternatives.

What I would like to do is prevent the user of a library from calling the destructor of a certain classes. The use case is when the ownership is not on the user's end at all or when the user should not destroy the instance themselves but only mark it for deletion. In both cases it would be possible to at least give appropriate run time errors but better would be to have compiler errors.

You can't. Even if you'd manage to "hide" Destroy for your class simply calling Free will result in the correct virtual method to be called. At worst you'll have memory leaks (e.g. if you used reintroduce and thus the normal, virtual Destroy won't call your destructor).

If you want better control over the lifetime of your instances use interfaces as eljo suggested. You can then make sure that the instance isn't destroyed until you want it to. Though you must only pass out the interface then! And you might want to override QueryInterface as otherwise a user can do IntfVar as TMyClass to get the class instance again (though to be fair, doing that would be "shooting oneself in the foot" and why would one do that?).

jamie

  • Hero Member
  • *****
  • Posts: 3648
Re: Hiding the destructor in class - Somehow possible?
« Reply #4 on: May 26, 2020, 05:21:50 pm »
I suppose if you are on windows and maybe other targets that support the dynamic objects  you could
put your code in a DLL and thus have strict control over how it is used...


 you could also supply linkable objects too with only the interface section files, much like what Delphi did and most likely still does.
The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 10516
Re: Hiding the destructor in class - Somehow possible?
« Reply #5 on: May 29, 2020, 06:01:52 pm »
You can't. Even if you'd manage to "hide" Destroy for your class simply calling Free will result in the correct virtual method to be called. At worst you'll have memory leaks (e.g. if you used reintroduce and thus the normal, virtual Destroy won't call your destructor).
Not quite It is possible (and I can also hide the warning and note:
Code: Pascal  [Select][+][-]
  1. unit hidedestructor;
  2. {$mode delphi}
  3. interface
  4. type  
  5.   TBaseHiddenDestructor = class
  6.   strict private
  7.     destructor destroy;override;
  8.   end;
  9.  
  10. implementation
  11.  
  12. destructor TBaseHiddenDestructor.Destroy;
  13. begin
  14.   writeln('I am hidden');
  15. end;
  16. end.
Code: Pascal  [Select][+][-]
  1. program testdestr;
  2. {$mode delphi}
  3. uses
  4.   hidedestructor;
  5.  
  6. type
  7.   TTestClass = class(TBaseHiddenDestructor)
  8.   end;
  9.  
  10. begin
  11.   with TTestClass.Create do free;
  12. end.

Not recommended, but possible... ;D And leaks...

« Last Edit: May 29, 2020, 06:05:29 pm by Thaddy »

Thaddy

  • Hero Member
  • *****
  • Posts: 10516
Re: Hiding the destructor in class - Somehow possible?
« Reply #6 on: May 29, 2020, 06:12:16 pm »
But it only hides the original of course. Hence not quite.

PascalDragon

  • Hero Member
  • *****
  • Posts: 2259
  • Compiler Developer
Re: Hiding the destructor in class - Somehow possible?
« Reply #7 on: May 30, 2020, 11:13:29 am »
You can't. Even if you'd manage to "hide" Destroy for your class simply calling Free will result in the correct virtual method to be called. At worst you'll have memory leaks (e.g. if you used reintroduce and thus the normal, virtual Destroy won't call your destructor).
Not quite It is possible (and I can also hide the warning and note:
Code: Pascal  [Select][+][-]
  1. unit hidedestructor;
  2. {$mode delphi}
  3. interface
  4. type  
  5.   TBaseHiddenDestructor = class
  6.   strict private
  7.     destructor destroy;override;
  8.   end;
  9.  
  10. implementation
  11.  
  12. destructor TBaseHiddenDestructor.Destroy;
  13. begin
  14.   writeln('I am hidden');
  15. end;
  16. end.
Code: Pascal  [Select][+][-]
  1. program testdestr;
  2. {$mode delphi}
  3. uses
  4.   hidedestructor;
  5.  
  6. type
  7.   TTestClass = class(TBaseHiddenDestructor)
  8.   end;
  9.  
  10. begin
  11.   with TTestClass.Create do free;
  12. end.

Not recommended, but possible... ;D And leaks...

Did you even test your code? For me running it prints:

Code: [Select]
PS C:\fpc\git> .\testoutput\tdestr.exe
I am hidden

And there is no memory leak either.

Oh and the compiler also tells that it doesn't care about your trick:

Code: [Select]
udestr.pp(7,5) Warning: Destructor should be public
udestr.pp(7,16) Note: Virtual method "destructor destroy;" has a lower visibility (strict private) than parent class TObject (public)

For virtual methods the highest visibility between an override and the parent/original is used.

soerensen3

  • Full Member
  • ***
  • Posts: 211
Re: Hiding the destructor in class - Somehow possible?
« Reply #8 on: June 02, 2020, 10:22:11 pm »
Thanks for your good suggestions. Sorry it took me some time to answer because I'm very busy at the moment. I think I want to keep it as simple as possible so I'm raising some runtime errors.
It would be nice to have an exit condition in destroy in this case. However the interfaces would give a good solution as well, but I think it is too much only for the corner case of a user freeing the class manually.
Lazarus 1.9 with FPC 3.0.4
Target: Manjaro Linux 64 Bit (4.9.68-1-MANJARO)

jamie

  • Hero Member
  • *****
  • Posts: 3648
Re: Hiding the destructor in class - Somehow possible?
« Reply #9 on: June 02, 2020, 10:49:16 pm »
So I assume you want to ensure inheritance classes (Child classes that is) would be the only one
playing with this ?
 
 can't you put in a control field that only the child classes access and thus if things get out of wack have the destructor of the base class fire off an exception message if not property classed

 This field of course can not be in the public sector..

 You can't directly stop them from calling the destroy but you can force a fault that makes them give up that idea! ;)
The only true wisdom is knowing you know nothing

jamie

  • Hero Member
  • *****
  • Posts: 3648
Re: Hiding the destructor in class - Somehow possible?
« Reply #10 on: June 02, 2020, 11:38:20 pm »
Actually after thinking about this, It really does not matter..

because if somehow call the destructor at the base level it will still call the destructor at the top level..

 However, like I said, if the base class was created on its own then safe guards can be put in. For example place a value in the base that only gets set from inherited classes so when the destructor gets call and it cycles down to this last one it needs to test for a value.. it not yet set then it faults..
The only true wisdom is knowing you know nothing

jamie

  • Hero Member
  • *****
  • Posts: 3648
Re: Hiding the destructor in class - Somehow possible?
« Reply #11 on: June 02, 2020, 11:52:39 pm »
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button4Click(Sender: TObject);
  2. begin
  3.   Base := TbaseClass.Create;
  4. end;
  5. Constructor TbaseClass.Create;
  6. begin
  7.   AObject := Nil;
  8. end;
  9.  
  10. Destructor TBaseClass.Destroy;
  11. Begin
  12.   if Not Assigned(AObject) Then
  13.     Raise Exception.Create('You did not call this from a proper place')
  14.   Else
  15.   Inherited Destroy;
  16. End;
  17. Destructor TMainClass.Destroy;
  18. Begin
  19.  Inherited Destroy;
  20. end;
  21. COnstructor TMainClass.Create;
  22. Begin
  23.   Inherited create;
  24.   AObject := Self;
  25. End;                              
  26.  

Something like that..
 If the class was created at the base and destroyed at the base it will fault

 but if it was created from an inherited setup where as it sets that field that its accepted.

Just an idea. This will stop them from creating the base on their own...

 also you can put Abstract members in there too that will fault if they don't get setup in the descendants.
 
The only true wisdom is knowing you know nothing

jamie

  • Hero Member
  • *****
  • Posts: 3648
Re: Hiding the destructor in class - Somehow possible?
« Reply #12 on: June 03, 2020, 12:07:16 am »
and using the abstract method..
Code: Pascal  [Select][+][-]
  1.   TBaseClass = Class
  2.     Procedure SomethingNeededInBaseToWork; virtual abstract;
  3.     Constructor Create;
  4.     destructor Destroy; override;
  5.   end;
  6.  TMainClass = Class(TbaseClass)
  7.  Procedure SomethingNeededInBaseToWork; Override;
  8.   constructor Create;
  9.   Destructor  Destroy; override;
  10.  End;
  11.  
  12.  

This will make life tough on those trying to use the base directly..
The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 10516
Re: Hiding the destructor in class - Somehow possible?
« Reply #13 on: June 03, 2020, 08:27:02 am »
@PascalDragon
Indeed my mistake. My method works only for constructors.

 

TinyPortal © 2005-2018