Basado en el libro Object-oriented Programming with Ansi-C de Axel-Tobias Schreiner.
Se define un struct base llamado Class. A partir de este se pueden crear más objetos que hereden los mismos atributos y métodos.
typedef struct {
size_t size;
void * (* ctor) (void * self, va_list * app);
void * (* dtor) (void * self);
void * (* clone) (const void * self);
size_t (* sizeOf) (const void * self);
int (* differ) (const void * self, const void * other);
char * (* toString) (const void * self);
} Class;
/* Métodos sobrecargados: */
void * new(const void * _class, ...);
void delete(void * self);
void * clone(const void * self);
int differ(const void * self, const void * other);
size_t sizeOf(const void * self);
char * toString(const void * self);
Primero consideremos la forma habitual de inicializar una cadena:
#include <stdlib.h>
int main() {
char * text = malloc(sizeof(char *) * 16);
// char text[16];
strcpy(text, "Hola mundo");
printf("%s", text);
return 0;
}
Aquí es necesario manejar la memoria de la cadena que vamos a utilizar. En caso de varias, el proceso sería más largo y su mantenimiento sería más difícil en caso de que el programa se haga más grande.
Mientras que utilizando la clase String
:
#include "String.h"
#include <stdlib.h>
int main() {
void * text = new(String, "Hola mundo");
printf("%s", toString(text));
return 0;
}
Ocultando el manejo de memoria en segundo plano.
Constructor
Para evitar esto último, se ha sobrecargado el método new()
para inicializar cualquier objeto. En el caso de un String o cadena:
void * text = new(String, "Hello, world");
Esto no da paso a que el código adopte muchas formas distintas para crear una cadena/string. Además que hace más legible y agradable a la vista del programador. Sin embargo, es cierto que detrás de bambalinas es dónde opera la lógica y la gestión de memoria.
Clonador
En caso de necesitar una cadena con el mismo contenido de una pero sin apuntar a la misma dirección de memoria, se utliza:
void * textCopy = clone(text);
Destructor
En caso de ya no utilizar el String hacemos uso de la función delete()
, que también hemos sobrecargado previamente:
delete(text);
toString
La función toString()
retorna char *
lo cual nos permite imprimir el contenido del objeto creado sin tener que acceder directamente a ese atributo delstruct.
printf("text = %s\n", toString(text));
Para compilar los ejemplos de uso y ver por consola el resultado, se puede utilizar el comando:
make examples && make run_examples
Todo el código figura en la carpeta examples
.
Del mismo modo, para verificar la integridad de las funciones implementadas para cada clase, se puede utilizar el comando:
make tests && make check
Esto ejecutará un conjunto de tests escritos en C con la librería de acutest.h. De esta forma, también se pueden agregar más funcionalidades y testearlas.
Puedes eliminar todos los archivos compilados con el comando:
make clean