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.