Jump to content
CoMPi74

Generic event handler for a generic class

Recommended Posts

Posted (edited)

Hi there, 

 

I have a component like this: 

TCustomEdit<T: record> = class;
TOnValidate<T: record> = function (ASender: TCustomEdit<T>; AValue: T): Boolean of object;

TCustomEdit<T: record> = class(TEdit)
private
  fOnValidate: TOnValidate<T>;
  // code here
published
  property OnValidate: TOnValidate<T> read fOnValidate write fOnValidate;
end;

TIntEdit = class(TCustomEdit<Integer>)
  // code here
end;

The code compiles and TIntEdit registers without any complaints. But, when I want to drop the component onto a form, Delphi crashes with AV. What I am missing? 

I tested it on Delphi XE4 and Delphi 10.4 CE. It's a bug, feature of just "by design"? @Stefan Glienke, will you help? Anyone?

Edited by CoMPi74

Share this post


Link to post

Just a guess, but I suspect the Object Inspector cannot cope with the generic event.

Share this post


Link to post
3 minutes ago, Uwe Raabe said:

Just a guess, but I suspect the Object Inspector cannot cope with the generic event.

I think so too, but it is quite strange, because OnChange of TIntEdit is just TOnChange(ASender: TIntEdit, AValue: Integer). Does not it?

Share this post


Link to post

Strictly speaking it is TOnChange(ASender: TCustomEdit<Integer>; AValue: Integer), but I suspect a general lack of OI support for that.

 

You should file a QP report for that.

Share this post


Link to post

It's been a while since I tried doing generic based controls, but I think I ran into the same issues.

In the end, I changed my approach to using proxy classes that simply hook into the control at runtime and take over its events.

Share this post


Link to post

I've also had bad experience in the past, and the bug should be re-reported. Does making an 'intermediate' concrete type like the following do anything:

TBaseIntEdit = class abstract(TCustomEdit<Integer>);
TIntEdit = class(TBaseIntEdit)
...

Share this post


Link to post
1 hour ago, darnocian said:

I've also had bad experience in the past, and the bug should be re-reported. Does making an 'intermediate' concrete type like the following do anything:


TBaseIntEdit = class abstract(TCustomEdit<Integer>);
TIntEdit = class(TBaseIntEdit)
...

Nope. It does not change anything 😕

Share this post


Link to post

It is look like none of a generic property is visible in Object Inspector. But, of course, they are accessible from code.

 

TCustomEdit<T: record> = class;

TEvent1 = procedure (AValue: Integer) of object;
TEvent2<V: record> = procedure (AValue: V) of object;
TEvent3<V: record> = procedure (ASender: TCustomEdit<V>; AValue: V) of object;

TCustomEdit<T: record> = class(TEdit)
public
  fEvent1: TEvent1;
  fEvent2: TEvent2<T>;
  fEvent3: TEvent3<T>;
published
  property Event1: TEvent1    read fEvent1 write fEvent1; // Accesible from Object Inspector and from code
  property Event2: TEvent2<T> read fEvent2 write fEvent2; // Only from code
  property Event3: TEvent3<T> read fEvent3 write fEvent3; // Only from code
end;

TEditEx = class(TCustomEdit<Integer>);

...

RegisterComponents('Test controls', [TEditEx]);

 

BTW. It looks like Delphi (10.4 CE) crashes because of CnMemProfProject wizard (CnPack, v. 1.2.0.1040 Unstable). But it is just an intermediate reason. The AV comes from System.TypInfo.GetPropInfos so definitely something is up.

SharedScreenshot.jpg

Share this post


Link to post

I thought I should try properly to try reproduce as well rather than speculate. 😉  The following worked for me:

type
  TEvent = procedure (avalue:integer)of object;
  TEvent2<T:record> = procedure (avalue:t) of object;
  TCustomEdit<T:record> = class(TEdit)
  private
    fevent1:tevent;
    fevent2:TEvent2<integer>;
      published

        property event1:tevent read fevent1 write fevent1;
        property event2:tevent2<integer> read fevent2 write fevent2;

  end;

  TEditEx = class(TCustomEdit<integer>)
  end;

image.thumb.png.cd13ea66c678979afa2adda4b48cacc5.png

ide was fine on my end.
 

 

Share this post


Link to post

@darnocian This is not the case I presented. I wanted to have an event handler which generic parameter T is same as in the class definition. I mean fevent2 should be rather TEvent2<T> instead of TEvent2<integer>. Did you try such a case? I did :/

I need it because I want to implement validation mechanism in TCustomEdit<T>.Change method, like in the code below:

TEvent<V: record> = procedure (AValue: V) of object;

TCustomEdit<T: record> = class(TEdit)
public
  fValue: T;
  fEvent: TEvent<T>;
protected
  procedure Change; override;
published
  property Event: TEvent<T> read fEvent write fEvent; 
end;
  
procedure TIMEdit<T>.Change;
begin
  if Assigned(fEvent) then fEvent(fValue);
end;


  

 

Share this post


Link to post
Guest

Sorry, but i think you are missing critical point here

7 hours ago, CoMPi74 said:

I mean fevent2 should be rather TEvent2<T> instead of TEvent2<integer>.

"TEvent2<T>" does not exist as code to be handled or inspected, while "TEvent2<Integer>" is existing creature, well known and defined.

Think of it as a macro or virtual imaginary code, something like "array of",  of what ?!

Share this post


Link to post

I beg to disagree here. If it is seen as an invalid construct the compiler should complain. As long as the compiler is fine with such a construct, there are only implementation details in Object Inspector responsible for the crash. There is nothing except missing developer time that should prevent to fix those. We can argue about the priority, though.

  • Like 1

Share this post


Link to post
Guest
48 minutes ago, Uwe Raabe said:

I beg to disagree here. If it is seen as an invalid construct the compiler should complain. As long as the compiler is fine with such a construct, there are only implementation details in Object Inspector responsible for the crash.

Can't agree more, that is not a bug with the compiler, but if we need to point to wrong doing or lets call it a bug then it is with the IDE, but on other hand published identifier is essential for the object inspector which we can blame, and it can be warned about by compiler.

Share this post


Link to post
Posted (edited)
11 hours ago, CoMPi74 said:

This is not the case I presented

Sorry, I think I was a dufus and didn't pay attention to what i was doing. 😉

 

Reproducing correctly with event2:TEvent2<T> , the event2 is not exposed as you highlighted...

 

It would be useful to raise the Quality Portal issue so that this type of scenario works... Generics can be really powerful, but unfortunately there are lots of bugs with the implementation. ;(

Edited by darnocian

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×