Friday, May 21, 2010

The discover of RTTI

I never got much interest, or knowledge, by Delphi/fpc RTTI. But the recent fuzz about the new RTTI features introduced in Delphi 2010 raised my curiosity even if the fpc provides only the old style RTTI. 

The opportunity to learn, and use, RTTI came when i figured the possibility to enhance/clear some of my code.

To show a generic TForm i created a very imaginative simple function:



function ShowForm(FormClass: TFormClass; Owner: TWinControl): TModalResult;
var
Form: TForm;
begin
Form := FormClass.Create(Owner);
try
Result := Form.ShowModal;
finally
Form.Destroy;
end;
end;

This little function works nice and save some boilerplate code but it's limited to forms that don't need to initialize a property before is called since i don't know class type before hand.

As stated before, messages can be used to notify with arbitrary information any TControl, so i created a variant of the ShowForm that takes two ordinal (LPARAM and WPARAM) parameters and send a CM_INIT message to the created TForm instance. The TForm descendant would need to add a CM_INIT message handler and interpret the TLMessage parameter.



function ShowForm(FormClass: TFormClass; Owner: TWinControl; WData: WPARAM = 0; LData: LPARAM = 0): TModalResult;
var
Form: TForm;
begin
Form := FormClass.Create(Owner);
try
Form.Perform(CM_INIT, WData, LData);
Result := Form.ShowModal;
finally
Form.Destroy;
end;
end;

So to set to true the MyBool field of a TForm descendant i would call:



ShowForm(TMyForm, nil, 1);

And in the CM_INIT handler:


procedure TMyForm.CMInit(var Msg: TLMessage);
begin
MyBool := (Msg.lParam = 1);
end;

It worked nice for simple variables like a integer or boolean, but things started to look clumsy when i needed to pass a variable of string or TObject type. Furthermore there's the limitation of restricted number of variables and the danger of the need to assume the meaning of the Msg(TLMessage) fields


There's where the RTTI ability to set arbitrary properties came in hand. All i needed to do is publish a property in the TForm descendant and set it through RTTI functions. This way i got type safety, unlimited number of parameters/variables and clearer code.


The new ShowForm interface:



function ShowForm(FormClass: TFormClass; Owner: TWinControl; FormProperties: array of const): TModalResult;

FormProperties is a array of const where the even items are the property names and the odd items, the property values.


What about RTTI? Pretty simple and direct:



//stripped code (no type check, no array of const parsing)
uses typinfo;
[..]
ClassInfo := Form.ClassInfo;
PropInfo := GetPropInfo(ClassInfo, PropertyName);
case PropInfo^.PropType^.Kind of
tkAString, tkSString:
SetStrProp(Form, PropInfo, StrPropertyValue);
tkInteger:
SetOrdProp(Form, PropInfo, IntPropertyValue);
tkBool:
SetOrdProp(Form, PropInfo, Integer(BoolPropertyValue));
end;
[..]

Now to set MyBool property of TMyForm i do:



ShowForm(TMyForm, nil, ['MyBool', True]);

A lot clearer no? ;-)

5 comments:

Unknown said...

Simply fantastic!

Thanks Luiz :)

Unknown said...

A simple sample with RTTI:

http://www.silvioprog.com.br/dicas/verificarexistenciadepropriedade

etrusco said...

Deveria ser "ShowModalForm", não? ;-)

Luiz Américo said...

Inicialmente tinha uma opção para escolher entre Show e ShowModal, até que eu notei que o valor retornado (ModalResult) seria errado no caso de usar Show. Eu mantive o nome para mantê-lo pequeno.

Anonymous said...

Brilliant.. Outstanding.. I’ll safe your blog and take the feeds also…I am cheerful to locate so much helpful knowledge right here within the blog post. Thanks for distribution.

Stationery design