De Objective Caml para C e C++/Construção de tipos: diferenças entre revisões

[edição não verificada][edição não verificada]
 
===Tipos uniões===
 
Tipos uniões agrupam diversos tipos em um mesmo tipo. Eles possuem portanto um papel muito similar aos tipos variantes de Objective Caml. Infelizmente, a programação por tipos uniões de C e C++ não é tão simples quanto a de tipos variantes.
 
A sintaxe para definir um tipo união é a seguinte:
union etiqueta {
tipo1 nome1;
tipo2 nome2;
...
tipoN nomeN;
};
Essa sintaxe é muito similar à dos tipos registros. A semântica porém é bem diferente. Para explicar ela, vamos nos basear em um exemplo:
typedef union Unumero {
long inteiro;
double real;
} Tnumero;
Tnumero numero.
Então <tt>Tnumero</tt> é o nome de um tipo união e <tt>numero</tt> é uma variável do tipo <tt>Tnumero</tt>. Os valores da variável <tt>numero</tt> podem ser interpretados como sendo, ou do tipo <tt>long</tt> ou do tipo <tt>double</tt>. Para intepretar <tt>numero</tt>
como um <tt>long</tt>, devemos utilizar o operador de seleção <tt>.</tt> (um ponto) da seguinte forma: <tt>numero.inteiro</tt>. Para interpretar <tt>numero</tt> como um <tt>double</tt>, utilizamos o operador de seleção com o nome correspondente: <tt>numero.real</tt>. Segue um pequeno programa que ilustra esses conceitos:
#include <cstdio>
int main ()
{
typedef union Unumero {
long inteiro;
double real;
} Tnumero;
Tnumero numero;
numero.inteiro = 2l;
numero.inteiro *= numero.inteiro;
printf("numero.inteiro = %li.\n", numero.inteiro);
numero.real = 3.0;
numero.real *= numero.real;
printf("numero.real = %lf.\n", numero.real);
}
No programa, definimos uma variável <tt>numero</tt> do tipo <tt>Tnumero</tt> que é atribuída primeiro um valor inteiro, e segundo um valor flutuante. A variável só pode armazenar um 'único valor' de um desses tipos. Assim, a execução desse programa resulta na seguinte impressão na saída padrão:
numero.inteiro = 4.
numero.real = 9.000000.
 
O leitor sagaz deve questionar-se agora, como pode saber se o valor que a variável armazena é de um tipo ou de outro? Ou seja qual das duas interpretações possíveis é correta? O exemplo mostra que cada interpretação é correta em trechos distintos do código. Inicialmente, a variável é atribuída é um valor inteiro. O que aconteceria se o programa fosse interpretar esse valor como um valor flutuante? E será que é legal fazer isso? Podemos construir um pequeno programa para discutir as respostas as essas duas perguntas:
#include <cstdio>
typedef union Unumero {
long inteiro;
double real;
} Tnumero;
int main ()
{
Tnumero numero;
numero.real = 3.0;
printf("numero.inteiro = %li.\n", numero.inteiro);
}
Aqui, a variável <tt>numero</tt> recebe um valor flutuante, que é interpretado como um número inteiro na chamada à função de impressão na saída padrão. Nesse caso, o compilador gera um programa sem emitir nenhuma mensagem de erro, nem sequer um aviso. Efetivamente, esse programa é perfeitamente legal. A saída gerada pela execução do programa é:
numero.inteiro = 1074266112.
Opa!?? O que acontece? Bom, a forma como os bits são arranjados para representar <tt>3.0</tt> do tipo <tt>double</tt> não tem nada a ver como a forma como o valor <tt>3</tt> do tipo <tt>long</tt> é representado... O que aparece na tela é o valor que a ''representação binária'' de <tt>3.0</tt> tem quando é interpretado como sendo tipo <tt>long</tt>. Mais precisamente, como o tipo <tt>long</tt> é representado por quatro bytes, e o tipo <tt>double</tt> com oito, o que aparece na tela a interpretação como valor do tipo <tt>long</tt> são os quatro primeiros bytes da representação binária do tipo <tt>double</tt>...
 
Então, como esse tipo de manipulação é legal tanto em C quanto em C++, é um recurso que podemos utilizar quando precisamos utilizar as representações binárias. Mas se precisamos trabalhar de forma mais abstrata, utilizando mecanismos semelhantes ao dos tipos variantes em Objective Caml, como podemos fazer?
 
===Programação de tipos variantes===
 
Considere o seguinte tipo Objective Caml
type number = Inteiro of int | Flutuante of float | Erro
e funções para combinar valores desses tipos, como por exemplo a soma:
let number_sum (n1: number) (n2: number) =
match n1, n2 with
_, Erro | Erro, _ -> Erro
| Inteiro i1, Inteiro i2 -> let positive n = n > 0 in
if positive (i1 + i2) <> positive i1
then Flutuante ((float_of_int i1) +. (float_of_int i2))
else Inteiro(i1 + i2)
| Inteiro i, Flutuante f -> Flutuante ((float_of_int i) +. f)
| Flutuante f, Inteiro i -> Flutuante (f +. (float_of_int i))
| Flutuante f1, Flutuante f2 -> Flutuante (f1 +. F2)
 
===Tipos funções===
187

edições