Algumas vezes é possível utilizar alguns paradigmas de orientação à objetos em C. Ao se projetar uma biblioteca, podemos encapsular o conteúdo das structs usando tipos incompletos e provendo funções para manipular o conteúdo destas.
Imagine uma biblioteca que cria um “objeto” para representar uma pessoa. Esta biblioteca provê um cabeçalho person.h com funções para manipular o “objeto”.
typedef struct person person; person *person_new(char *name, int age); void person_free(person *handler); void person_print(person *handler);
Note que não existe uma descrição da struct person no cabeçalho. A única forma então de manipular esta struct é usando as funções, porque os atributos da struct não são conhecidos. Esta struct portanto caracteriza um tipo incompleto, mas apenas do ponto de vista dos usuários da biblioteca. Em sua implementação, a biblioteca conhece o conteúdo de person e consegue manipulá-los, como vemos a seguir.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "person.h"
struct person {
char *name;
int age;
};
person *person_new(char *name, int age)
{
person *new = malloc(sizeof(*new));
new->name = strdup(name);
new->age = age;
return new;
}
void person_free(person *handler)
{
free(handler->name);
free(handler);
}
void person_print(person *handler)
{
printf("%s - %d\n", handler->name, handler->age);
}
A vantagem de se encapsular os dados, forçando esta política através da técnica apresentada, é que a implementação da biblioteca pode ser alterada sem impacto em seus clientes. person_new poderia checar a validade do atributo age, retornando um ponteiro nulo caso seja passado uma idade negativa. O código seguinte ilustra um possível cliente da biblioteca, note que não há referência aos atributos de struct person.
#include <person.h>
int main(void)
{
person *mike = person_new("Mike", 21);
person_print(mike);
person_free(mike);
return 0;
}
A primeira vez que vi uma biblioteca fazendo uso extenso desta técnica foi no projeto OpenSync, que inspirou este post.







6 comentários
Feed de comentários deste artigo
4 agosto 2009 às 11:46
William Antônio Siqueira
Encapsulamento em C!!! Me parece ser uma boa para a validação de campos!
5 agosto 2009 às 4:52
Murilo Adriano
Interessante a técnica :D
Usei ela em uma experiência que fiz. Gostaria de saber o que acha:
http://murilo.wordpress.com/2009/08/05/como-programar-em-c-orientado-a-objetos/
Abraço!
5 agosto 2009 às 18:22
Blabos
Gostei do post.
Existem outras bibliotecas que implementam essa técnica.
No ano passado eu escrevi sobre isso também:
http://blog.blabos.org/2008/05/membros-privados-em-estruturas-c
Abração
10 agosto 2009 às 14:15
Jorge Pereira
E usado largamente este tipo de técnica em códigos relacionados ao GNOME! (Conceitos envolvendo GObject, dados privados, …)
Muito legal!!!
15 agosto 2009 às 14:12
Adriano Bonat
Nunca vi essa nomenclatura de “tipos incompletos”, conheço essa maneira de prover funções para manipular structs pelo nome de “tipos opacos”.
Com tipos opacos, programas utilizam a biblioteca apenas através de referências as estruturas, e realizam operações sobre estas através de funções.
Dessa maneira, a biblioteca esconde detalhes de sua implementação, os programas não devem setar valores diretamente nos campos da estrutura, deve-se sempre utilizar funções da bibilioteca.
Um exemplo de biblioteca que utiliza isso e que vários não perceberam, é quando se manipula arquivos. fopen() retorna um ponteiro para um handler, depois utiliza-se outras funções como ftell(), fwrite() e ao final fclose(), porém em nenhum momento se mexeu diretamente com o handler :)
sqlite e libevent são também exemplos de bibliotecas em C que utilizam-se de tipos opacos.
Espero ter ajudado :)
16 agosto 2009 às 20:07
Thiago Marcos P. Santos
Ambos os nomes estão corretos. Já vi ambas nomenclaturas, mas a maioria das vezes, Opaque Pointers era usado quando se falava de C++.
http://tinobox.com/wordpress/c-programming/incomplete-types-in-c-programming/
http://www.ibm.com/developerworks/library/pa-ctypes1/
[]‘s