¿ ANSI C Orientado a Objetos ?

18 febrero 2010

Bueno, todos sabemos que estos es imposible porque básicamente C es un lenguaje estructurado, ¿Entonces porque hablar de Orientación a Objetos en ANSI C? Es bastante simple, cualquier lenguaje de programación moderno trabaja con el paradigma de objetos y es natural que todos estemos mayoritariamente acostumbrados a eso. Por otro lado el paradigma de objetos nos permite pensar, entender y desarrollar con mayor modularidad que con lenguajes estructurados.
El punto de todo esto es tratar de adoptar un patrón de codificación que nos acerque a lo que todos estamos acostumbrados, y es por eso que después de mucho investigar voy a presentar algunos pequeños conceptos.

Relación Objeto – Mensaje:

Lo primero a comprender es como poder establecer una relación en la que yo tengo un objeto y le envío un mensaje ( es decir invocamos el método de un objeto ).

# Método I ( TAD ):

En C tenemos las queridas y amadas estructuras de datos, y quizás el patrón mas conocido es el TAD. Un TAD se define como una estructura de datos y sus funciones asociadas, como por ejemplo:

	typedef struct {
		char * name;
		int age;
	} t_person;

Y a esto tenemos sus funciones asociadas:

	char* person_getname(t_person*);
	int   person_getage(t_person*);
	void  person_walk(t_person*);

Ahora bien, modificando un poco el criterio de la sintaxis podria definir lo siguiente:

	struct Person {
		char * name;
		int age;
	};

	char* Person_getName(struct Person *);
	int   Person_getAge(struct Person *);
	void  Person_walk(struct Person *);

Esto mas que nada nos puede hacer pensar que Persona es nuestra clase y que getName es un método estático de la clase Persona y nosotros le pasamos la instancia de la clase. Quizás este puede ser una de las formas mas comunes de ver el código en C. Pero como todo suele tener varias problemáticas, las cuales veremos mas adelante.

# Método II:

Este método, también es uno de los mas conocidos y consiste en asociar las funciones con las estructuras de datos, haciendo que estas contengan los punteros.

	struct Person {
		char * name;
		int age;
		void (* walk) (void *_self);
		void (* run) (void *_self);
		void (* talk) (void *_self, void *otherPerson);
	};

	person1->age;
	person1->run(person1);
	person1->talk(person1, person2);

En este caso cuando nosotros creamos la estructura le asociamos los punteros a los métodos los cuales invocamos con una sintaxis que nos podría hacer pensar en objetos pero no lo es. La principal cuestión de este patrón es la recursividad, como se puede ver la invocación no tiene mucho sentido:

	person1->run(person1);

El hecho de acceder a la función run de person1 y tener que volver a pasarle la instancia lo vuelve algo poco amigable.

# Método II (BIS):

Una posible alternativa que se podría suponer que funciones es utilizar inner functions ( solo soportado por el GCC ) para solucionar el problema anterior:

	struct Person {
		char * name;
		int age;
		void (* walk) ();
		void (* run) ();
		void (* talk) (void *otherPerson);
	};

	static void * Person_init(void * _self){
		struct Person * self = _self;

		void inner_walk(){
			Person_walk(self);
		}
		self->walk = inner_walk;

		void inner_run(){
			Person_run(self);
		}
		self->run = inner_run;

		void inner_talk(void *arg){
			Person_talk(self, arg);
		}
		self->talk = inner_talk;

		return _self;
	}

	person1->run();

Pero lamento comunicar que esto no funciona, ya que el scope de las inner functions esta limitado a al stack de llamada, por lo que llamar a person1->run(); fuera del Person_init genera un seg fault porque el self al que llaman todas las inner ya no esta mas dentro del stack.

# Método III:

Esta forma es una combinación de sintaxis del Método II con el concepto del Método I, es decir utilizamos punteros a funciones dentro de nuestra estructura pero lo trabajamos como si fueran clases con métodos estáticos:

	struct PersonClass {
		void (* walk) (void *_self);
		void (* run) (void *_self);
		void (* talk) (void *_self, void *otherPerson);
	};

	struct Person {
		char * name;
		int age;
	};

	void  Person_walk(struct Person *);
	void  Person_run(struct Person *);
	void  Person_talk(struct Person *, struct Persona *);

	static const struct PersonClass PersonClass = {
			Person_walk,
			Person_run,
			Person_talk
	};

	struct Person *person1;

	PersonClass.run(person1);

Bueno dejando de lado como inicializamos ( ya que lo voy a exponer mas adelante ), vemos que la forma de trabajar de esto es mas o menos similar a las anteriores pero rejunta un poco de cada una. El punto es ¿Porque hace PersonClass.run(person1); en vez de hacer Person_run(person1)?
La respuesta es simple, C no es un lenguaje que nos deje sobre-escribir funciones tal como se puede en otros lenguajes, y menos hacer sobrecarga de funciones por lo cual dentro de nuestro entorno de compilación solo puede existir un solo Person_run que tiene un único comportamiento predefinido. Cuando trabajamos con PersonClass.run, este resulta ser un puntero a una función por lo cual nosotros podemos optar por cambiar el puntero para que apunte a otra función y con eso logramos cambiar la implementación Esto seguramente nos puede resultar muy útil para de alguna forma mockear nuestras estructuras o realizar cierto tipo de herencia ( veremos mas adelante ).
Como detalle hay que tener en cuenta que el manejo de punteros a funciones bajo esta metodología no es de los mas performante que existe. Esto es porque el compilador no sabe resolver los punteros de las funciones para que queden de tal forma que sean contiguos. Esto nos podría generar que el IP del CPU valla saltando de lado a lado por la memoria generando una enorme cantidad de page fault, cosa que sin la utilización de punteros el compilador puede ordenar la memoria para minimizar esta situación

# Método IV:

Como ultimo método pediremos ayuda a las poderosas macros de C:

	#define INIT(var)	void var##_run() { Person_run(var); } \
				void var##_walk() { Person_walk(var); } \
				void var##_talk(arg) { Person_talk(var, (void*)arg); }

	#define NEW(var, type)	type *var = malloc( sizeof(type) ); INIT(var)

	NEW(person1, struct Person);

	person1_run();
	person1_talk(person2);

Tal y como se observa corremos con las ventaja de que en esta ocasión el pre-compilador nos ayuda a construir las invocaciones y finalmente podremos lograr una sintaxis en la que invocamos el método de una instancia. Nuestro principal problema acá es que nuestro scope es reducido ya que el NEW es local al bloque y si pasamos person1 como referencia a otra función dentro de esta otra deberíamos hacer INIT(person1). Por otro lado hay que tener en cuenta que eso obviamente nos genera una gran cantidad de código extra que si bien no es visible en la edición del código si lo puede ser cuando se compila.

Instanciación & Destrucción:

Hasta lo que mencione ahora otro de los factores a tener en cuenta en el modelo orientado a objetos es la instanciaciones de un objeto. A los fines prácticos cuando uno instancia una clase se reserva memoria para el objeto y se inicializa esta misma. Vamos a ver como logramos eso:

#ifndef CLASS_H_
#define CLASS_H_

	#include <stddef.h>
	#include <stdarg.h>

	struct Class {
		char * name;
		size_t size;
		void * (* ctor) (void * self, va_list * app);
		void * (* equals) (const void * _class1, const void * _class2);
		void * (* dtor) (void * self);
	};

	int Class_equals(const void * _class1, const void * _class2);

#endif

int Class_equals(const void * _class1, const void * _class2) {
	const struct Class * class1 = _class1;
	const struct Class * class2 = _class2;
	return strcmp(class1->name, class2->name) == 0
					&& class1->size == class2->size;
}

Acá tenemos definido lo que seria el molde de una clase genérica, lo que vamos a hacer es crear un tipo de clase, que para este caso vamos a tomar los String, y vamos a ver como manejarlo utilizando el Método III de relación entre objetos y mensajes.
Pero antes es necesario implementar nuestro mecanismo de construcion de objetos y para ellos vamos a tener:

#ifndef OBJECT_H_
#define OBJECT_H_

void * new (const void * type, ...);
int instanceOf (const void * obj, const void * class);
void delete (void * obj);

#endif /* OBJECT_H_ */

#include
#include
#include
#include

#include "Class.h"
#include "Object.h"

void * new(const void * _class, ...) {
const struct Class * class = _class;
void * p = calloc(1, class->size);
assert(p);
*(const struct Class **) p = class;
if (class->ctor) {
va_list ap;
va_start(ap, _class);
p = class->ctor(p, &amp;ap);
va_end(ap);
}
return p;
}

int instanceOf(const void * obj, const void * _class) {
const struct Class ** cp = obj;
const struct Class * class = _class;
return strcmp((*cp)->name, class->name) == 0
&amp;&amp; (*cp)->size == class->size;
}

void delete(void * obj) {
const struct Class ** cp = self;
if (self && *cp && (*cp)->dtor)
self = (*cp)->dtor(self);
free(self);
}

Como se observa ahora tenemos un mecanismo de instanciación y destrucción que nos permite llamar a los métodos correspondientes de una clase y liberar su memoria. Pero ahora necesitamos nuestra clase String y definir sus comportamientos de destrucción y construcción.

#ifndef STRING_H_
#define STRING_H_

#include "Class.h"

struct String {
const void * class; /* must be first */
char * text;
};

struct StringClass {
void * (* clone) (struct String *);
int (* equals) (struct String *, struct String *);
};

void * String_constructor (void * _self, va_list * app);
void * String_destructor (void * _self);
void * String_clone (void * _self);
int    String_equals (void * _self, void * _other);

static const struct Class _String = {
"String",
sizeof(struct String),
String_constructor, Class_equals, String_destructor
};

static const void * String = &amp;_String;

static const struct StringClass StringClass = {
String_clone,
String_equals
};

#endif /* STRING_H_ */

#include
#include
#include
#include

#include "Class.h"
#include "Object.h"
#include "String.h"

void * String_constructor (void * _self, va_list * app){
struct String * self = _self;
const char * text = va_arg(* app, const char *);
self->text = malloc(strlen(text) + 1);
assert(self->text);
strcpy(self->text, text);
return self;
}

void * String_destructor (void * _self){
struct String * self = _self;
free(self->text), self->text = NULL;
return self;
}

void * String_clone (void * _self){
struct String * self = _self;
return new(String, self->text);
}

int String_equals (void * _self, void * _other){
struct String * str1 = _self;
struct String * str2 = _other;
return strcmp(str1->text, str2->text) == 0;
}

Finalmente, ya tenemos nuestra “clase” String con un constructor, un destructor, un clonador y un comparador. Para una pequeña prueba:

int main(void) {
struct String *str = new(String, "Hola!!!!");
struct String *aux = StringClass.clone(str);

printf("%s\n", str->text);
printf("%s\n", aux->text);
printf("%d\n", StringClass.equals(str, aux));

return EXIT_SUCCESS;
}

Output:

Hola!!!!
Hola!!!!
1

FUENTE: http://docs.google.com/viewer?url=http://www.planetpdf.com/codecuts/pdfs/ooc.pdf


Smalltalks 2009 en Buenos Aires

10 noviembre 2009

Y después de que la comunindad Argentina de Smalltalk se revigorizara a principios de este año cuando nuestro compatriota Gabriel Honoré ganara el premio “Innovation Award” de ESUG 2009, haciendo historia como la primera persona fuera de Europa en recibirlo, ahora se anunció la conferencia Smalltalks 2009 en Buenos Aires para los próximos días 19, 20 y 21 de Noviembre.

El evento se realizará en la Facultad de Ciencias Exactas y Naturales de la Universidad de Buenos Aires (UBA) y su entrada es libre y gratuita, pero es necesarioregistrarse antes. Para la conferencia se organizó también, y por primera vez, unconcurso de programación con premios que incluyen un iPod Touch de 8 Gb y una cámara Nikon Coolpix L20.

La lista de disertantes que presentarán sus charlas es realmente impresionante, y la personalidad más destacada es sin duda Daniel Henry Holmes Ingalls, pionero de la programación orientada a objetos y principal diseñador y arquitecto de cinco generaciones de entornos de Smalltalk. Smalltalk es un sistema completo que permite realizar tareas de computación mediante la interacción con un entorno de objetos virtuales, generalmente compuesto por:

 

  • Una máquina virtual.
  • Una imágen virtual que contiene todos los objetos del sistema.
  • Un lenguaje de programación (también conocido como Smalltalk).
  • Una biblioteca de objetos reusables.

 

Opcionalmente un entorno de desarrollo que funciona como un sistema en tiempo de ejecución.

Y como lo menciona la fuente de esta noticia, aunque quizás no relacionado con la conferencia en sí, esta charla de Dan Ingalls sobre el proyecto Lively Kernel, una plataforma para aplicaciones web con gráficos dinámicos, acceso de red, y herramientas de desarrollo que no requiere nada más que un navegador, no tiene desperdicio.

 

Via: http://www.vivalinux.com.ar/eventos/smalltalks-2009-buenos-aires

 


Links Interesantes II

10 noviembre 2009

# Can Java Be Saved???http://java.dzone.com/articles/can-java-be-saved

Un articulo muy intersante de leer, en resumidas cuentas esto es un poco de lo que ya sabemos y ya se viene sabiendo, Java es popular pero no quita que se este quedando atras. Hay que diferencias la plataforma de la JVM ( la cual es una masa ) con el lenguaje insignia que corre sobre esta ( el cual no es exactamente lo mejor que existe ).

Uno obviamente no puede comparar lenguajes en sentido de Java o Ruby, porque son mundo diferentes e incluso targets diferentes. Pero uno si puede ver como muchos lenguajes tienen un proceso evolutivo mucho mas rapido que Java.

Hace ya unos bueno 4 años que salio la JDK 6 ( si mucha gente no la conoce, una pena por ellos pero para los que utilizamos todo el potencial del lenguaje es mas que claro que ya se esta quedando vieja ) y recien a fines del proximo año veremos la JDK 7 ( que a traido mas decepciones que alegrías, no es que sea basura pero para muchos realmente se esperaba mucho mas ).

Estos features, mensionados solo son algunas de las cosas que realmente muchos esperabamos ver en Java 7 y que seguramente tendremos que esperar otros 4 años a Java 8 para quizas verlos. El impacto de estos es cierto que generarian cierta retrocompatibilidad ( quizas ) pero seria un paso mas para que el dia de mañana cuando una aplicacion/proyecto/sistema quiera aprobechar las nuevas cosas, nos permita mayor fluides en el desarrollo de este.

Java is Dead??

Yo creo que por ahora no, pero si en lo proximo 2 años no se hace algo, por darle mayor potencial al lenguaje definitivamente va a morir ( no es que desaparezca sino que realmente no va a resultar una buena opcion para el desarrollo y posiblemente empresas de ultima tecnologia, vease google, van a empezar a promocionar la utilizacion de otros lenguajes mas poderosos, faciles de usar, etc … vease python, ruby, etc … y esto no es algo nuevo ya que grandes lenguajes popularmente usados han desaparecido a lo largo de la historia justamente por su poca evolucion vease Visual Basic, C++, Cobol, etc… ). Obviamente no hablamos de que nos despertamos un dia y ya nadie usa Java, sino que hablamos de un proceso en el cual Java en sus años venideros simplemente va a empezar a dejarse de tomar como alternativa.

Ohh pero la grandes empresas apoyan al lenguaje, y??? queres pensarlo 100% empresa?? bueno esto es costo beneficio, si yo te digo tengo un lenguaje que me permite hacer lo mismo que Java pero en menos tiempo, de manera mas simple, mas legible y mas escalar, obvio que el cliente te dice donde firmo????? Porque defender un lenguaje que solo te complica las cosas no sirve de mucho que digamos.

# Soft, Weak and Phantom references in Java – http://www.rachvela.com/2009/11/soft-weak-and-phantom-references-in.html

Muy interesante para leer y tener en cuenta.

# Dependency Injection Makes Your Code Worse - http://java.dzone.com/articles/dependency-injection-makes

Me parecio interesante el comentario, sobre todo porque es casi un comentario muy cuestionable teniendo en cuenta de que muchos tendemos a utilizar inyeccion de dependencias. De por si creo que el autor no termino de entender el concepto y la utilización de inyeccion de dependencias. Yo creo que a fin de cuenta todo depende mucho del programador y como lo haga, si aplicas mal el patron de diseño y usas un framework malo seguramente vas a estar en un punto en donde vas a decir ….  de donde salio esto?? que esto??? y seguramente termines en un gran problema de dependencias. Pero si un programador con experiencia arma un buen modelado orientado a DI  seguramente no vas a tener mucho problema. Para Spring es feo, pero no hace que todos los IoC sean feos, Guice es muy bueno a mi criterio y bien aplicado es muy util y facilita varias cosas.


Links Interesantes I

1 noviembre 2009

 

  • Un blog interesnate sobre HTML 5:  http://carsonified.com/blog/web-apps/the-future-of-html-5/
    Esta interesante ver algunos de los ejemplos que demuestran lo que se puede hacer hasta ahora, seguramente con la introduccion de WebGL para la aceleracion de graficos atravez de navegadores se pueda lograr un rendimiento mucho mas alto sin gastar tanta CPU.

Google DevFes en Argentina

1 noviembre 2009

El DevFest 2009 de Google, se enfocará en promover la apertura de las fronteras de las aplicaciones Web empleando las tecnologías de desarrollo de Google. Los ingenieros de Google y desarrolladores líderes serán tus anfitriones en un día completo de profundas sesiones sobre las últimas tecnologías de Google.

 

Fecha: 17 de noviembre de 9 a 20 ( Paseo La Plaza Av. Corrientes 1660)

 

URL: https://sites.google.com/a/mazalan.com.ar/devfest/home


FatELF los nuevos binarios universales para Linux

1 noviembre 2009

FatELF es nuevo formato de archivo que permite “embeber” múltiples archivos binarios ELF (Executable and Linkable Format) para diferentes arquitecturas en un solo archivo mucho más fácil de distribuir….

 

Sólo algunos de los beneficios anunciados que FatELF traería a Linux incluyen:

  • Ya no será necesario que las distribuciones tengan descargas separadas para varias plataformas.
  • Ya no será necesario tener directorios /lib, /lib32 y /lib64 separados.
  • Ya no será necesaria la librería de compatibilidad ia32.
  • Soporte para binarios de 32 y 64 bits en un sólo archivo.
  • Se podrá distribuir un único archivo que funcione en Linux y en FreeBSD.

 

Via: http://www.vivalinux.com.ar/soft/fatelf


JPA 2.0

23 octubre 2009

Aca posteo algunos links de JPA2.0 era algo previsible de cambio con algunas mejoras importantes como la API de criteria que tenia hibernate años atras, no se como no vieron ese feature, hacer queries con strings es casi como usar jdbc con el mapeo resuelto, por suerte parece ser un error corregido para esta nueva version, tambien vi que agregaron una api standard de cache, que es un must para que una aplicación sea mas o menos performante.

http://www.infoq.com/presentations/whats-new-and-exciting-in-jpa-20
http://en.wikibooks.org/wiki/Java_Persistence/What_is_new_in_JPA_2.0%3F
http://www.ibm.com/developerworks/java/library/j-typesafejpa/


Lo Interesante del Día

23 octubre 2009

Lo Interesante del Día

5 octubre 2009

Estos días fueron bastante interesantes, la net supo alimentar mi sed de información

  • Top 20 de los Lenguajes de Programación ( http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html )
    En el puesto numero uno es para ….. Java! … que raro no?,  pero viéndolo desde  un puesto de vista mas general, es una pena ver lenguajes emergentes como Groovy o Scala tan lejos y lenguajes tan poco agradables ( por decirlo de una manera linda ), como Perl o Visual Basic tan cerca de la cima. Que mundo generoso, o que cantidad de código basura hay dando vuelta.
  • Swing Application Framework from Java 7 ( http://www.infoq.com/news/2009/09/java7_m5 )
    Y yo que pensaba que no iba a pasar dentro de esta decada, pero así es Swing renace nuevamente! Mejorado, con Annotations y con Generics ( aunque juraría que estas cosas son de Java 6 ). Es bueno ver el progreso de la plataforma pero es decepcionante ver lo lento que van.  Oracle ayudara a la situación?
  • El XML del XML ( http://jibx.sourceforge.net/ )
    Las Annotations son muy complejas? JAXB 2 te resulta un desafío? Dom4j ya no te satisface?  No puede dormir pensando que las cosas son demasiado complejas? …. Acá tenemos tus soluciones, JiBX, ahorrarte los problemas bindeando XML con mas XML!
    En realidad me da casi vergüenza mostrar este link, pero a veces es necesario ver nuestras falencias para aprender de estas. Que clase de persona en su sano juicio considera al XML como


    Youtube (Arquitectura de Alta disponibilidad)

    2 octubre 2009

    Si no podias dormir pensando como hacia una pagina de streaming de video para soportar que mas de 20 usuarios subieran la saga de Hades de Saint Seiya completa y nunca tener un DoS

    Te recomiendo que leas este post, donde explica entre otras cosas que Youtube utiliza Python (ooooohoooo) y tecnologia muy LAMP

    http://highscalability.com/youtube-architecture

    Dentro de ese post podes ver un link a google video (y porque no youtube? :P ) donde explican que vinieron haciendo.


    Seguir

    Get every new post delivered to your Inbox.