sábado, 30 de abril de 2011

macros en C


En el tutorial anterior hable sobre las funciones
plantilla
y hacia una comparación con el procesador
de macros de C, ahora voy a mostrar una
reimplementación, utilizando cpp (C Pre Procesor),
el siguiente programa esta escrito en C, no en C++, aunque
se puede compilar con un compilador de C++, la diferencia
es mínima, solo cambian los archivos de cabecera (stdio.h
en vez de iostream) y que se usa printf en vez de cout.

 1 /**********************************************
 2    * Name:     print_size_macro.c
 3    * Created:   18-Abr-2011
 4    * Author:    Gabriel Espinoza
 5      <virtuosonic@users.sourceforge.net>
 6    * License:     MIT
 7    ********************************************/
 8  #include <stdio.h>
 9  
10  #define print_size(x) \
11      printf("  %s  %i bits\n", \
12      #x,sizeof(x)*8)
13  
14  int main()
15  {
16      puts("types size:");
17      print_size(char);
18      print_size(short);
19      print_size(int);
20      print_size(long);
21      print_size(float);
22      print_size(double);
23      print_size(long double);
24  }

Toda la acción sucede entre la linea 10 y 12,
donde se define el macro print_size(), este
macro se expande simplemente a una llamada
a la función printf, lo que importa es como
se pasan los parametros, %s se sustituye
por una cadena de carateres y %i por un
numero entero, # convierte el parametro
del macro en una cadena de caracteres por ejemplo
char se convierte en "char". Tambien
se podria haber colocado el ';' dentro del macro,
pero preferi no hacerlo para ponerlo en cada
llamada al macro para mantener el estilo.

En la función main empezamos con una
llamada a puts que escribe en la salida
estandar, usamos esta función en vez de printf
por que printf es una función muy compleja
y solo deberiamos usarla para cosas complejas, a
continuacion aparecen varias llamadas a print_size y
termina nuestro programa.

Comparativa con plantillas

En este ejemplo especifico podemos ver una ventaja,
en la implementación anterior usabamos
type_id(T).name(), esto tenia la desventaja que
cada compilador produce diferente salida, en esta siempre
sera uniforme, lo que aumenta la portabilidad entre
compiladores. Pero no debemos de ver los macros
como algo inocente, los macros podrian usarse de
maneras que no fueron pensadas, por ejemplo
intenta agregar esto:

 char c[256];
 print_size(c);

Nota: en Visual Studio para que compile
si agregas las dos lineas anteriores debes
declarar char c[256]; antes de la primera
llamada a print_size.

Todavia mas raro prueba esto

 print_size(12468944444444444444);

Luego lo puedes cambiar un poco

 print_size(12468944.444444444444);

Y para perder el sentido por completo:

 print_size(0);
 print_size(0.0);

Pruebenlo, en serio, los macros no son tan buenos.


Bajar el codigo de este programa

lunes, 18 de abril de 2011

plantillas en c++ (templates)


En este tutorial voy a exponer una de las características
mas complicadas del C++, las plantillas (en
ingles templates) y para la
demostración usare un problema muy sencillo y fácil de
entender.

Problema: El entandar C++ no especifica un tamaño para los
tipos de variable, solo dice que deben tener un
tamaño mínimo.


Solución: Escribir un programa que muestre el tamaño
de los tipos de variable.


Para saber el tamaño de un tipo o variable
podemos usar el operador sizeof,
que retorna el tamaño en bytes, podríamos multiplicarlo por
8 para saber cuantos bits son, asi que para mostrarlo en pantalla
usaríamos este código:

std::cout << "char " << sizeof (char) * 8;
Pero seria un desperdicio tener que repetir todo
para cada tipo, aquí es donde entran en funcion las plantillas,
este es el programa terminado.



 1 /**********************************************
 2    * Name:     test_type_sizes.cpp
 3    * Created:   19-Abr-2011
 4    * Author:    Gabriel Espinoza
 5      <virtuosonic@users.sourceforge.net>
 6    * License:     MIT
 7    ********************************************/
 8  #include <iostream>
 9  #include <typeinfo>
10  #include <iomanip>
11  
12  using std::cout;
13  using std::endl;
14  using std::cin;
15  using std::setw;
16  using std::setiosflags;
17  using std::ios;
18  
19  template <class T> void print_size()
20  {
21      cout << "  " << setiosflags(ios::left)
22          << setw(12)
23          << typeid(T).name()
24          << resetiosflags(ios::left)
25          << setw(3)<< sizeof (T)*8
26          <<" bits" << endl;
27  }
28  
29  int main()
30  {
31      cout << "types size:" << endl;
32      print_size<bool>();
33      print_size<char>();
34      print_size<short>();
35      print_size<int>();
36      print_size<long>();
37      print_size<float>();
38      print_size<double>();
39      print_size<long double>();
40      cin.get();
41  }

Las primeras 7 lineas son comentarios, en resumen dicen
que el programa lo escribi yo y no me hago responsable
si tu computadora, celular, horno de microondas, tamagochi
o cualquier otro aparato en el que lo pruebes estalla, te asesina,
te deja sin empleo, domina el mundo, se va de la casa caminando,
etc.
De la linea 8 a la 16 declaramos que usaremos
algunas partes de la STL (standard template library(libreria de plantillas estandar))

En las lineas 18 a la 22 encontramos la parte
interesante, es la funcion print_size que hace toda
la accion, seguro ya notaste que no lleva parametros,
esto es porque no trabaja con objetos o variables,
solamente nesecita saber el tipo que debe analizar y
ese tipo es el que se pasa como el parametro de la
plantilla (notese la diferencia entre parametro de plantilla
y parametro de funcion).
Dentro de la funcion main (que es el punto de
inicio del programa) podemos ver el uso de la funcion
print_size, los espacios y la funcion setw son
usados para formatear la salida(hacer que se vean derechas
las letras en la pantalla), setiosflags y resetiosflags
se modifica la alineacion del texto, con typeid(T).name()
sabemos el nombre del tipo de T, el valor retornado por
sizeof lo multiplicamos por 8 para convertirlo a bits.

A algunos autores les gusta comparar las plantillas con la
Herencia Multiple, en mi opinion aunque se parecen, hay
problemas como el anterior que no se pueden resolver con
Herencia multiple, asi que me parece mas apropiado comparar
a las plantillas con el "Pre Procesador de C" (en ingles c
preprocesor o cpp), el ejemplo anterior bien se podria haber
resuelto con macros de cpp, pero al ser un proceso aparte el
compilador no puede localizar errores en macros tan facilmente
y cpp por lo mismo es responsable (en muchos casos) de complicar
el mantenimiento de los programa que lo usan, el mismisimo
Bjarne Stroustrup (el creador de C++) ha declarado que cpp es
redundante y que quisiera verlo abolido. Asi que sugiero usar
plantillas en vez de macros siempre que sea posible.

Nota: Esta solucion no es completamente efectiva
porque segun el estandar C++, el valor retornado por
typeid(T).name() es dependiente de implementacion,
o sea que cada compilador puede retornar el valor que le
de la gana, como gcc (GNU Crap Compiler) que por ejemplo
para long double retorna 'e'.


Bajar el codigo de este programa