6 de maio de 2009

Chamando funções e classes C++ no Delphi - parte 2

Dando continuidade ao post sobre chamada de código C++ no Delphi, falarei sobre name mangling e como isso influencia na chamada de funções C/C++ dentro do delphi.

Na linguagem C++, é permitido criar funções "sobrecarregadas" (overloaded), isto é, várias funções podem ter o mesmo nome mas ter parâmetros em quantidade e tipos diferentes. Por causa dessa peculiaridade, compiladores C++ incluem "decorações" no nome de todas as funções - sobrecarregadas ou não - de modo a garantir a unicidade desses nomes no código binário final gerado. Normalmente, essa "decoração" significa incluir mnemônicos do tipo de retorno da função e um mnemônico do tipo de cada parâmetro. Veja o exemplo abaixo, tirado do Wikipedia para o C++ da Borland:

Função C++Nome exportado
void funcao(int par1)@funcao$qi
void funcao(int par1, char par2)@funcao$qizc
void funcao(void)@funcao$qv

Então, para mapear uma função C++ no Delphi, precisaremos descobrir o nome com que ela foi exportada na biblioteca DLL e utilizar esse nome na declaração pascal. Suponha que a DLL seja BIBLIO.DLL e as funções são as do exemplo acima. O mapeamento dessas funções em Delphi seria :

procedure funcao1 (par1 : integer);cdecl;
external 'BIBLIO.DLL' name '@funcao$qi';
procedure funcao2(par1 : integer; par2 : char);cdecl;
external 'BIBLIO.DLL' name '@funcao$qizc';
procedure funcao3;cdecl;
external 'BIBLIO.DLL' name '@funcao$qv';

Esse tipo de mapeamento é estático, de forma que as DLLs assim mapeadas são carregadas imediamente, junto com o executável. Se preferir carregar as bibliotecas somente quando forem de fato necessárias, use as funções LoadLibrary e GetProcAddress, lembrando que o nome de função que deve ser passado a essa última é o nome com as "decorações".

Como descobrir o nome modificado pelo compilador e exportado nas bibliotecas é assunto tratado em outro post.

3) Para o caso de ter que usar classes instanciadas do C++ no Delphi, o mapeamento de tipos para os campos deve ser feito da mesma maneira que para estruturas, como mostrado no post anterior. Para as funções virtuais da classe, o mapeamento deve ser feito do mesmo modo que as funções mostrado no post anterior, com uma diferença : deve-se sinalizar ao Delphi que essa função é abstrata, isto é, que essa função será localizável pela tabela de funções virtuais e que o Delphi não deve esperar encontrar a implementação da função nessa classe. Segue exemplo do mapeamento de uma função virtual do C++ no Delphi; este exemplo é utilizado na prática na ABC71, dentro do Gerador de Relatórios:

procedure Preview (aReport : TPtrFolderData);virtual;abstract;

Como a tabela de funções virtuais funciona de forma idêntica no Delphi e no C++, ao invocar essa função ("mensagem", para os puristas) no Delphi, a função que será chamada efetivamente será aquela cujo corpo está codificado na biblioteca C++. Lembre-se que no pascal ela foi declarada como "abstrata" e portanto não tem implementação aí.

Implicações disso que foi exposto nos dois últimos parágrafos : as funções que serão chamadas no Delphi têm que ser virtuais no C++ e o C++ deve disponibilizar uma forma de se obter uma instância da classe, provavelmente através de uma função tipo Create ou do tipo Get.

Por fim, as regras e recomendações são bastante parecidas com isso caso se queira fazer o contrário, isto é, chamar no C++ código feito no Delphi.

Nenhum comentário :

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.