Linguagens e Ambientes de Programação (2022/2023)



Teórica 15 (27/abr/2023)

Pré-processador.
Polimorfismo implementado usando mecanismos de baixo nível: apontadores void* e macros.
Módulos genéricos (polimórficos) em C.



Pré-processador

Programa que corre sempre antes do compilador de C e que faz substituição de macros, inclusão de ficheiros, e possibilita compilação condicional.

Em Linux, o pré-processador chama-se cpp e pode ser invocado diretamente pelo utilizador. Mas é mais prático deixar que seja o compilador de C a invocar o pré-processador automaticamente. Em todo o caso, vejamos o que diz o início do manual do comando cpp:

Exemplos de macros:

Exemplos de inclusão de ficheiros

Exemplos de compilação condicional

No caso das macros com argumentos, note que existe a necessidade de proteger todas as utilizações dos argumentos com parêntesis, para compensar o facto da expansão das macros ser textual. Por exemplo, considere a seguinte chamada mult(1+1,5) no caso da macro abaixo não seguir esta regra...

Polimorfismo implementado usando mecanismos de baixo nível: apontadores void* e macros

Função monomórfica

A seguinte função permite trocar os valores de duas variáveis inteiras. É uma função monomórfica pois os seus argumentos são de tipo fixo. Esta função pode ser testada assim:

Polimorfismo implementado usando manipulação direta de memória (esta é a forma mais usada na prática)

A seguinte função permite trocar os valores de duas variáveis de qualquer tipo. É uma função polimórfica pois os seus argumentos podem ser aplicados a argumentos de tipos diversos. Esta função ser testada assim: A função swap não é segura pois o compilador aceita todas as situações, mesmo as mais absurdas - o tipo void * é compatível com todos os apontadores...

Atenção, que esta é a forma mais habitual de obter polimorfismo em C!

Polimorfismo implementado usando macros

A seguinte macro também permite trocar os valores de duas variáveis de qualquer tipo. Esta macro ser testada assim: A macro swap é mais segura do que a função anterior, pois o compilador valida todas as suas utilizações. Mesmo assim continua a existir algum nível de insegurança: imagine que a macró é aplicada a uma variável int e a uma variável double - esta situação de erro não é detetada.

Repare nos cuidados que é preciso ter para escrever uma macro que não dê problemas. Os argumentos devem ser envolvidos em parêntesis, e qualquer nova variável que seja introduzido deve ter um nome que não entre em conflito com possíveis nomes existentes. Porque todos estes cuidados? E porque razão a macro foi definida usando um do-while? (Elimine o do-while, teste, e logo descobrirá o problema.)

Polimorfismo implementado usando manipulação direta de memória e macros

A seguinte implementação da operação swap é a mais agradável de usar do ponto de vista sintático. Até parece que está em causa uma operação primitiva da linguagem. Contudo, esta implementação não é segura, pelo que o utilizador tem de ter algumas cautelas. Esta função ser testada assim: A macro swap não é segura pois o compilador aceita todas as situações, mesmo as mais absurdas - o tipo void * é compatível com todos os apontadores...

Em GCC

O pré-processador do GCC possui uma construção typeof que permite escrever macros ainda mais sofisticadas do que as anteriores e seguras. Por exemplo, a expressão typeof(a) representa o tipo da variável a e pode ser usada como um tipo normal, inclusivamente para definir outras variáveis com o mesmo tipo de a.

É pena o C padrão não suportar a construção typeof.

Exemplo de polimorfismo na biblioteca padrão do C - função qsort

A função qsort permite ordenar vetores de qualquer tipo.

Exercício: Como faria para ordenar um vetor com componentes do seguinte tipo:


Módulos genéricos (polimórficos) em C

É possível escrever módulos genéricos em C usando macros com várias linhas e a operação de concatenação de tokens que se escreve ##.

Os módulos genéricos são muito difíceis de escrever e de ler, mas são fáceis de usar.

No seguinte módulo, o tipo Data passa a ser argumento de duas macros.

Ficheiro Generic_LinkedList.h

Ficheiro Generic_LinkedList.c

Exemplo de instanciação do módulo genérico

Ficheiro LinkedList_double.h

Ficheiro LinkedList_double.c



Vídeos antigos

Estes vídeos poderão estar um pouco desatualizados, pois foram feitos no contexto duma edição anterior do LAP. Contudo, partes dos vídeos poderão continuar a ter alguma utilidade.

#--- 40 40