Suena lindo no? Bueno no es tan así pero esta bastante cercano. Tras mis años viviendo encerrado en la cueva, alias mi casa, y mis noches programando en C descubrí que la clave para mejorar la delegación y mejorar la funcionalidad de nuestras aplicaciones en C es la de utilizar punteros a funciones.
Para dar un ejemplo de estos, voy a mostrarles una implementación que estuve desarrollando para el manejo de listas desde C
collections.h
#ifndef COLLECTIONS_H_
#define COLLECTIONS_H_
#define _XOPEN_SOURCE 500
#include "collections.h"
struct link_element{
void *data;
struct link_element *next;
};
typedef struct link_element t_link_element;
struct dlink_element{
struct dlink_element *previous;
void *data;
struct dlink_element *next;
};
typedef struct dlink_element t_dlink_element;
#endif /* COLLECTIONS_H_ */
list.h
#ifndef LIST_H_
#define LIST_H_
#define _XOPEN_SOURCE 500
#include "collections.h"
typedef struct{
t_link_element *head;
int elements_count;
sem_t semaforo;
}t_list;
t_list *collection_list_create();
int collection_list_add( t_list *list, void *data );
void *collection_list_get( t_list *list, int num );
void collection_list_put( t_list *list, int num, void *data );
void *collection_list_switch( t_list* list, int num, void* data );
void collection_list_set( t_list* list, int num, void* data, void (*data_destroyer)(void*));
void *collection_list_find( t_list *list, int (*closure)(void*) );
void collection_list_iterator( t_list *list, void (*closure)(void*) );
void *collection_list_remove( t_list *list, int num );
void collection_list_removeAndDestroy( t_list *list, int num, void (*data_destroyer)(void*) );
void collection_list_removeByPointer( t_list *list, void *data, void (*data_destroyer)(void*) );
void collection_list_removeByClosure( t_list *list, int (*closure)(void*), void (*data_destroyer)(void*) );
int collection_list_size( t_list *list );
int collection_list_isEmpty( t_list *list );
void collection_list_clean( t_list *list, void (*data_destroyer)(void*) );
void collection_list_destroy( t_list *list, void (*data_destroyer)(void*) );
#endif /*LIST_H_*/
Un ejemplo básico de como crear una lista:
t_list *list = collection_list_create();
collection_list_add(list, "Leonardo");
collection_list_add(list, "Raphael");
collection_list_add(list, "Michelangelo");
collection_list_add(list, "Donatello");
collection_list_add(list, "Splinter");
Vamos a ver el caso de 2 funciones bastante útiles, la primera es el iterator con el cual podemos recorrer todos los elementos de la lista y el segundo es el find con el cual podemos buscar un elemento en la lista.
void ninja_print(char *name){ printf("%s\n", name); }
collection_list_iterator( list, &ninja_print );
Como se puede ver en este ejemplo, el código lo que hace es nos imprime cada uno de los elementos de la lista, hay que tener en cuenta que esto es totalmente posible de hacer en C salvo por un pequeño detalle. Solo la implementación GNU de C, es decir el GCC, nos permite definir inner functions. Por lo que podemos colocar la funcion ninja_print dentro de una función. Esto nos resultaría casi como hacer un Closure salvo por el detalle de que C no permite funciones anónimas. Nos encantaría ver algo así:
collection_list_iterator( list, void (*)(char *name){ printf("%s\n", name); } );
Pero de momento el estándar no lo define, quizás habrá que esperar a la nueva revisión de C para ver este tipo de cosas.
Aun así vamos veamos algunos ejemplos mas, esta vez con el find:
int ninja_findMaster(char *name){ return strcmp(name, "Splinter" ) == 0; }
collection_list_find( list, &ninja_findMaster );
Esto resulta algo similar al iterator, salvo que ahora nuestro “closure” retorna un valor indicando si encontró algo no. Este ejemplo no tiene mucho sentido ya que el find nos devuelve un puntero al elemento encontrado y en este caso el elemento es lo que estábamos buscando. Así como esta implementada seria una buena forma de implementar un “collection_list_contains” y si “collection_list_find” devuelve NULL es que no encontró nada.
Pero en caso de que guardemos estructuras complejas dentro de la lista, podríamos utilizar el ninja_findMaster para buscar entre alguna de las propiedades del elemento y que nos retorne el putero al elemento en si.
Finalmente y una de las cosas mas interesantes es la forma de destruir la lista:
collection_list_destroy( list, &free );
Obviamente esto tiraría segfault porque los elemento son estáticos pero si fueran dinámicos nos simplificaría muchos. En caso de que la lista manejara un tipo complejo de datos, es decir algo que contenga punteros o descriptores en su interior, como buena practica se debería realizar un destructor para ese tipo de datos y pasárselo al collection_list_destroy.
Por ultimo, les dejo un ejemplo final
typedef struct{
char* name;
int age;
}t_person;
t_person *person_create(char* name, int age){
t_person *person = malloc( sizeof(t_person) );
person->name = name;
person->age = age;
return person;
}
void person_destroy(t_person *person){
free(person);
}
int main(int argc, char** argv){
t_list *list = collection_list_create();
collection_list_add(list, person_create("Juan",20) );
collection_list_add(list, person_create("Pedro",20) );
collection_list_add(list, person_create("Hector",20) );
int find_person(t_person *person){ return strcmp(person->;name,"Juan") == 0; }
t_person *findedPerson = collection_list_find(list, &find_person);
}
Mientras que en Java el mismo find seria:
Persona findedPerson;
for( Person person: persons){
if( person.getName().equals("Juan") ){
findedPerson = person;
}
}
CHAN???
<source languague=”java>
</source>