Introduction to the rtti mechanism of Delphi (1)

Source: Internet
Author: User

Directory
========================================================== ========================================================
⊙ Rtti Introduction
Relationship between class and VMT
Class, Class of class, and class variable
⊙ Tobject. classtype and tobject. classinfo
Principles of the IS and as operators
⊙ Ttypeinfo-structure of rtti Information
⊙ Obtain the property information of a class
⊙ Obtain method type information
⊙ Obtain rtti information of the ordinal and set types
⊙ Obtain rtti information of other data types
========================================================== ========================================================

========================================================== ========================================================
⊙ Rtti Introduction
========================================================== ========================================================

The name translated by rtti (run-time type information) is "runtime type information", that is, information about the data type or class can be obtained at runtime. What is the use of this rtti? I cannot tell it now. I found a lot of rtti applications in the code of reading the Delphi persistence mechanism, so I had to learn rtti first. Below are my study notes. If you find an error, please let me know. Thank you!

The rtti of Delphi is mainly divided into the rtti of class and the rtti of general data types. The following starts from class.

========================================================== ========================================================
Relationship between class and VMT
========================================================== ========================================================

A class is a pointer to VMT from the compiler's Perspective (expressed in vmtptr later ). Some class information pointers are stored in the negative address direction of the class vmtptr. The value and content of these pointers are determined after compilation. For example, the content of vmtptr-44 is a pointer to the class name (classname. However, we generally do not use numeric values to access these class information. Instead, we use constants defined in system. Pas that start with VMT, such as vtmclassname and vmtparent.

There are two types of Methods: Object-level methods and class-level methods. The self pointer has different meanings. In an object-level method, self points to the object address space, so you can use it to access the object's member functions. In a class-level method, self points to the class's VMT, therefore, you can only use it to access VMT information, but not the member fields of the object.

========================================================== ========================================================
Class, Class of class, and class variable
========================================================== ========================================================

The class mentioned above is vmtptr. In Delphi, you can also use the class of keyword to define the class, and you can use the class to define class variables. It is not difficult to understand the key of the three syntactically. Consider the class as a common data type. What is the performance at the compiler level?

To simplify the discussion, we use tobject, tclass, and tmyclass to represent the three types mentioned above:

Type
Tclass = Class of tobject;
VaR
Tmyclass: tclass;
Myobject: tobject;
Begin
Tmyclass: = tobject;
Myobject: = tobject. Create;
Myobject: = tclass. Create;
Myobject: = tmyclass. Create;
End;
 
In the preceding example, all three tobject objects are successfully created. The implementation of the compiler is: tobject is a vmtptr constant. Tclass is also a vmtptr constant, and its value is tobject. Tmyclass is a vmtptr variable, which is assigned as tobject. The Assembly Code of tobject. Create and tclass. Create are identical. However, tclass not only represents a class by default, but also (mainly) represents the class type. It can be used to define class variables to implement class-level operations.

========================================================== ========================================================
⊙ Tobject. classtype and tobject. classinfo
========================================================== ========================================================

Function tobject. classtype: tclass;
Begin
Pointer (result): = ppointer (Self) ^;
End;

Tobject. classtype is an object-level method. The self value is a pointer to the object's memory space. The first four bytes of the object's memory space are class vmtptr. Therefore, the return value of this function is the vmtptr of the class.

Class function tobject. classinfo: pointer;
Begin
Result: = ppointer (INTEGER (Self) + vmttypeinfo) ^;
End;

Tobject. classinfo is defined using the class keyword, so it is a Class-level method. The self pointer in this method is vmtptr. Therefore, the returned value of this function is vmttypeinfo in the negative direction of vmtptr.

The pointer returned by tobject. classinfo is actually a pointer to the rtti structure of the class. However, you cannot access the content pointed to by tobject. classinfo (the return value of tobject. classinfo is 0), Because delphi only generates rtti information in the tpersistent class and the successor class of tpersistent. (From the compiler's perspective, this is the result of using the {$ M ++} sign before the declaration of the tpersistent class .)

Tobject also defines some functions for retrieving class rtti information, which are listed below and will not be analyzed one by one:

Tobject. classname: struct string; Class Name
Tobject. classparent: tclass; parent class of the object
Tobject. inheritsfrom: Boolean; Whether to inherit from a certain class
Tobject. instancesize: longint; object instance size

========================================================== ========================================================
Principles of the IS and as operators
========================================================== ========================================================

We know that we can use the is keyword at runtime to determine whether an object belongs to a class. We can use the as keyword to safely convert an object to a class. At the compiler level, the operations of IS and as are completed by two functions in system. Pas.

{System. Pas}
Function _ isclass (Child: tobject; parent: tclass): Boolean;
Begin
Result: = (Child <> nil) and child. inheritsfrom (parent );
End;

_ Isclass is simple. It uses the inheritsform function of tobject to determine whether the object is inherited from a class or its parent class. Each class's VMT has a vmtparent pointer pointing to the class's parent class's VMT. Tobject. inheritsfrom uses [recursion] to determine whether the parent VMT pointer is equal to its own VMT pointer to determine whether it is inherited from this class.

{System. Pas}
Class function tobject. inheritsfrom (Aclass: tclass): Boolean;
VaR
Classptr: tclass;
Begin
Classptr: = self;
While (classptr <> nil) and (classptr <> Aclass) Do
Classptr: = ppointer (INTEGER (classptr) + vmtparent) ^;
Result: = classptr = Aclass;
End;

The as operator is actually completed by the _ asclass function in system. Pas. It simply calls the is operator to determine whether an object belongs to a class. If not, an exception is triggered. Although _ asclass returns the tobject type, the compiler automatically changes the returned object to the parent class. Otherwise, the returned object cannot use methods and data other than tobject.

{System. Pas}
Function _ asclass (Child: tobject; parent: tclass): tobject;
Begin
Result: = child;
If not (child is parent) then
Error (reinvalidcast); // loses return address
End;

========================================================== ========================================================
⊙ Ttypeinfo-structure of rtti Information
========================================================== ========================================================

The structure of the rtti information is defined in typinfo. PAS:

Ttypeinfo = record // ttypeinfo is the structure of rtti Information
Kind: ttypekind; // data type of rtti Information
Name: Required string; // data type name
{Typedata: ttypedata} // rtti content
End;

Ttypeinfo is the structure of rtti information. Tobject. classinfo returns a pointer to the class ttypeinfo information. Kind is an enumeration type, which indicates the Data Types contained in the rtti structure. Name is the name of the data type. Note that the last field typedata is commented out, which indicates that the structure content of the field varies according to different data types.

The ttypekind enumeration defines the data types that can use rtti information. It contains almost all Delphi data types, including tkclass.

Ttypekind = (tkunknown, tkinteger, tkchar, tkenumeration, tkfloat,
Tkstring, tkset, tkclass, tkmethod, tkwchar, tklstring, tkwstring,
Tkvariant, tkarray, tkrecord, tkinterface, tkint64, tkdynarray );

Ttypedata is a huge record type and is not listed here. The content of this record will be listed later as needed.

========================================================== ========================================================
⊙ Obtain the property information of a class
========================================================== ========================================================

This section is the most complex part of rtti. It is very simple to try to thoroughly understand this section.

The following is an example of getting the attributes of a class:

Procedure getclassproperties (Aclass: tclass; astrings: tstrings );
VaR
Propcount, I: smallint;
Proplist: pproplist;
Propstr: string;
Begin
Propcount: = gettypedata (Aclass. classinfo). propcount;
Getproplist (Aclass. classinfo, proplist );
For I: = 0 to propcount-1 do
Begin
Case proplist [I] ^. proptype ^. Kind
Tkclass: propstr: = '[Class]';
Tkmethod: propstr: = '[Method]';
Tkset: propstr: = '[set]';
Tkenumeration: propstr: = '[Enum]';
Else
Propstr: = '[field]';
End;
Propstr: = propstr + proplist [I] ^. Name;
Propstr: = propstr + ':' + proplist [I] ^. proptype ^. Name;
Astrings. Add (propstr );
End;
Freemem (proplist );
End;

You can place a tlistbox in the form and execute the following statement to observe the execution result:

Getclassproperties (tform1, listbox1.items );

This function first uses the gettypedata function to obtain the number of class attributes. Gettypedata is a function in typinfo. Pas. Its function is to return the pointer to the typedata of ttypeinfo:

{Typinfo. Pas}
Function gettypedata (typeinfo: ptypeinfo): ptypedata; assembler;

The ttypedata structure of the class is as follows:

Ttypedata = packed record
Case ttypekind
Tkclass :(
Classtype: tclass; // class (vmtptr)
Parentinfo: pptypeinfo; // rtti pointer of the parent class
Propcount: smallint; // number of attributes
Unitname: extends stringbase; // unit name
{Propdata: tpropdata}); // detailed attribute information
End;

The propdata is a variable-size field. Tpropdata is defined as follows:

Tpropdata = packed record
Propcount: word; // number of attributes
Proplist: record end; // placeholder, which indicates the next row.
{Proplist: array [1 .. propcount] of tpropinfo}
End;

The structure of each attribute information in the memory is tpropinfo, which is defined as follows:

Ppropinfo = ^ tpropinfo;
Tpropinfo = packed record
Proptype: pptypeinfo; // pointer to the property type information pointer
Getproc: pointer; // get method pointer of the attribute
Setproc: pointer; // set method pointer of the attribute
Storedproc: pointer; // storedproc pointer of the attribute
Index: integer; // the index value of the attribute
Default: longint; // default value of the attribute
Nameindex: smallint; // name index of the attribute (starts with 0)
Name: Required string; // attribute name
End;

To facilitate access to attribute information, typinfo. Pas also defines a pointer to the tpropinfo array:

Pproplist = ^ tproplist;
Tproplist = array [0 .. 16379] of ppropinfo;

We can use getproplist to obtain the pointer array of all attribute information. Remember to use freemem to clear the memory of the array after the array is used up.

{Typinfo. Pas}
Function getproplist (typeinfo: ptypeinfo; out proplist: pproplist): integer;

The ttypeinfo pointer of the getproplist incoming class and the tproplist pointer. It allocates a piece of memory for the proplist and fills the memory into a pointer array pointing to the tpropinfo, and finally returns the number of attributes.

The preceding example shows how to obtain all the attributes of a class. You can also obtain the attributes based on their names:

{Typinfo. Pas}
Function getpropinfo (typeinfo: ptypeinfo; const propname: string): ppropinfo;

Getpropinfo returns the tpropinfo pointer Based on the rtti pointer of the class and the name string of the attribute. If this attribute is not found, Nil is returned. Getpropinfo is easy to use. For example:

Showmessage (getpropinfo (tform, 'name') ^. proptype ^. Name );

This call shows the type name of the name attribute of the tform class: tcomponentname.

========================================================== ========================================================
⊙ Obtain method type information
========================================================== ========================================================

The so-called method is a function pointer declared with the of object keyword. The following function can display the type information of a method:

Procedure getmethodtypeinfo (atypeinfo: ptypeinfo; astrings: tstrings );
Type
Pparamdata = ^ tparamdata;
Tparamdata = record // data structure of function parameters
Flags: tparamflags; // parameter transfer rule
Paramname: parameter string; // parameter name
Typename: Required string; // parameter type name
End;
Function getparamflagsname (aparamflags: tparamflags): string;
VaR
I: integer;
Begin
Result: = '';
For I: = INTEGER (pfvar) to INTEGER (pfout) Do begin
If I = INTEGER (pfaddress) then continue;
If tparamflag (I) in aparamflags then
Result: = Result + ''+ getenumname (typeinfo (tparamflag), I );
End;
End;
VaR
Methodtypedata: ptypedata;
Paramdata: pparamdata;
Typestr: p1_string;
I: integer;
Begin
Methodtypedata: = gettypedata (atypeinfo );
Astrings. Add ('---------------------------------');
Astrings. Add ('method name: '+ atypeinfo ^. Name );
Astrings. Add ('method kind: '+ getenumname (typeinfo (tmethodkind ),
INTEGER (methodtypedata ^. methodkind )));
Astrings. Add ('params count: '+ inttostr (methodtypedata ^. paramcount ));
Astrings. Add ('params list :');
Paramdata: = pparamdata (@ methodtypedata ^. paramlist );
For I: = 1 to methodtypedata ^. paramcount do
Begin
Typestr: = pointer (INTEGER (@ paramdata ^. paramname) +
Length (paramdata ^. paramname) + 1 );
Astrings. Add (format ('[% s] % s: % s', [getparamflagsname (paramdata ^. Flags ),
Paramdata ^. paramname, typestr ^]);
Paramdata: = pparamdata (INTEGER (paramdata) + sizeof (tparamflags) +
Length (paramdata ^. paramname) + Length (typestr ^) + 2 );
End;
If methodtypedata ^. methodkind = mkfunction then
Astrings. Add ('result value: '+ p;string (paramdata) ^ );
End;

As an experiment, place a tlistbox in the form and execute the following code to observe the execution result:

Type
Tmymethod = function (A: array of char; var B: tobject): integer of object;
Procedure tform1.formcreate (Sender: tobject );
Begin
Getmethodtypeinfo (typeinfo (tmymethod), listbox1.items );
Getmethodtypeinfo (typeinfo (tmouseevent), listbox1.items );
Getmethodtypeinfo (typeinfo (tkeypressevent), listbox1.items );
Getmethodtypeinfo (typeinfo (tmousewheelevent), listbox1.items );
End;

Because the type information of the method is complex, I try to compress the code as long as possible. Let's take a look at its implementation principles. The first parameter of getmethodtypeinfo is of the ptypeinfo type, indicating the type information address of the method. The second parameter is a string list, which can be any object that implements the tstrings operation. We can use the typeinfo function in system. Pas to obtain rtti information pointers of any type. The typeinfo function is built into the compiler like sizeof.

Getmethodtypeinfo also uses the getenumname function in typinfo. Pas. This function obtains the name of the enumerated type through an integer of the enumerated type.

Function getenumname (typeinfo: ptypeinfo; Value: integer): string;

Similar to getting the property information of a class, the type information of a method is also in the ttypedata structure.

Ttypedata = packed record
Case ttypekind
Tkmethod :(
Methodkind: tmethodkind; // method pointer type
Paramcount: byte; // number of parameters
Paramlist: array [0 .. 1023] of char // For more information, see the downstream comments.
{Paramlist: array [1 .. paramcount]
Record
Flags: tparamflags; // parameter transfer rule
Paramname: parameter string; // parameter name
Typename: Required string; // parameter type
End;
Resulttype: Repeated string}); // Return Value Name
End;

Tmethodkind is a method type and is defined as follows:

Tmethodkind = (mkprocedure, mkfunction, mkconstructor, mkdestructor,
Mkclassprocedure, mkclassfunction,
{Obsolete}
Mksafeprocedure, mksafefunction );

Tparamsflags is the rule for passing parameters. It is defined as follows:

Tparamflag = (pfvar, pfconst, pfarray, pfaddress, pfreference, pfout );
Tparamflags = set of tparamflag;

Since paramname and typename are variable-length strings, you cannot directly use the value of this field. Instead, you should use the pointer step method to retrieve the parameter information, so the above Code looks long.

========================================================== ========================================================
⊙ Obtain rtti information of the ordinal and set types
========================================================== ========================================================

After discussing the rtti information of attributes and methods, it is much easier to look at the rtti information of other data types. All the principles for obtaining rtti are to get the ttypedata pointer through the gettypedata function, and then parse ttypedata through ttypeinfo. typekind. The ttypeinfo pointer of any data type can be obtained through the typeinfo function.

The ttypedata of the ordered type is defined as follows:

Ttypedata = packed record
Tkinteger, tkchar, tkenumeration, tkset, tkwchar :(
Ordtype: tordtype; // ordered Value Type
Case ttypekind
Case ttypekind
Tkinteger, tkchar, tkenumeration, tkwchar :(
Minvalue: longint; // minimum value of the type
Maxvalue: longint; // the maximum value of the type.
Case ttypekind
Tkinteger, tkchar, tkwchar :();
Tkenumeration :(
Basetype: pptypeinfo; // pointer to the enumerated ptypeinfo
Namelist: policstringbase; // name string of the enumeration (cannot be used directly)
Enumunitname: shortstringbase); // the name of the unit (you cannot directly use it)
Tkset :(
Comptype: pptypeinfo); // pointer to the rtti pointer of the set base class
End;

The following is a function for obtaining rtti information of the ordered type and set type:

Procedure getordtypeinfo (atypeinfo: ptypeinfo; astrings: tstrings );
VaR
Ordtypedata: ptypedata;
I: integer;
Begin
Ordtypedata: = gettypedata (atypeinfo );
Astrings. Add ('------------------------------------');
Astrings. Add ('Type name: '+ atypeinfo ^. Name );
Astrings. Add ('Type kind: '+ getenumname (typeinfo (ttypekind ),
INTEGER (atypeinfo ^. Kind )));
Astrings. Add ('data type: '+ getenumname (typeinfo (tordtype ),
INTEGER (ordtypedata ^. ordtype )));
If atypeinfo ^. Kind <> tkset then begin
Astrings. Add ('min value: '+ inttostr (ordtypedata ^. minvalue ));
Astrings. Add ('max value: '+ inttostr (ordtypedata ^. maxvalue ));
End;
If atypeinfo ^. Kind = tkset then
Getordtypeinfo (ordtypedata ^. comptype ^, astrings );
If atypeinfo ^. Kind = tkenumeration then
For I: = ordtypedata ^. minvalue to ordtypedata ^. maxvalue do
Astrings. Add (format ('value % d: % s', [I, getenumname (atypeinfo, I)]);
End;

Place a tlistbox in the form and run the following code to view the result:

Type tmyenum = (Enuma, enumb, enumc );
Procedure tform1.formcreate (Sender: tobject );
Begin
Getordtypeinfo (typeinfo (char), listbox1.items );
Getordtypeinfo (typeinfo (integer), listbox1.items );
Getordtypeinfo (typeinfo (tformborderstyle), listbox1.items );
Getordtypeinfo (typeinfo (tbordericons), listbox1.items );
Getordtypeinfo (typeinfo (tmyenum), listbox1.items );
End;

(If the enumeration element is not defined based on the default 0 reference, rtti information cannot be generated. Why ?)

========================================================== ========================================================
⊙ Obtain rtti information of other data types
========================================================== ========================================================

We have discussed the running of several typical rtti information. The methods for obtaining rtti information of other data types are similar to those described above. Since these operations are simpler, we will not discuss them one by one. The following describes other types of rtti information:

Longstring, widestring, and variant have no rtti information;
Optional string only contains maxlength information;
Only floattype: tfloattype information;
Tfloattype = (ftsingle, ftdouble, ftextended, ftcomp, ftcurr );
Int64 only contains the maximum and minimum values (also expressed as 64-bit integers );
Interface and dynamic array are not very familiar.

========================================================== ========================================================
End of ⊙
========================================================== ========================================================

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.