Recent

Author Topic: Typecasting issues  (Read 1423 times)

Flea

  • New member
  • *
  • Posts: 9
Typecasting issues
« on: April 03, 2025, 04:23:07 pm »
Two queries!

1) I'm trying to typecast a TEdit control to a TMyEdit so as I can access the protected method KeyPress in its ancestral class (TCustomEdit etc) as in the code below. When it get to the type cast "(Edit1 as TMyEdit).KeyPress" near the end of the code, I get an Invalid Typecast exception. Why is that? Ive tried forcing the typecast with "TMyEdit(Edit1).KeyPress" and this fails with the same exception (compiled under release mode or debug mode).

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode Delphi}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.   TForm1 = class(TForm)
  12.     Edit1: TEdit;
  13.     procedure FormCreate(Sender: TObject);
  14.   end;
  15.  
  16. var
  17.   Form1: TForm1;
  18.  
  19. implementation
  20.  
  21. {$R *.lfm}
  22.  
  23. { TForm1 }
  24.  
  25. type
  26.   TMyEdit=class(TEdit)
  27.   public
  28.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  29.   end;
  30.  
  31. procedure TMyEdit.KeyDown(var Key: Word; Shift: TShiftState);
  32. begin
  33.   inherited;
  34. end;
  35.  
  36. procedure TForm1.FormCreate(Sender: TObject);
  37. var
  38.   LKey: word;
  39. begin
  40.   (Edit1 as TMyEdit).KeyDown(LKey, [ssShift]);
  41. end;
  42.  
  43. end.
  44.  

2) When digging in deeper, I wrote the following test code. When I step into the code near the end "TMySuperClass(LC).DoSomething;" the code execution doesn't step into the TMySuperClass.DoSomething as expected, its steps into the ancestral TMyClass.DoSomething ? Again, does this not work the way Delphi does. I was expecting it to step into TMySuperClass.DoSomething.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode Delphi}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.   TForm1 = class(TForm)
  12.     procedure FormCreate(Sender: TObject);
  13.   end;
  14.  
  15. var
  16.   Form1: TForm1;
  17.  
  18. implementation
  19.  
  20. {$R *.lfm}
  21.  
  22. { TForm1 }
  23.  
  24.  
  25. type
  26.   TMyClass=class(Tobject)
  27.   protected
  28.     procedure DoSomething;virtual;
  29.   end;
  30.  
  31.   TMySuperClass=class(TMyClass)
  32.   public
  33.     procedure DoSomething;override;
  34.   end;
  35.  
  36. { MyClass }
  37. procedure TMyClass.DoSomething;
  38. begin
  39.   Inc(gDummy);
  40. end;
  41.  
  42. { MySuperClass }
  43. procedure TMySuperClass.DoSomething;
  44. begin
  45.   Inc(gDummy);
  46.   inherited;
  47. end;
  48.  
  49.  
  50. procedure TForm1.FormCreate(Sender: TObject);
  51. var
  52.   LC: TMyClass;
  53. begin
  54.   gDummy:=0;
  55.   LC:=TMyClass.Create;
  56.   TMySuperClass(LC).DoSomething;
  57.   LC.Free;
  58. end;
  59.  
  60. end.
  61.  

jamie

  • Hero Member
  • *****
  • Posts: 7492
Re: Typecasting issues
« Reply #1 on: April 03, 2025, 04:58:21 pm »
Yes that looks normal to me you never actually created that class but you're using it for a Typecast and if the virtual method so all it's doing is returning back to the very first virtual method that exists which is not your Typecast
The only true wisdom is knowing you know nothing

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1570
    • Lebeau Software
Re: Typecasting issues
« Reply #2 on: April 03, 2025, 05:00:53 pm »
The typecast using the 'as' operator fails because the Edit1 pointer is not pointing at a TMyEdit object, it is pointing at a standard TEdit object. Just because you declared the TMyEdit class does not magically turn Edit1 from a TEdit object into a TMyEdit object.

In Delphi, you can use an interposer class (not sure if it works in FreePascal, too?), eg:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode Delphi}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.   TEdit=class(StdCtrls.TEdit)
  12.   public
  13.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  14.   end;
  15.  
  16.   TForm1 = class(TForm)
  17.     Edit1: TEdit;
  18.     procedure FormCreate(Sender: TObject);
  19.   end;
  20.  
  21. var
  22.   Form1: TForm1;
  23.  
  24. implementation
  25.  
  26. {$R *.lfm}
  27.  
  28. { TForm1 }
  29.  
  30. procedure TEdit.KeyDown(var Key: Word; Shift: TShiftState);
  31. begin
  32.   inherited;
  33. end;
  34.  
  35. procedure TForm1.FormCreate(Sender: TObject);
  36. var
  37.   LKey: word;
  38. begin
  39.   Edit1.KeyDown(LKey, [ssShift]);
  40. end;
  41.  
  42. end.

Otherwise, typecasting without the 'as' operator should work just fine (unless FreePascal silently turns the cast into the 'as' operator - Delphi doesn't do that). In which case, you don't need to override the ancestor method in order to call it.  Just use an accessor class to bring the object's protected methods into the calling unit's scope, eg:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode Delphi}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.   TForm1 = class(TForm)
  12.     Edit1: TEdit;
  13.     procedure FormCreate(Sender: TObject);
  14.   end;
  15.  
  16. var
  17.   Form1: TForm1;
  18.  
  19. implementation
  20.  
  21. {$R *.lfm}
  22.  
  23. type
  24.   TEditAccess=class(TEdit)
  25.   end;
  26.  
  27. { TForm1 }
  28.  
  29. procedure TForm1.FormCreate(Sender: TObject);
  30. var
  31.   LKey: word;
  32. begin
  33.   TEditAccess(Edit1).KeyDown(LKey, [ssShift]);
  34. end;
  35.  
  36. end.
« Last Edit: April 04, 2025, 02:50:50 am by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

PascalDragon

  • Hero Member
  • *****
  • Posts: 6272
  • Compiler Developer
Re: Typecasting issues
« Reply #3 on: April 03, 2025, 09:10:12 pm »
1) I'm trying to typecast a TEdit control to a TMyEdit so as I can access the protected method KeyPress in its ancestral class (TCustomEdit etc) as in the code below. When it get to the type cast "(Edit1 as TMyEdit).KeyPress" near the end of the code, I get an Invalid Typecast exception. Why is that? Ive tried forcing the typecast with "TMyEdit(Edit1).KeyPress" and this fails with the same exception (compiled under release mode or debug mode).

Using as is wrong here, because that checks the correct instance of the class. You need to use a normal typecast instead and make sure that $ObjectChecks is disabled (though for a virtual method this wouldn't work anyway, see your second question):

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   LKey: word;
  4. begin
  5.   {$Push}{$ObjectChecks Off}
  6.   TMyEdit(Edit1).KeyDown(LKey, [ssShift]);
  7.   {$Pop}
  8. end;

2) When digging in deeper, I wrote the following test code. When I step into the code near the end "TMySuperClass(LC).DoSomething;" the code execution doesn't step into the TMySuperClass.DoSomething as expected, its steps into the ancestral TMyClass.DoSomething ? Again, does this not work the way Delphi does. I was expecting it to step into TMySuperClass.DoSomething.

I would be really surprised if this would work in Delphi, because the point of a virtual method is that it will be called through the VMT which will not change if you typecast the type.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1570
    • Lebeau Software
Re: Typecasting issues
« Reply #4 on: April 04, 2025, 02:55:37 am »
You need to use a normal typecast instead and make sure that $ObjectChecks is disabled...

Oh yeah, I keep forgetting about {$ObjectChecks}! That doesn't exist in Delphi.

I would be really surprised if this would work in Delphi, because the point of a virtual method is that it will be called through the VMT which will not change if you typecast the type.

Agreed.  That code is definitely wrong, trying to force a virtual call on the wrong object type.  In that example, this line:

Code: Pascal  [Select][+][-]
  1. LC:=TMyClass.Create;

Would need to be changed to this:

Code: Pascal  [Select][+][-]
  1. LC:=TMySuperClass.Create;

Then the rest of the code is fine (but defeats the purpose of using a virtual method).
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Flea

  • New member
  • *
  • Posts: 9
Re: Typecasting issues
« Reply #5 on: April 05, 2025, 01:39:46 pm »
Pascaldragon: Yes, the {$objectchecks off} worked like a charm !

Sorry Remy, your are absolutely correct there. My second example was indeed creating an instance of the wrong class. Doh!  ::)

Thanks for your help guys!

 

TinyPortal © 2005-2018