Back to Home

RTTI contexts in Delphi 2010: how it works and how to use them

delphi rtti

RTTI contexts in Delphi 2010: how it works and how to use them

Original author: Barry Kelly
  • Transfer
Delphi 2010 includes enhanced RTTI support, also known as run-time type info or reflection. Many design approaches were previously available only in managed languages ​​such as C # and Java, since they require code annotation and introspection (introspection). Now it is possible in the world of Delphi.


What is interesting about RTTI is its approach to organizing object pools. Delphi is a language that does not use “garbage collection”, so users need to be careful and destroy objects when they are no longer needed, doing it explicitly, or creating or using some kind of ownership scheme (objects), such as those used by TComponent- om, where the owner (Owner) takes responsibility for the destruction of objects.

The use of type information is not well combined with the TComponent-style ownership scheme. Usually, when working with RTTI, you need to search for objects of interest to you, do something with them and continue working further. This means that many objects can be identified for verification, but not really used. Managing the lifetime of these objects should be quite tedious, so a different approach has been used: a single global RTTI pool of objects. While at least one RTTI context is active in the program, the object pool keeps all its objects up to date. When the last context goes out of scope, objects are freed.

Management of the pool for work uses the Delphi entry, which contains a link to the interface. When any RTTI context passed is used for the first time, it is placed in this interface link. It only fits there for the first time - once, because Delphi records do not support default constructors, which also have their own problems. For example, how do you handle exceptions in the default constructor, at all points where they can occur? Creating arrays that are local to the flow of variables, global variables, global variables in modules, temporary objects in expressions, etc. This can become disgusting, and sometimes it does in C ++.

Thus, the first use creates an interface called a pool token. It acts like a descriptor with a reference counter pointing to a global pool of objects. As long as this interface is relevant (exists), the global pool of objects will remain relevant. Even if the RTTI context is copied somewhere, the built-in Delphi interface control logic, created on the basis of the principles of COM, allows you to be sure that the interface will not be prematurely deleted, the reference counter will have the correct value. And when the RTTI context goes out of scope, or being a local variable in the function that completed, or a field in the remote object, the reference count will decrease. When the reference counter reaches zero, the pool is empty.

The biggest advantage of this approach is that the use of RTTI, in essence, should be easy and intuitive. It is only necessary to declare a variable of the appropriate type in the program code and start using it: However, the flip side is that lazy initialization can cause an error. Imagine this scenario: 1. Library A declares RTTI context AC 2. User code B declares RTTI context BC 3. Code B requests some RTTI objects O from BC in order to transfer them to library A 4. BC goes out of scope 5. Library A is now trying to work with O, but discovers to his surprise that the objects were prematurely deleted, even if A already has an RTTI context AC

procedure Foo;
var
ctx: TRttiContext;
t: TRttiType;
begin
t := ctx.GetType(TypeInfo(Integer));
Writeln(t.Name);
end;










The problem is that A never used AC, because the pool token was not created. When BC used its context, the pool began to exist, and O objects were assigned to it; but, after the BC went out of scope, the objects were freed.

The solution to this problem is to let library A know that it uses a long-lived RTTI context and that it expects to interact with third-party code that creates objects from its own RTTI context and passes them back, it must be sure that a pool token has been created for this long-lived context . A simple way to do this is:

type
TFooManager = class
FCtx: TRttiContext;
// ...
constructor Create;
// ...
end;

constructor TFooManager.Create;
begin
FCtx.GetType(TypeInfo(Integer));
// ...
end;


This will create only the necessary minimum RTTI objects that are needed for a representation of the System.Integer type, but more importantly, it will ensure that FCtx has a pool token and leaves the global RTTI pool up to date.

In future versions of Delphi, the static TRttiContext.Create method will ensure that the value returned by it receives the pool token; but so far it’s not. TRttiContext.Create was originally defined to make the TRttiContext entry more like a class for people unfamiliar with the idiom of using interfaces to automatically determine the lifetime of objects. The corresponding TRttiContext.Free method removes the pool token, and should remain the same.

Translated.by: translated by the crowd

Translation: © DreamerSole, r3code , debose .

Read Next