Recent

Author Topic: Some observations on the use of RTTI  (Read 3752 times)

vfclists

  • Hero Member
  • *****
  • Posts: 1146
    • HowTos Considered Harmful?
Some observations on the use of RTTI
« on: October 05, 2024, 05:23:33 pm »
I came across this article on RTTI by Luiz Americo (where and how is he anyway?) - http://lazarusroad.blogspot.com/2010/05/discover-of-rtti.html and realized some applications of RTTI I never truly thought of.

I have been looking at RTTI in terms of setting the properties of controls, such as with the RTTI controls unit, but never in terms of passing any arbitrary data to a class in order to get its property setters and getters to operate on that data even if the property doesn't have a corresponding field in the object.

I read about how some projects which involving hooking up Lazarus to other languages like Python4Lazarus, VClua etc depend on RTTI, ExtendedRTTI etc and wonder how little of this has to do with updating controls and other UI stuff.

Is there more to RTTI than I think, and are there more examples of this kind of usage that has nothing to do with UI controls?

Have I been confusing RTTI for UI controls with the purpose and importance of RTTI in general?
Lazarus 3.0/FPC 3.2.2

egsuh

  • Hero Member
  • *****
  • Posts: 1471
Re: Some observations on the use of RTTI
« Reply #1 on: October 07, 2024, 08:09:47 am »
RTTI can be linked to any property, calling its setter and getter even if there are no corresponding field. Is this what you are wondering?

vfclists

  • Hero Member
  • *****
  • Posts: 1146
    • HowTos Considered Harmful?
Re: Some observations on the use of RTTI
« Reply #2 on: October 07, 2024, 02:05:02 pm »
RTTI can be linked to any property, calling its setter and getter even if there are no corresponding field. Is this what you are wondering?

Yes. Although it is logical when you think of it, it escaped me for a while and probably because most of the examples shown involved manipulating UI objects.
Lazarus 3.0/FPC 3.2.2

Thaddy

  • Hero Member
  • *****
  • Posts: 16018
  • Censorship about opinions does not belong here.
Re: Some observations on the use of RTTI
« Reply #3 on: October 07, 2024, 02:22:27 pm »
My complete example on the wiki is a console demo....
If I smell bad code it usually is bad code and that includes my own code.

vfclists

  • Hero Member
  • *****
  • Posts: 1146
    • HowTos Considered Harmful?
Re: Some observations on the use of RTTI
« Reply #4 on: October 07, 2024, 06:20:34 pm »
My complete example on the wiki is a console demo....

Link? Searching for "rtti console" is not revealing it.
Lazarus 3.0/FPC 3.2.2

vfclists

  • Hero Member
  • *****
  • Posts: 1146
    • HowTos Considered Harmful?
Re: Some observations on the use of RTTI
« Reply #5 on: October 07, 2024, 06:28:18 pm »
RTTI can be linked to any property, calling its setter and getter even if there are no corresponding field. Is this what you are wondering?

Yes. Although it is logical when you think of it, it escaped me for a while and probably because most of the examples shown involved manipulating UI objects.

Do you know of any examples which are less UI oriented?
Lazarus 3.0/FPC 3.2.2

Thaddy

  • Hero Member
  • *****
  • Posts: 16018
  • Censorship about opinions does not belong here.
Re: Some observations on the use of RTTI
« Reply #6 on: October 07, 2024, 06:35:45 pm »
ink? Searching for "rtti console" is not revealing it.
https://wiki.freepascal.org/Custom_Attributes

The attributes are still being worked on. i can probably simpyfy that example a bit.
And clarify that with the same attribute used like this, you get the day I was born:
Code: Pascal  [Select][+][-]
  1. [ADateTime(21237.0)]
You can see that the other constructor is called.
Attributes can have multiple constructors, so you can add what you need.

Another example I have is where you annotate a TBufDataSet with field attributes, so you can set e.g. FieldDefs at runtime from values that are defined as attributes at compile time.
You can change the main program body of the wiki examle to this and you can see there are more than one way to query the attribute value:
Code: Pascal  [Select][+][-]
  1.  //adapted from an unrelated Embarcadero example
  2. // rest of the code stays as is.
  3. var
  4.   LContext: TRttiContext;
  5.   LType: TRttiType;
  6.   LAttr: TCustomAttribute;
  7. begin
  8.   { Create a new Rtti context }
  9.   LContext := TRttiContext.Create;  
  10.   { Extract type information for TMyDateTimeClass }
  11.   LType := LContext.GetType(TypeInfo(TMyDateTimeClass));
  12.   { Search for the custom attribute and do some custom processing }
  13.   for LAttr in LType.GetAttributes() do
  14.     if LAttr is ADateTimeAttribute then
  15.       Writeln(DateTimeToStr(ADateTimeAttribute(LAttr).FArg));
  16.   { Destroy the context }
  17.   LContext.Free;
  18. end.
« Last Edit: October 07, 2024, 07:59:24 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

Warfley

  • Hero Member
  • *****
  • Posts: 1690
Re: Some observations on the use of RTTI
« Reply #7 on: October 07, 2024, 09:36:33 pm »
Do you know of any examples which are less UI oriented?
There are two man use cases for RTTI. First the most obvious one is data serialization and deserialization. For example, if you want to take a record and serialize it into json, right now you have to do it like this:
Code: Pascal  [Select][+][-]
  1. jsonObject.Strings['MyField1'] := MyRec.MyField1;
  2. jsonObject.Integers['MyField2'] := MyRec.MyField2;
  3. jsonObject.Arrays['MyArrayField'] := SerializeArray(MyRec.MyArrayField);
Extremely annoying and error prone. With Extended RTTI, where field names are written into the RTTI, you could write a serializer that will just take all public fields and writes them down, recursively descending into other records or arrays. Then it would just be:
Code: Pascal  [Select][+][-]
  1. SerializeRecord(@MyRec, TypeInfo(TMyRec));
And when you update your record, rename fields, etc. the serializer will still work because of RTTI.

This is especially useful combined with the custom attributes, where you could do things like that:
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyRecord = record
  3.     [SerializeName('Field1'), SerializeOptional('Default')]
  4.     FField1: String;
  5.     [SkipSerialize]
  6.     FHiddenField: Integer;
  7.   //...
so SerializeName would be an attribute to have a custom name, e.g. for backwards compatibility, or to not have the leading F as in this case. SerializeOptional could be an attribute to say when deserializing and this object is not there, fill it with 'Default'. SkipSerialize would tell the serializer that FHiddenField is an internal field not to be serialized into json.

This way you do not need to write a single line of (error prone) code to convert your data into other formats, as all the required information can be encoded into the definition of the type.

The second main use case is reflection. Basically altering the behavior of your code at runtime. For example you could have a module system where you scan your filesystem for a module (e.g. in form of a dynamic library) and if a new module is found the module is loaded and RTTI is used to e.g. load these function pointers into existing objects/classes at runtime without having to restart the application.

For example you could create a software where classes objects have logger fields, and on startup, the module loader would look if there is a logger module available, and if so, scan the classes, if any has a logger field, and dynamically load the logger into that field.

egsuh

  • Hero Member
  • *****
  • Posts: 1471
Re: Some observations on the use of RTTI
« Reply #8 on: October 08, 2024, 04:29:05 am »
Quote
https://wiki.freepascal.org/Custom_Attributes

In the second example of previous page, where does [ADateTime] come from? Should it be [ADateTimeAttribute]? I have added comments below.

Code: Pascal  [Select][+][-]
  1. program testattributes;
  2.    
  3. {$mode delphi}{$H+}{$M+}
  4. {$warn 5079 off} { turn warning experimental off }
  5. uses
  6.   sysutils, typinfo, rtti, classes;
  7.  
  8. type
  9.    ADateTimeAttribute = class(TCustomAttribute)
  10.    private
  11.      FArg:TDateTime;
  12.    public
  13.      constructor Create(const aArg: TDateTime);overload;
  14.      constructor Create(const aArg: String = {$I %DATE%});overload;
  15.     published
  16.      property Date:TDateTime read Farg;
  17.    end;
  18.  
  19.  
  20.    {This calls the second constructor }
  21.    [ADateTime]     // <== Where does this come from? not [ADateTimeAttribute]?
  22.    TMyDateTimeClass = class
  23.    private
  24.      FDateTime:TDateTime;
  25.    public
  26.      constructor create;
  27.    published
  28.      [ADateTime]   // <== And this
  29.      property Day:TDateTime read FDatetime write FdateTime;
  30.    end;
  31.  

Thaddy

  • Hero Member
  • *****
  • Posts: 16018
  • Censorship about opinions does not belong here.
Re: Some observations on the use of RTTI
« Reply #9 on: October 08, 2024, 09:50:31 am »
That is explained higher up in the wiki and in my comments of the complete wiki example too: you can omit the attribute suffix part. One usually does.
The code is correct.
Clear? Otherwise, please ask what is not clear. This is also Delphi compatible.
« Last Edit: October 08, 2024, 09:54:31 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

egsuh

  • Hero Member
  • *****
  • Posts: 1471
Re: Some observations on the use of RTTI
« Reply #10 on: October 08, 2024, 10:18:07 am »
Ah I see. There were explanations already.

Warfley

  • Hero Member
  • *****
  • Posts: 1690
Re: Some observations on the use of RTTI
« Reply #11 on: October 08, 2024, 10:49:45 am »
That is explained higher up in the wiki and in my comments of the complete wiki example too: you can omit the attribute suffix part. One usually does.
The code is correct.
Clear? Otherwise, please ask what is not clear. This is also Delphi compatible.
My question to this would be, if people are to lazy to type out ADateTimeAttribute, why name it ADataTimeAttribute and not just ADateTime to begin with? Code should not enforce naming conventions, the programmer should chose the names they seem fit and the compiler should not interfere with that by introducing new names for things without them being specifically declared.

Or to put it in a different way, the compiler should not silently add new symbols because it thinks it can do naming better than the programmer

Thaddy

  • Hero Member
  • *****
  • Posts: 16018
  • Censorship about opinions does not belong here.
Re: Some observations on the use of RTTI
« Reply #12 on: October 08, 2024, 11:09:15 am »
The attribute post-fix is (seems) only necessary to distinguish between a normal class and an attribute class
The attribute class has some special handling (like I showed with the multiple constructors).
As I understand it, it is to recognize an attribute, and how to interpret it:
[ADateTime] vs [ADateTime(DateTimeValue)] in my example reference the same attribute class.
The compiler knows it is an attribute class and based on how the attribute is declared it calls the proper constructor.
You do not instantiate an attribute class manually. That happens at runtime or compile time.
An Attribute needs not to be ever used and is only created when it is referenced.
« Last Edit: October 08, 2024, 11:23:34 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

Warfley

  • Hero Member
  • *****
  • Posts: 1690
Re: Some observations on the use of RTTI
« Reply #13 on: October 08, 2024, 01:00:09 pm »
Well... I don't think it's a good idea to have semantic embedded in naming conventions. Because in the end it should just be that, a convention. Also it should be mentioned that this is a complete novum for Pascal, there is not a single feature that implies semantic from naming. You can write your programs in any (natural) language (using define macros you can even translate keywords). If you impose meaning to the names, you also impose english as base language for your identifiers.
Especially as Attribute is a very common word that you may call your classes that just by "accident".

I mean the clearest way of doing that would just be a syntax feature:
Code: Pascal  [Select][+][-]
  1. TMyAttribute = attribute class ...
Or to not introduce new keywords, a synonym for "attribute" would be property, so adding something like this:
Code: Pascal  [Select][+][-]
  1. TMyAttribute = property class ...
would be completely trivial to add to the parser (like <10 lines of code to be added to the FPC kind of trivial)

I don't see any advantage in utilizing the naming scheme over something like above, and but I see tons of disadvantages

Thaddy

  • Hero Member
  • *****
  • Posts: 16018
  • Censorship about opinions does not belong here.
Re: Some observations on the use of RTTI
« Reply #14 on: October 10, 2024, 10:51:39 am »
It is also the proverbious Delphi compatibility..
If I smell bad code it usually is bad code and that includes my own code.

 

TinyPortal © 2005-2018