A API do Windows geralmente prima pela excelência em maus exemplos. A Notação Húngara e o Typedef Arcaico são duas técnicas que, por motivos históricos, são usados a torto e a direito pelos códigos de exemplo.
Já foi escrito muita coisa sobre os prós e contras da notação húngara. Já o typedef arcaico, esse pedacinho imprestável de código, ficou esquecido, e hoje em dia traz mais dúvidas na cabeça dos principiantes em C++ do que deveria. Para tentar desobscurecer os mitos e fatos, vamos tentar explicar o que significa essa construção tão atípica, mas comum no dia-a-dia.
Vejamos um exemplo típico desse pequeno Frankenstein semântico:
typedef struct _MINHASTRUCT {
int x;
int y;
}
MINHASTRUCT, *LPMINHASTRUCT;
Bom, eu nem sei por onde começar. Talvez pelo conceito de typedef.
Typedefs
Um typedef, basicamente, é um apelido. Você informa um tipo e define "outro tipo".
typedef <tipo> apelido;
O <tipo> é tudo que fica entre o typedef e o novo nome, que deve ser um identificador válido na linguagem. Por exemplo, a empresa onde trabalho fez um typedef informal do meu nome:
typedef Wanderley Caloni Wandeco;
Se, futuramente, eu sair da empresa e entrar outro "Wanderley alguma-coisa", será possível usar o apelido novamente, bastando alterar o typedef:
typedef Wanderley Cardoso Wandeco;
Bom, "outro tipo" é forma de dizer. Isso é uma descrição errônea em muitos livros. De fato, o compilador enxerga o mesmo tipo com outro nome, daí chamarmos o typedef de apelido, mesmo.
/** @file dois_apelidos.cpp */ #include <iostream> using namespace std; struct Struct { int x; int y; }; typedef Struct Struct1; typedef Struct Struct2; int main() { Struct1 s1; Struct2 s2; cout << typeid(s1).name() << endl; cout << typeid(s2).name() << endl; }
C:\Tests>cl /EHsc dois_apelidos.cpp ... /out:dois_apelidos.exe dois_apelidos.obj C:\Tests>dois_apelidos.exe struct Struct struct Struct
Granularidade dos tipos
Tipos simples são fáceis de entender porque possuem seus símbolos no mesmo lugar:
int x; char c; long p;
Já os tipos um pouco mais complicados permite alguma mudança aqui e acolá:
int* x; char *y; long * p;
Essa liberdade da linguagem, mesmo sendo um recurso útil, pode ser bem nocivo dependendo de quem olha o código:
int x, y; // dois inteiros int * x, y; // um ponteiro para inteiro e um inteiro int x, *y; // um inteiro e um ponteiro para inteiro int *x, y; // um ponteiro para inteiro e um inteiro
Em algumas formas da sintaxe, além de ser inevitável, gera bastante desconfiança:
// Um ponteiro para função que recebe dois inteiros e não retorna nada. typedef void (*FP)(int, int); // Um ponteiro para função que recebe dois inteiros e não retorna nada. void (*)(int, int); // Um cast para ponteiro para função que recebe dois inteiros e não retorna nada. ( ( void (*)(int, int) ) pf )(x, y);
#include <iostream> void func(int x, int y) { std::cout << x << '-' << y << '\n'; } int main() { void* pf = func; ( ( void (*)(int, int) ) pf )(3, 14); }
Structs em C++
Antigamente, as structs eram construções em C que definiam um agregado de tipos primitivos (ou outras structs) e que poderiam gerar variáveis desse tipo em qualquer lugar, desde que informado seu nome e que se tratasse de uma struct:
/** @file structs.cpp */ struct MyStruct { int x, y; }; void func1() { struct MyStruct ms; //... } void func2(struct MyStruct msa) { //... } int main() { struct MyStruct ms; func2(ms); }
Para evitar toda essa digitação, os programadores usavam um pequeno truque criando um apelido para a estrutura, e usavam o apelido no lugar da struct (apesar de ambas representarem a mesma coisa).
struct MyStruct { int x, y; };
typedef struct MyStruct MS;
ou
typedef struct MyStruct { int x, y; } MS;
struct MyStruct ms1; // ainda prolixo
MS ms2; // mais simples
Com a definição da linguagem C++ padrão, e mais moderna, essa antiguidade foi removida, apesar de ainda suportada. Era possível usar apenas o nome do struct como seu tipo:
/** @file structs.cpp */ struct MyStruct { int x, y; }; void func1() { /*struct*/ MyStruct ms; //... } void func2(/*struct*/ MyStruct msa) { //... } int main() { /*struct*/ MyStruct ms; func2(ms); }
Porém, isso vai um pouco além de quando a Microsoft começou a fazer código para seu sistema operacional. Naquela época, o padrão ainda estava se formando e existia mais ou menos um consenso de como seria a linguagem C++ (sem muitas alterações do que de fato a linguagem C já era). De qualquer forma, a linguagem C imperava bem mais que C++. Dessa forma, já era bem formada a ideia de como declarar uma struct: a forma antiga.
typedef struct _MINHASTRUCT {
int x;
int y;
}
MINHASTRUCT, *LPMINHASTRUCT;
Além do uso controverso do _sublinhado para nomear entidades (que no padrão foi recomendado que se reservasse aos nomes internos da biblioteca-padrão) e do uso de MAÍUSCULAS_NO_NOME (historicamente atribuído a nomes definidos no pré-processador), o uso do typedef atracado a um struct era muito difundido. E ficou ainda mais depois que a API do Windows foi publicada com essas definições.
Como fazer,então?
Ora, do mesmo jeito que é feito há vinte anos: sem typedefs. O próprio paradigma da linguagem, independente de padrões de APIs, de sistemas operacionais ou de projetos específicos já orienta o programador para entender o que o espera na leitura de um código-fonte qualquer. Qualquer pessoa que aprendeu o básico do básico sobre ponteiros e structs consegue ler o código abaixo:
// Papai, o que que é isso? // Ora, filho, apenas uma definição de estrutura! // struct MinhaStruct { int x; int y; }; // muitas linhas abaixo... void func(MinhaStruct* ms) { // asterisco significa ponteiro para MinhaStruct! } int main() { MinhaStruct ms; func(&ms); }
Agora, para entender a forma antiga, ou você se baseou no copy&paste dos modelos Microsoftianos, ou seja, decoreba, ou você é PhD em Linguagem C/C++ e padrões históricos de linguagens legadas. Se não é, deveria começar o curso agora.
// Papai, o que que é isso? // Ora, filho, apenas uma definição de sinônimo da struct // _MINHASTRUCT, cujo nome não é usado, para dois nomes // em maiúsculas, apesar se não serem defines, com uma // nomenclatura de ponteiro que eu nunca vi na vida (obs: // papai programa em um sistema não-Windows). // typedef struct _MINHASTRUCT { int x; int y; } MINHASTRUCT, *LPMINHASTRUCT; // muitas linhas abaixo... void func(LPMINHASTRUCT ms) { // o que diabos é um LP, mesmo? } int main() { MINHASTRUCT ms; func(&ms); }
Da mesma forma, o uso de uma estrutura simples de tipos mantém a lista de nomes do seu projeto limpa e clara. Compare o visualizador de classes em projetos Windows com algo mais C++ para ter uma ideia.
É claro, essa é apenas uma sugestão. Existem vantagens em sua utilização. Existe alguma vantagem no modo antigo? Existe: a Microsoft usa, e talvez mais pessoas usem. Basta a você decidir qual deve ser o melhor caminho.
Atualização
De acordo com o leitor Adriano dos Santos Fernandes, a obrigatoriedade do nome struct após seu nome continua valendo para a linguagem C padrão, assim como no compilador GCC ocorre um erro ao tentar omiti-la. Apenas na linguagem C++ essa obrigatoriedade não existe mais.
Eu não fiz meus testes, mas confio no diagnóstico de nosso amigo. A maior falha do artigo, no entanto, é usar a linguagem C como base, quando na verdade ele deveria falar sobre o uso desses typedefs em C++. Esse erro também foi corrigido no original.

April 20th, 2010 at 3:58 pm
Taí o problema de ter aprendido C no tempo da 1a edição do K&R: eu não sabia que podia omitir o struct na declaração de variáveis! Eu tenho a mania de escrever coisas do tipo
typedef struct { } NOME;
Os fatos de eu não usar _NOME nem declarar *LPNOME é suficiente para eu trocar o inferno por uma breve estadia no purgatório?
Em tempo: como você deve saber o L de LP vem do tempo que existiam ponteiros curtos e longos.
April 20th, 2010 at 4:15 pm
Muito bom!
Aguardo a segunda parte: A aplicação do typedef em traits e para aliviar a digitação e o entendimento da composição dos templates instanciados... E já tem a deixa para o auto, tbm
April 20th, 2010 at 6:35 pm
Fábio,
Traits parece um tópico um pouco avançado, mas é uma ótima ideia. Vou tentar planejar algo menos painless que templates de templates usando typedefs e typenames combinados com autos do C++0x.
[]s
April 20th, 2010 at 6:37 pm
Caro DQ,
Imagino que seu código deva ser compartilhado com programadores intermediários/experientes, então talvez você até vá para o céu; o pecado é apresentar essa monstruosidade para newbies.
Você pegou justamente no ponto que minha pupila não entendeu e cujo conteúdo tive que dividir para explicar em um artigo futuro: de onde surgiram os ponteiros longos. Esse já está no forno
[]s
April 20th, 2010 at 11:00 pm
Wanderley,
Acredito que vc esteja enganado sobre não precisar usar a palavra "struct" em C. Em C++ não é, mas em C é sim.
Se o seu compilador aceita, acredito que seja um desvio do padrão. O GCC não aceita (pelo menos sem usar alguma opção pra isso).
April 21st, 2010 at 11:58 am
[...] « Typedef arcaico (Caloni.com.br) [...]
April 21st, 2010 at 12:36 pm
Muito bem, Adriano. Coloquei uma seção Atualização para avisar sobre as correções necessárias, dividindo a lógica entre C e C++. De fato eu pensei em C++ e escrevi tudo como se fosse C.
Obrigado pelo aviso.
[]s