Newsgroup: it.comp.lang.delphi
Titolo: [d5] RTTI di una classe che eredita da tobject

Salve.
Facendo seguito al post legato alle inesistenti RTTI dei tipi records, sono
passato a usare le classi.
facendo varie ricerche ho trovato vari blocchi di codice per ciclare su
property di una classe.
Ma il mio problema è che, data una classe del tipo:
TMiaClass = class
public
  constructor Create;
  destructor Destroy; override;
published
  property Qualcosa: integer read FQualcosa write SetQualcosa;
end;
se scrivo del codice tipo:
..
var
  m: TMIaClass;
  p: pointer:
begin
  m := TMiaclasse.Create;
  p := m.ClassInfo
..
end;
mi trovo sempre che p è NIL.
Tutti gli esempi che ho trovato sul web partono da classinfo per accedere
alle RTTI.
Qualcuno mi sa dare qualche indicazione?
Tnx
A

mi trovo sempre che p è NIL.
ClassInfo è una "class function", un metodo legato all'intera classe e
non all'oggetto specifico ed è usato internamente da Delphi.
Ci sono altri metodi per accedere al RTTI, guarda la unit (non
documentata) TypInfo.
-- 
Andrea Laforgia
Open Delphi Manual
http://groups.yahoo.com/group/delphi_open_manual/

 documentata) TypInfo.
P.S. Non vorrei sbagliarmi, ma mi pare che la RTTI si possa avere solo da
TPersistent in poi, poiché TObject è compilata con {$M-}
Andrea

"Alberto Salvati" ha scritto nel messaggio
[CUT]
Su Delphi 6 Developer's Guide di Teixeira/Pacheco al capitolo 10 c'č un
esempio, dove sostanzialmente presenta in una listbox un elenco di classi
della VCL e permette di analizzare le caratteristiche di quella selezionata.
L'uso di ClassInfo, avviene:
var
    ClassTypeInfo: PTypeInfo;
    ClassTypeData: PTypeData;
...
ClassTypeInfo := AClass.ClassInfo;
ClassTypeData := GetTypeData(ClassTypeInfo);
...
di seguito utilizza i membri della struttura TTypeData.
...
nell'Itialization della Unit registra tutte le classi di cui vuole prelevare
le informazioni RTTI
initialization
    RegisterClasses([TApplication, TButton, TForm, TListBox, TPaintBox]);
end;
In effetti č piuttosto complesso per potersi esaurire su un post; se vuoi
posso mandarti l'intero capitolo in formato PDF.
Ciao,
Gianluca.

 Qualcuno mi sa dare qualche indicazione?
Di fatto il metodo ClassInfo torna un pointer inutile, dato che
l'implementazione della tabella cui punta puo' cambiare ad ogni versione
del compilatore (e prob. lo fa): usare qual puntatore sarebbe come
pretendere di usare Pointer(Longint(Self) -4) per accedere alla VMT
della classe. Le funzioni di TypInfo, invece, sono piu' o meno sempre le
stesse.
Inoltre, almeno sino a D6, la RTTI lavora solo sulle classi discendenti
da una compilata con abilitato il flag di creazione delle RTTI ($M, se
non sbaglio). Cio' in pratica significa che TTuaClasse dovrebbe essere
_almeno_ TPersistent.
Dopo di che, se vuoi ad esempio settare il valore di Qualcosa tramite
RTTI scriverai qualcosa del genere:
uses TypInfo;
SetOrdProp(anInstOfTuaClasse, 'Qualcosa', anIntegerValue);
Se non sei certo che esista una certa proprieta' nella tua classe:
var pi: PPropInfo;
...
pi := GetPropInfo(TTuaClasse, 'Qualcosa');
if (pi <> nil) then begin
  case pi^.PropType.Kind of
    tkInteger, tkChar, tkEnumeration: SetOrdProp(anInstance, 'Qualcosa',
anIntVal);
    tkString: SetStrProp(anInstance, 'Qualcosa', aString);
    else raise EMyRTTIException.Create('Tipo non supportato');
  end;
end;
Vedi anche uno degli articoli demo di TDM:
http://www.thedelphimagazine.com/samples/1614/1614.htm
Oltre, naturalmente, a EOS (su sourceforge) dove l'RTTI viene usata sia
per lo streaming su XML sia per la visualizzazione in controlli CLX std.
Saluti
Paolo Arosio

_almeno_ TPersistent.
Be', non è propriamente così.  Una classe non discendente da
TPersistent e compilata col flag {$M+} ha comunque la possibilità di
implementare membri "published" (e quindi consentire l'RTTI).


 implementare membri "published" (e quindi consentire l'RTTI).
Certo, apposta mettevo "in pratica" (e non semplicemente *deve*): nel
senso che e' piu' pratico (inoltre, con TPersistent arriva anche lo
streaming: sara' mica che le due cose son collegate?).
Saluti
Paolo Arosio

stesse.
Ok. Prendo atto di questa cosa.
Ma, mi chiedo (magari sono in crash xche sto nei casini--)....
a) Quali alternative ci sono per ottenere un qualcosa da dare in pasto come
primo parametro alla:
procedure GetPropInfos(TypeInfo: PTypeInfo; PropList: PPropList); assembler;
b) xche, cazzarola, tutti gli esempi che ho trovato sul web usando la
qualcosa.ClassInfo?
E dico tutti, non uno solo!!
3: The DeerBear
Senza la tua dritta avrei concluso ben poco: 1000 grazie.
Bye
A

 E dico tutti, non uno solo!!
Nun zaprei, non ne ho mai visto alcuno.
Ma stai sperimentando o devi risolvere un problema particolare?
Saluti
Paolo Arosio

 Ma stai sperimentando o devi risolvere un problema particolare?
devo produrre x ieri un qualcosa che ciclando sulle prop di alcune classi
che ereditano da una classe base sforna una lista di nomi prop, tipo,
lunghezza e valore.
?ste cose le metto poi in una mia classe che incapsula una tlist x farci
altre cose.
Tnx
A

ereditare da tpersistent.
*Conviene* ereditare da TPersistent se il tuo oggetto sfrutta le
caratteristiche di un TPersistent.  Ma nessuno ti vieta di abilitare
il flag {$M+} e compilare ugualmente la tua classe.  Esattamente come
i programmatori Borland hanno fatto per TPersistent che č, anch'esso,
derivato da TObject.


 altre cose.
Io faccio così:
function GetPropListUnsorted(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds;
PropList: PPropList): Integer;
var
  I, Count: Integer;
  PropInfo: PPropInfo;
  TempList: PPropList;
begin
  Result := 0;
  Count := GetTypeData(TypeInfo)^.PropCount;
  if Count > 0 then
  begin
    GetMem(TempList, Count * SizeOf(Pointer));
    try
      GetPropInfos(TypeInfo, TempList);
      for I := 0 to Count - 1 do
      begin
        PropInfo := TempList^[I];
        if PropInfo^.PropType^.Kind in TypeKinds then
        begin
          if PropList <> nil then
            PropList^[Result] := PropInfo;
          Inc(Result);
        end;
      end;
    finally
      FreeMem(TempList, Count * SizeOf(Pointer));
    end;
  end;
end;
Per recuperare la lista delle published properties di un oggetto.
Poi, data la classe:
  TMiaClasse = class(TPersistent)
  private
    fData       : TDateTime;
    fStringa    : string;
    fIntero     : integer;
  public
    constructor Create;
  published
    property Data:TDateTime read fData write fData;
    property Stringa:string read fStringa write fStringa;
    property Intero:integer read fIntero write fIntero;
  end;
constructor TMiaClasse.Create;
begin
    inherited Create;
    fData:=Now;
    fStringa:='Hello World!';
    fIntero:=1974;
end;
E' sufficiente:
procedure TForm1.Button1Click(Sender: TObject);
var
    numprop : integer;
    i       : integer;
    proplist: TPropList;
begin
    try
        mio:=TMiaClasse.Create;
numprop:=GetPropListUnsorted(TypeInfo(TMiaClasse),[tkInteger,tkString,tkLStr
ing],@proplist);
        Memo1.Clear;
        with Memo1, Lines do
            for i:=0 to numprop-1 do
            begin
                case proplist[i].PropType^.Kind of
                    tkString,tkLString:
Add(format('%s=%s',[proplist[i].Name,GetStrProp(mio,proplist[i].Name)]));
                    tkInteger:
Add(format('%s=%s',[proplist[i].Name,GetPropValue(mio,proplist[i].Name,True)
]));
                end;
            end;
    finally
        FreeAndNil(mio);
    end;
end;
In questo modo, nel memo ottengo:
Stringa=Hello World!
Intero=1974
Spero sia quello che ti serve.
Saluti,
Carmine

TPersistent in poi, poiché TObject è compilata con {$M-}
Ti sbagli.


lunghezza e valore.
Per questo si usano le funzioni contenute in TypInfo.pas, come ti
avevo già suggerito.


streaming: sara' mica che le due cose son collegate?).
Per lo streaming č strettamente necessario accedere al RTTI, quindi il
fatto che le due cose siano collegate č fuori di dubbio.  Quello che
molti pensano č che per avere RTTI č _necessario_ ereditare da
TPersistent.  Non č cosě.
p.s.: "in pratica" <> "č piů pratico" ;-)


 prop, tipo, lunghezza e valore.
Vedi l'esempio sotto (form con un pulsante ed una listbox); inoltre, sul
DDH di Cantu' c'e' un capitolo dedicato alla bisogna. Il codice dovrebbe
essere scaricabile (v. Capitolo 16: Object debugger).
Saluti
Paolo Arosio
uses TypInfo;
procedure TForm1.Button1Click(Sender: TObject);
var PropList: PPropList;
    PropInfo: PPropInfo;
    TypeInfo: PTypeInfo;
    i, PropCount: integer;
    s, propName: string;
begin
  PropCount := GetPropList(Sender, PropList);
  ListBox1.Clear;
  if Sender is TComponent then
    ListBox1.Items.Add('Property of ' + Sender.ClassName + ' ' +
TComponent(Sender).Name)
  else
    ListBox1.Items.Add('Property of ' + Sender.ClassName + ' @ ' +
InttoHex(Longint(Sender), 8));
  if PropCount > 0 then try
    for i := 0 to PropCount-1 do begin
      PropInfo := PropList^[i];
      TypeInfo := PropInfo^.PropType^;
      PropName := PropInfo^.Name;
      s := PropName;
      case TypeInfo^.Kind of
        tkInteger, tkChar, tkEnumeration: begin
          s := s + ' = ' + inttostr(GetOrdProp(Sender, PropName));
          if UpperCase(TypeInfo^.Name) = 'BOOLEAN' then
            s := s + ' or, better, ' +
BoolToStr(Boolean(GetOrdProp(Sender, PropName)), True);
        end;
        tkFloat: s := s + ' = ' + Format('%1.5f', [GetFloatProp(Sender,
PropName)]);
        tkString, tkWChar, tkLString, tkWString: s := s + ' = ' +
GetStrProp(Sender, PropName);
        tkClass: s := s + ' is a class type ' + TypeInfo^.Name;
        tkMethod: s := s + ' is a method of type ' + TypeInfo^.Name;
        else s := s + ' is of type ' + TypeInfo^.Name + ' and its value
is ' + GetPropValue(Sender, PropName, True);
      end;
      ListBox1.Items.Add(s);
    end;
  finally
    FreeMem(PropList);
  end;
end;

 p.s.: "in pratica" <> "è più pratico" ;-)
ma "in pratica" = "e' uso comune".
Ti lascio l'ultima parola in questo trefolo.
Saluti
Paolo Arosio

Ti lascio l'ultima parola in questo trefolo.
Fai bene, perché "in pratica" vuol dire "in termini pratici devi fare
così", non significa che stai indicando un uso comune.  Sembra una
sciocchezza, ma se c'è confusione sull'argomento (come per chi è
convinto che *sia indispensabile* ereditare da TPersistent) è proprio
perché molti ricevono risposte del genere.  


   PropCount := GetPropList(Sender, PropList);
io ho una riga simile che il compilatore mi segnala come errore:
PropCount := GetPropList(AObject, PropList);
Il prototipo, preso da TypInfo di d5 enterprise č:
function GetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds;
  PropList: PPropList): Integer;
Mi spieghi come fai a passare l'handle di un oggetto come PTypeInfo?
Bye
A

 Mi spieghi come fai a passare l'handle di un oggetto come PTypeInfo?
In D6, per quelli come me, hanno messo in overload anche le seguenti
function GetPropList(TypeInfo: PTypeInfo; out PropList: PPropList):
Integer;
begin
  Result := GetTypeData(TypeInfo)^.PropCount;
  if Result > 0 then
  begin
    GetMem(PropList, Result * SizeOf(Pointer));
    GetPropInfos(TypeInfo, PropList);
  end;
end;
function GetPropList(AObject: TObject; out PropList: PPropList):
Integer;
begin
  Result := GetPropList(PTypeInfo(AObject.ClassInfo), PropList);
end;
che praticamente fanno quel che fa il codice postato da Carmine Moleti.
Saluti
Paolo Arosio

 il flag {$M+} e compilare ugualmente la tua classe.
Ok. chiedo venia.
Devo aver male interpretato cio' che è scirtto nella guida di delphi5.
Bye
A

>
numprop:=GetPropListUnsorted(TypeInfo(TMiaClasse),[tkInteger,tkString,tkLStr
ok. tnx!
Ma adesso mi chiedo se a monte della tua funzione ne ho un altra che
richiama la tua e che riceve in ingresso una istanza di una classe di cui
devo processare le property, come faccio a estrare il TypeInfo dalla
istanza?
Se scrivo:
PTYPEINFO(AOBJECT.CLASSTYPE) la funzione va in AV.
function FaiQualcosa(AObject: TObject);
var
  lista: PProplist;
begin
numprop:=GetPropListUnsorted(TypeInfo(AOBJECT.????),[tkInteger,tkString,tkLS
tr
Bye
A

sob! :((
nel titolo del post ho indicato che uso delphi5 enterprise.
E qui non esiste l'overload di cui parli.
A

 PTYPEINFO(AOBJECT.CLASSTYPE) la funzione va in AV.
Perchè ClassType non è quello che ti serve. Se analizzi TypeInfo.pas
noterai che tutte le funzioni RTTI che accettano come parametro TObject,
poi fanno riferimento a ClassInfo!
Il codice allora diventa...
...
numprop:=GetPropListUnsorted(PTypeInfo(AObject.ClassInfo),[...],@proplist);
...
Ho visto che altre funzioni si rifanno a ClassInfo, quindi non so quanto sia
"pericoloso"
appoggiarsi a ClassInfo.
Saluti

 E qui non esiste l'overload di cui parli.
Ho postato il corpo delle funzioni apposta.
Saluti
Paolo Arosio

 A
Andrea

:.((
sara' che ho bisongo di ferie, ma in typinfo gli unici overload che ho
trovato sono relativi alla lettura/scrittura dei valori e delle
caratteristiche di una property dato il suo nome, tipo isPublished.
Poi ci sta questo:
function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string):
PPropInfo; overload;
function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string; AKinds:
TTypeKinds): PPropInfo; overload;
ma quello di cui parla paolo io non lo trovo.
Bye
A

 caratteristiche di una property dato il suo nome, tipo isPublished.
In D5 Ent. non esiste l'overload delle funzioni riportate da Paolo.
Infatti, lui ha riportato il corpo per un eventuale "integrazione".

 In D5 Ent. non esiste l'overload delle funzioni riportate da Paolo.
Allora non ho le traveggole, o almeno, non questa volta: respiro!
Bye
A

Scusate un secondo...
Sicuramente mi sarò perso qualcosa.... ma comunque.
Ho provato così al volo a recuperare la lista delle proprietà di un
oggetto derivato da TPersistent...
  TMyObj = class(TPersistent)
  private
    FName: string;
  published
    property Name : string read FName write FName;
  end;
nell'initialization ho scritto:
initialization
  RegisterClasses([TMyObj]);
uso la seguente funzione per smazzarmi le proprietà...
procedure GetClassProperties(AClass: TObject; AStrings: TStrings);
{ This method retrieves the property names and types for the given
object
and adds that information to the AStrings parameter. }
var
  PropList: PPropList;
  ClassTypeInfo: PTypeInfo;
  ClassTypeData: PTypeData;
  i: integer;
  NumProps: Integer;
begin
  ClassTypeInfo := AClass.ClassInfo;
  ClassTypeData := GetTypeData(ClassTypeInfo);
  if ClassTypeData.PropCount <> 0 then
  begin
    // allocate the memory needed to hold the references to the
TPropInfo
    // structures on the number of properties.
    GetMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
    try
      // fill PropList with the pointer references to the TPropInfo
structures
      GetPropInfos(AClass.ClassInfo, PropList);
      for i := 0 to ClassTypeData.PropCount - 1 do
        // filter out properties that are events ( method pointer
properties)
        if not (PropList[i]^.PropType^.Kind = tkMethod) then
          AStrings.Add(Format('%s: %s', [PropList[i]^.Name,
            PropList[i]^.PropType^.Name]));
    finally
      FreeMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
    end;
  end;
end;
se vogli la lista lelle proprietà scrivo....
GetClassProperties(SomeComp, ATStringListClass);
L'esempio funziona e non mi da alcun problema. ???
Acqua
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 oggetto derivato da TPersistent...
Usando D7 puoi semplificare la tua procedura così:
var
  pListaProp: PPropList;
  nTot, i: integer;
begin
  Memo1.Clear;
  nTot := GetPropList(Self, pListaProp);
  try
    for i:=0 to Pred(nTot) do
      if (pListaProp^[i].PropType^^.Kind in tkProperties) then
        Memo1.Lines.Add(pListaProp^[i].Name+
                                      '='+VarToStr(GetPropValue(Self,
pListaProp^[i].Name))+
                                      ' ['+
pListaProp^[i].PropType^^.Name+']');
  finally
    FreeMem(pListaProp);
  end;
end;
Se avete letto il thread "RTTI Round Table Discussion" sui ng borland
(potete farlo anche da web grazie a 
www.delphiedintorni.it
 ):
http://www.delphiedintorni.it/geoxml/forumgetthread?groupname=borland.public
.delphi.oodesign&messageid=
3f1806cf%2...@newsgroups.borland.com
&displaymode=a
ll
vedrete che Thorpe spesso sottolinea il fatto che per essere certi di non
avere dipendenze dalla versione del compilatore (visto che le strutture dati
usate per l'RTTI sembrano cambiare spesso) bisogna cercare di usare le
funzioni di TypInfo (senza accedere troppo direttamente alle strutture dati
che stanno sotto). Questo però con Delphi 5-6 è difficile da realizzare... e
anche col 7 qualche struttura bisogna smazzarsela...
Il thread è molto interessante (è anche molto lungo!) e si può imparare
molto... Thorpe non credo abbia bisogno di presentazioni...
Ciao :-)
--
Andrea /\/\agni
ICQ#: 174108000

io ho rocostruito la stessa cosa tranne che x:
1) come suggerito da andrea LF, non ho ereditato da TPersistent ma da
TObject e ho messo {$M+}
2) non ho usato la registerclass
3) avendo intanto "scoperto" TReader e TWriter ho usato anche quelle per
implementare lo streaming senza dover passare x TPersistent.
Pero, condivido a pieno l'idea di non scendere troppo a basso livello e di
usare gli strati previsti da delphi, OVVIAMENTE SE ESISTONO.
Bye
A

 per implementare lo streaming senza dover passare x TPersistent.
Anch'io avevo usato ObjectBinaryToText l'anno scorso in un progetto per
avere la persistenza di un oggetto: molto comoda.


 in uso...
'naltra..... che barba....
su DDG leggo:
Often, Borland doesn't document a feature because it might change between
versions.
When using features such as the undocumented RTTI, realize that your code
might not be fully portable between versions of Delphi.
Sapevo ciň per quanto riguardava le Open Tools API per via degli Experts che
ho scritto, ma questa non la sapevo proprio...
Bč... c'era da aspettarselo!
--
Acqua
-------------------------------------------------------
Download new version of my PixExperts for Delphi 6/C++ Builder 6 at
http://www.salexander.supereva.it/Pix_Exp171D6.zip

 code might not be fully portable between versions of Delphi.
Se leggi i commenti di Danny Thorpe a riguardo vedrai che le cose non stanno
proprio così...
Riassumendo per punti cardinali direi che si può "stringere" tutto in 2
affermazioni:
- TypInfo non è da intendere come "undocumented" ma come "self-documented"
...
- Se usi le _funzioni_ di TypInfo non avrai problemi... se fai accesso
pesante alle strutture sei fregato...
Sinceramente le RTTI sono una di quelle cose così intrinsecamente legate al
compilatore che mi stupirei se non cambiassero da compilatore a compilatore
e la necessità di usarle in modo intensivo credo sia sorta solo negli ultimi
anni (per esempio per SOAP [che io non conosco molto] dice Thorpe) e per i
vari OPF, sistemi di remoting ecc...
Io personalmente non trovo da lamentarmi...


 Io personalmente non trovo da lamentarmi...
Sicuramente perchè tu non ne fai un uso pesante (e meno male ;)  ). Ma
immagina i programmatori del tool Multilizer......
Se Borlad si prendesse la briga di modificare la RTTI? Loro fanno
larghissimo uso della unit TypInfo!!!!!!!
O megli ancora, visto che ci sono proprio dentro......
Ti ricordi cosa disse il tizio di Borland al Delphi day sulle Open Tools API
di OCTANE?
"Per quanto riguarda le OTA ci saranno forti cambiamenti..." e Marco
aggiunse: "In effetti è vero. Chi ha scritto degli experts, come quelli di
GExpert, Eagle Software (che, tra l'altro, ora ha avuto una fusione con la
Developer express Inc.), dovranno riscrivere quasi tutto da capo". Cacchio,
non devo lamentarmi :)? Ho speso un casino di tempo per scivermi gli expert
proprio perchè l'OTA era priva di documentazione *ufficiale* anche se gran
parte del suo codice è self-documented. Esattamente come la RTTI.
Questo mi ha portato a non *fidarmi* più del *self-documented*.
A questo punto la tua domanda sarà scontata: "Ma chi cacchio te lo ha detto
di scriverti degli expert?". Bè, iniziavo ad odiare il modo in cui Delphi ci
fa ordinare la sequenza di tabulazione dei controlli... e vari. :)
Conclusione:
INHO bisogna fidarsi soltanto di ciò che Borland ti documenta realmente sui
suoi manuali e sull'help!
Se possibile, stare alla larga da tutto ciò che è legato direttamente
all'IDE o alle gestione interna di Delphi.
Ciao Andrea,
