Esse artigo faz parte da revista Clube Delphi edição 42. Clique aqui para ler todos os artigos desta edição

Atenção: por essa edição ser muito antiga não há arquivo PDF para download. Os artigos dessa edição estão disponíveis somente através do formato HTML. 

 

RTTI: Tópicos Avançados

Campos publicados, VMTs e métodos dinâmicos

O suporte a RTTI (Runtime Type Information) é, sem dúvida, um dos recursos mais poderosos oferecidos pelo Delphi. A RTTI, como o nome indica, permite obter informações de tipos em tempo de execução. Os operadores mais básicos de RTTI são o is e o as, que permitem, respectivamente, testar o tipo de um objeto e efetuar uma conversão em tempo de execução. A RTTI do Delphi oferece muitos outros recursos para trabalhar com informações de tipos, como veremos nesse artigo.

O próprio Delphi faz uso intensivo de RTTI – ela é usada para publicar declarações para que a IDE possa salvar e carregar dados de arquivos .dfm. Entretanto, as tabelas de RTTI armazenam outros dados úteis para o desenvolvedor, tais como informações detalhadas sobre métodos virtuais, métodos dinâmicos e interfaces implementadas.

Lembre-se que nem todos os tipos de dados podem ser usados com RTTI. O Delphi armazena informações de tipos somente para campos, métodos e propriedades publicadas (published). Declarações privadas (private), protegidas (protected) e públicas (public) não têm RTTI.

Você pode ver uma introdução ao uso de RTTI no artigo de Daniel Wildt na Edição 26 da ClubeDelphi. Neste artigo, vamos estender a análise dessa tecnologia.

O exemplo deste artigo simula, em tempo de execução, um Object Inspector (somente-leitura) e um Object TreeView – este último exibe a relação entre os componentes de um formulário numa estrutura de árvore.

Criando a aplicação

Iniciando o exemplo, crie um novo projeto no Delphi, clique em File | Save All salve o formulário como “uFrmMain.pas” e o projeto comoRTTIDemo.dpr”. Selecione o formulário, dê a ele o nome de “FrmMain”, adicione um componente MainMenu da paleta Standard e adicione três itens de menu: “Show”, “Object Inspector” e “Object TreeView”. Coloque no formulário alguns componentes à sua escolha (por exemplo, da paleta Additional, escolha: 1 Shape, 1 StringGrid e 1 ScrollBox. Da paleta Standard, escolha:  1 Button, 1 ComboBox, 1 Edi e 1 Label. Da paleta Data Controls, escolha 1 DBGrid. No componente Shape, clique no sinal de “+” à frente da propriedade Brush e configure a subpropriedade Color, com o valor clBlue.)

 

Em seguida, utilizaremos RTTI para extrair informações sobre esses componentes em tempo de execução. Seu formulário deve estar semelhante ao mostrado na Figura 1.

 

Figura 1. Formulário principal da aplicação.

Units Auto e VMT

A VMT (Virtual Method Table – Tabela de Métodos Virtuais) é utilizada em conjunto com a RTTI e permite obter informações em tempo de execução sobre métodos. A VMT armazena ponteiros para todos os métodos virtuais declarados em uma classe e em suas ancestrais, além de ponteiros para outras tabelas (métodos dinâmicos, campos publicados, etc.).

Para trabalhar com a VMT em nosso exemplo, criaremos duas units. Clique em File | New | Unit.  Salve-as com os nomes “Auto” e “VMT”. A unit Auto contém várias declarações de tipos que são utilizados na estrutura da VMT. A maior parte dessas declarações foram retiradas das units Ole2 e Oleauto do Delphi, e algumas do livro “Delphi: O Guia Essencial” (Ray Lischner). Em nosso exemplo, utilizaremos basicamente o tipo TMethodTable definido na unit Auto, que armazena os métodos declarados na classe. Porém, apresentamos a listagem completa da unit para referência. Veja o código das duas units nas Listagens 1e 2.

 

Listagem 1. Auto.pas

unit Auto;

 

interface

 

uses

  SysUtils, windows, TypInfo;

 

type

  //Layout da tabela de método dinâmico

  PDynMethodTable = ^TDynMethodTable;

  TDynMethodTable = packed record

    Count: Word;

    Indexes  : packed array [1..3000] of Smallint;

    Addresses: packed array [1..3000] of Pointer;

  end;

 

  //Lista de Parâmetros de Métodos

  TMethodParam = packed record

    TypeInfo: PPTypeInfo;

    Name: ShortString; //O nome do parâmetro é seguido por um byte #0

  end;

 

  TRecMethod = packed record

   {Alguns métodos possuem 4 bytes #0 adicionais, o que significa que o método

   não usa parâmetros. Alguns métodos possuem 6 bytes adicionais, seguidos por

   uma série de registros TMethodParam para cada parâmetro }

   Size: Word;

   Address : Pointer; //ponteiro para p endereço de memória do método

   Name: ShortString; //nome do método

  end;

 

  //Tabela contendo os Métodos publicados

  PMethodTable = ^TMethodTable;

  TMethodTable = packed record

    Count: Word;

    Methods: array [1..3000] of TRecMethod;

  end;

 

  //Tabela contendo classes de campo

...

Quer ler esse conteúdo completo? Tenha acesso completo