Archivo

Archivo para la categoría ‘C/C++’

Hallar la IP de un dispositivo en C

Lunes, 30 de Abril de 2012 Gaspar Fernández 2 comentarios

Puede que se nos haya presentado alguna vez la necesidad de saber la dirección de un dispositivo desde nuestro programa, y no es plan de ponernos a ejecutar ifconfig o algún programa parecido para hallar la dirección.

Podemos hablar con ioctl() una función destinada a definir y obtener información de dispositivos. Hay cientos de llamadas, aquí comentaremos la llamada SIOCGIFADDR cuya función es la que comentábamos, obtener la dirección de un interfaz de red.

Podemos probar el siguiente programa con el que hallaremos la IP del dispositivo eth0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>

int main()
{
  int sock;
  struct ifreq ifr;

  sock=socket(AF_INET, SOCK_DGRAM, 0);
  if (sock<0)
    return -1;          /* No puedo crear el socket */

  ifr.ifr_addr.sa_family = AF_INET;
  strcpy(ifr.ifr_name, "eth0");

  if (ioctl(sock, SIOCGIFADDR, &ifr) < 0)
    return -1;          /* No encuentro el dispositivo */
 
  printf("IP de eth0: %s\n", inet_ntoa( (*(struct in_addr *) &ifr.ifr_addr.sa_data[2])));

  close(sock);

  return 0;
}

Primero creamos un socket, luego definimos la familia y el dispositivo que queremos consultar con:

1
2
ifr.ifr_addr.sa_family = AF_INET;
  strcpy(ifr.ifr_name, "eth0");

Para comprender cómo obtenemos la dirección debemos echar un ojo a las estructuras:
(struct ifreq)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct ifreq
{
    char            ifr_name[IFNAMSIZ];   /* Nombre de la interfaz */
    union {
                    struct sockaddr       ifr_addr;
                    struct sockaddr       ifr_dstaddr;
                    struct sockaddr       ifr_broadaddr;
                    struct sockaddr       ifr_netmask;
                    struct sockaddr       ifr_hwaddr;
                    short                 ifr_flags;
                    int                   ifr_ifindex;
                    int                   ifr_metric;
                    int                   ifr_mtu;
                    struct ifmap          ifr_map;
                    char                  ifr_slave[IFNAMSIZ];
                    char                  ifr_newname[IFNAMSIZ];
                    char *                ifr_data;
    };
}

(struct sockaddr)

1
2
3
4
5
struct sockaddr
{
   unsigned short sa_family;  /* familia de la dirección */
   char sa_data[14];          /* 14 bytes de la dirección del protocolo */  
};

(struct sockaddr_in)

1
2
3
4
5
6
7
struct sockaddr_in
{
   short int sin_family;        /* Familia de la Dirección              */
   unsigned short int sin_port; /* Puerto                               */
   struct in_addr sin_addr;     /* Dirección de Internet                */
   unsigned char sin_zero[8];   /* Del mismo tamaño que struct sockaddr */
};

(struct in_addr)

1
2
3
4
struct in_addr
{
   unsigned   long s_addr;
};

Bien, en el programa anterior, la línea más conflictiva es:

1
printf("IP de eth0: %s\n", inet_ntoa( (*(struct in_addr *) &ifr.ifr_addr.sa_data[2])));

En ella, sacaremos la dirección en forma de struct in_addr que es el que le gusta a inet_ntoa() (esta función sólo coge la dirección y la pone en forma de char* para que podamos entenderla.
Para llegar al struct in_addr, partimos de la estructura struct ifreq (variable ifr) que nos devuelve ioctl(), ésta tiene un campo llamado ifr_addr de tipo struct sockaddr. El tipo struct sockaddr y struct sockaddr_in podemos compararlos, es decir, podemos ponerlos uno encima de otro y pueden tener la misma información en el mismo byte, por tanto, podemos ver que el campo sa_data de un struct sockaddr coincide con sin_port, sin_addr y sin_zero de un struct sockaddr in; vemos que sin_port es de tipo unsigned short por tanto ocupará 2 bytes y como queremos la información encerrada en sa_data a partir del byte 2, de ahí viene dicho número. Si queremos ponerlo de otra forma, ésta podría ser:

1
2
struct sockaddr_in sain;
  printf("IP de eth0: %s\n", inet_ntoa( (*(struct in_addr *) &ifr.ifr_addr.sa_data[sizeof(sain.sin_port)])));

Algoritmos: generar números aleatorios para la lotería

Lunes, 9 de Abril de 2012 Gaspar Fernández Sin comentarios

Es un ejemplo típico y nos muestra el uso de rand() con arrays para generar varios números aleatorios y no repetidos.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
*************************************************************
* @file loteria.c
* @brief Saca números aleatorios para la lotería
* Basado en el sorteo de la primitiva, hay que sacar 7 números
* del 1 al 49, sin repetir
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <time.h>       /* para time() */


int numero_aleatorio(int desde, int hasta)
{
  return desde+rand()%(hasta-desde+1);
}

short numero_repetido(int numeros[7], int n)
{
  int i=0;

  while (i<n)
    {
      /* Si un número sacado anteriormente es igual al número
         en la posición n, decidido, está repetido. */

      if (numeros[i]==numeros[n])
    return 1;
      i++;
    }

  /* Si llegamos hasta aquí, el número no se ha repetido */
  return 0;
}

void numeros_loteria(int numeros[7])
{
  int i;

  /* El primer número lo generamos, este no se repetirá con nadie
     anterior */

  numeros[0]=numero_aleatorio(1, 49);

  /* A partir del segundo número tenemos que verificar que no se
     repite. */

  for (i=1; i<7; i++)
    {
      do
    {
      /* Generamos un número */
      numeros[i]=numero_aleatorio(1,49);
      /* Si el número está repetido, volvemos a generar */
    } while (numero_repetido(numeros, i));
    }
}

int main(int argc, char *argv[])
{
  int numeros_premiados[7];
  int i;
  srand(time(NULL));        /* Una nueva semilla de números aleatorios */

  numeros_loteria(numeros_premiados);
  for (i=0; i<7; i++)
    printf ("Numero %d -> %d\n", i+1, numeros_premiados[i]);
 
  return EXIT_SUCCESS;
}

Descargar: loteria.c (1.6Kb)

En este ejemplo vamos guardando todos los números en un array y vamos comparando los nuevos números que vamos generando con los antiguos.

Algoritmos: Validar un DNI en C

Miércoles, 4 de Abril de 2012 Gaspar Fernández Sin comentarios

En España, para identificar de forma única a cada ciudadano se utiliza el número del Documento Nacional de Identidad (DNI). Como es costumbre, en muchos datos numéricos, como este, un número de cuenta corriente, códigos ISBN, etc existe un algoritmo de verificación que comprueba que el número es válido. En este caso, la comprobación se realiza con la letra que acompaña el número.

El algoritmo es sencillo, puede que a la hora de implementarlo no nos acordemos del orden de las letras (para eso lo pongo, y podemos hacer Copia y Pega rápidamente).

Tal vez si estás empezando con la programación y no domines el uso de Arrays te resulte algo complicado, pero es un buen momento para empezar a programar Arrays, te ayudarán mucho y te ahorrarán en casos como este muchísimo código.

Presento primero una función que genera la letra del DNI a partir del número:

1
2
3
4
5
6
char letraDNI(int dni)
{
  char letra[] = "TRWAGMYFPDXBNJZSQVHLCKE";

  return letra[dni%23];
}

En definitiva, necesitamos averiguar el resto de el número del DNI entre 23 (lo cual nos dará una cifra entre 0 y 22), cada cifra corresponderá con una letra: el 0 con la T, el 1 con la R, el 2 con la W…

Podemos utilizar esta función, no para generar, sino para validar un DNI almacenado en una cadena de caracteres:

1
2
3
4
5
6
7
short verificaDNI(char *dni)
{
  if (strlen(dni)!=9)
      return 0;
  else
    return (letraDNI(atoi(dni))==dni[8]);
}

En ella, convertimos el número a entero (después de verificar que hay 9 caracteres), y se lo pasamos a letraDNI() para que genere una letra, luego comparamos la letra generada con la letra introducida por el usuario.

Aquí vemos el código completo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
*************************************************************
* @file dni.c
* @brief Comprueba la letra del DNI (España)
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char letraDNI(int dni)
{
  char letra[] = "TRWAGMYFPDXBNJZSQVHLCKE";

  return letra[dni%23];
}

short verificaDNI(char *dni)
{
  if (strlen(dni)!=9)
      return 0;
  else
    return (letraDNI(atoi(dni))==dni[8]);
}

int main(int argc, char *argv[])
{
  char dni[20];
 
  printf ("Introduce tu DNI con letra (sin espacios): ");
  scanf("%s", dni);

  if (verificaDNI(dni))
    printf ("El DNI es correcto\n");
  else
    printf ("El DNI no es correcto\n");
   
  return EXIT_SUCCESS;
}

Descargar: dni.c (838bytes)

Algoritmos: Probar la existencia de un fichero con open() en C

Sábado, 31 de Marzo de 2012 Gaspar Fernández 2 comentarios

Estos días a varios de mis alumnos les ha surgido la necesidad de probar la existencia de un archivo haciendo la llamada al sistema open(). Es bastante sencillo, sólo hay que probar si éste se ha podido abrir (como lectura, por ejemplo, para no tocar el fichero en la medida de lo posible).
Una vez se abre el fichero pueden ocurrir varias cosas:

  • que la llamada a open() no devuelva error. Por tanto el fichero existe
  • que la llamada a open() de error:
    • si el resultado de errno es ENOENT (constante de error), ENOFILE en algunos sistemas o 2 (valor que generalmente vale dicha constante), el fichero no existe
    • si el resultado de errno es distinto, no podemos asegurar que no exista, pero tal vez el nombre de fichero no sea correcto, la ruta no exista, o no tengamos acceso a él.

Para contemplar estos casos creamos la función exists(). Incluyo la función y el código de ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
*************************************************************
* @file exists.c
* @brief Verifica la existencia o no de un fichero
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

short exists(char *fname)
{
  int fd=open(fname, O_RDONLY);
  if (fd<0)         /* error */
    return (errno==ENOENT)?-1:-2;
  /* Si no hemos salido ya, cerramos */
  close(fd);
  return 0;
}

int main(int argc, char *argv[])
{
  int abre=exists("archivo");
  switch (abre)
    {
    case 0: printf ("El archivo existe\n"); break;
    case -1: printf ("El archivo no existe\n"); break;
    case -2: printf ("Ocurrió un error al abrir. %d (%s)\n", errno, strerror(errno)); break;

    default: printf ("Nunca veremos este mensaje");
    }

  return EXIT_SUCCESS;
}

Descargar: exists (1Kb)
Para ejecutarlo podemos probar:

$ ./exists
El archivo no existe
$ touch archivo
$ ./exists
El archivo existe
$ chmod -r archivo # Eliminamos permiso de lectura al fichero
$ ./exists
Ocurrió un error al abrir. 13 (Permission denied)

Puede que también seas de los que piensan que si hay algún error al abrir el archivo, da igual, lo marcamos como no existente, de todas formas no podemos hacer nada con él. Así, construimos una función exists() más intuitiva que devuelve 1 cuando existe y 0 cuando no existe o hay error:

1
2
3
4
5
6
7
8
9
short exists(char *fname)
{
  int fd=open(fname, O_RDONLY);
  if (fd<0)         /* error */
    return 0;
  /* Si no hemos salido ya, cerramos */
  close(fd);
  return 1;
}

Algoritmos: Factorial y cálculo del seno por la serie de MacLaurin (Taylor alrededor de 0) en C

Martes, 27 de Marzo de 2012 Gaspar Fernández 2 comentarios

Algo más matemático vuelvo cuando propongo lo siguiente. Vamos a calcular un seno con la serie de MacLaurin. Ésta es la fórmula, extraída de Wikipedia.sin_maclaurin

Y este el algoritmo en C:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#define PI 3.141592653588

unsigned factorial(int n)
{
  if (n<1)
    return 1;
 
  return n*factorial(n-1);
}

double senoTaylor(double n)
{
  double t=0;
  int i;

  short signo=(n>=0);
  n=fabs(n);            /* Calculamos el valor absoluto */

  while (n>PI)      /* Reducimos el ángulo, de dos en dos cuadrantes*/
    {
      n-=PI;            /* 1 vuelta = 2PI radianes */
      signo=!signo;     /* cada PI, el signo cambia */
    }

  for (i=0; i<9; i++)
    t+=pow(-1, i)*pow(n, 2*(double)i+1)/(double)factorial(2*i+1);

  if (!signo)           /* Cambiamos el signo del ángulo */
    t*=-1;

  return t;
}

Con este algoritmo, cuantas más iteraciones demos, más preciso será el cálculo, aunque tenemos que tener cuidado de nos desbordar las variables, ya que el crecimiento de la función factorial es bastante rápido.

Es importante lo que hacemos al principio, el ángulo en radianes debe ser reducido, nos quedamos con un valor que esté siempre en la mitad superior del eje X (cuadrantes 1 y 2), este algoritmo es mejor con valores pequeños. Sería incluso mejor quedarnos con el ángulo en el primer cuadrante para que mejore la precisión.

Para probar el algoritmo, podemos utilizar este programa, en él utilizamos la función sin() de math.h y la comparamos con esta nueva función (aunque sin es más rápida y mejor, dejamos esta función de Taylor o MacLaurin con fines educativos):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
*************************************************************
* @file senoMaclaurin.c
* @brief Cálculo del seno con el algoritmo de MacLaurin
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*
* Compilar con:
*   $ gcc -o senoMaclaurin senoMaclaurin.c -lm
* para incluir la biblioteca matemática
*************************************************************/


#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define PI 3.141592653588

unsigned factorial(int n)
{
  if (n<1)
    return 1;
 
  return n*factorial(n-1);
}

double senoTaylor(double n)
{
  double t=0;
  int i;

  short signo=(n>=0);
  n=fabs(n);            /* Calculamos el valor absoluto */

  while (n>PI)      /* Reducimos el ángulo, de dos en dos cuadrantes*/
    {
      n-=PI;            /* 1 vuelta = 2PI radianes */
      signo=!signo;     /* cada PI, el signo cambia */
    }

  for (i=0; i<9; i++)
    t+=pow(-1, i)*pow(n, 2*(double)i+1)/(double)factorial(2*i+1);

  if (!signo)           /* Cambiamos el signo del ángulo */
    t*=-1;

  return t;
}

int main(int argc, char *argv[])
{
  double n;
  printf ("Introduzca un numero: ");
  scanf ("%lf", &n);

  printf ("sen: %lf\n", sin(n));
  printf ("sen: %lf\n", senoTaylor(n));

  return EXIT_SUCCESS;
}

Importante, para compilar el proyecto, debemos utilizar gcc con el modificador -lm para poder utilizar math.h correctamente.

Descargar: senomaclaurin.c (1.2Kb)

Algoritmos: Año bisiesto y validación de fechas en C

Lunes, 26 de Marzo de 2012 Gaspar Fernández 2 comentarios

Para inaugurar la nueva sección de Algoritmos de este blog, empezaré presentando una función para calcular años bisiestos (lo podemos encontrar en Wikipedia en bastantes más lenguajes, pero, ¿por qué aquí no?

1
2
3
4
function bisiesto(int year)
{
  return (a%4==0) && ( (a%100!=0) || (a%400==0) );
}

Es decir, cada 4 años hay un año bisiesto, no cuando el año es múltiplo de 100, aunque sí cuando es múltiplo de 400.

Esta función, podemos utilizarla para validar fechas:

1
2
3
4
5
6
7
8
9
10
11
12
short valida_fecha(short d, short m, int a)
{
  short dias_mes[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

  if ( (d<=0) || (m<=0) )   /* Si el día, o mes es negativo o cero */
    return 0;           /* la fecha no es válida */

  if ( (m!=2) || (!bisiesto(a)) )
      return (d<=dias_mes[m-1]);
  else
    return (d<=dias_mes[1]+1);
}

Es decir, primero creamos un array con los días que tiene cada mes, para que sea de fácil acceso, si lo prefieres, puede ser un array estático: short dias_mes[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
Luego comprobamos si el mes es distinto de 2, o no el año no es bisiesto, en ese caso comparamos con el valor del array (es importante decir que el mes 1 es Enero, pero en nuestro array, Enero será el elemento 0 y por eso restamos 1. Si la condición no se cumple, verificamos que el día es menor o igual que los días de Febrero + 1 (29 días).

En el ejemplo a continuación vemos todo el código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
*************************************************************
* @file valida_fechas.c
* @brief Valida una fecha introducida por teclado
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* http://totaki.com/poesiabinaria/algoritmos/
*************************************************************/


#include <stdlib.h>
#include <stdio.h>

short bisiesto (int a)
{
  return (a%4==0) && ( (a%100!=0) || (a%400==0) );
}

short valida_fecha(short d, short m, int a)
{
  short dias_mes[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

  if ( (d<=0) || (m<=0) )   /* Si el día, o mes es negativo o cero */
    return 0;           /* la fecha no es válida */

  if ( (m!=2) || (!bisiesto(a)) )
      return (d<=dias_mes[m-1]);
  else
    return (d<=dias_mes[1]+1);
}


int main(int argc, char *argv[])
{
  int a,m,d;

  printf ("Por favor, introduce una fecha (D/M/A)\n");
  scanf("%d/%d/%d", &d, &m, &a);
 
  if (valida_fecha(d, m, a))
    printf ("Fecha válida\n");
  else
    printf ("Fecha NO válida\n");

  return EXIT_SUCCESS;
}

Descargar archivo: valida_fechas.c (1Kb)

Generar texto e implantarlo en una imagen desde PHP

Viernes, 10 de Febrero de 2012 Gaspar Fernández 1 comentario

En ocasiones, se puede presentar la necesidad de introducir en nuestros proyectos una imagen cuyo contenido sea un texto, y sobre todo necesitamos integrarlo dentro de nuestro contenido, intentando que no se note que es una imagen, a no se que seleccionen el contenido para copiarlo.

Una de las principales utilidades de esto es evitar el SPAM al escribir nuestra dirección de e-mail en una web. Muchos clientes prefieren enviar un e-mail en lugar de utilizar un formulario de contacto y tenemos que satisfacerles, sin arriesgar la seguridad de nuestro sistema, en el sentido de que hay cientos de motores que se dedican a rastrear webs en busca de direcciones de e-mail (lo cual es muy sencillo de hacer con expresiones regulares, por ejemplo).

Tenemos que estudiar bien cómo hacemos el script, ya que la entrada de datos es esencial. Sería improductivo hacer un script cuya llamada fuera:

1
<img src="scriptGenerador.php?texto=midireccion@miservidor.com" />

Ya que un motor se enteraría de que eso es una dirección de correo igualmente. Por lo que yo sugiero algunas opciones:

  • Para simplificar, podemos codificar el texto, por ejemplo, en base 64, el e-mail anterior quedaría así:bWlkaXJlY2Npb25AbWlzZXJ2aWRvci5jb20 y sería más complicado de detectar, aunque es nuestra responsabilidad hacer la conversión. Además, si en cualquier momento queremos identificar el texto que pusimos, en cierto punto del código debemos hacer la conversión inversa. Por otra parte, aunque es difícil que algún motor se dedique a convertir el texto en base64 a binario de nuevo, es posible.
  • Otra posibilidad es, si sólo vamos a escribir un sólo texto de esta forma, implantarlo dentro del archivo php que genera la imagen, como una variable del propio código, así no debemos escribir nada cuando llamemos al script. Si tenemos que cambiar el texto, basta con editar el script
  • Si, por el contrario, vamos a escribir varios mensajes, podemos asignar una clave a cada uno, por ejemplo, un número o una cadena de texto corta, y en nuestro script hacer corresponder ese número con el texto en cuestión.

Aquí vamos a hacer un ejemplo de la tercera opción. Aunque podemos extender el ejemplo usando bases de datos (si tenemos que introducir muchos mensajes, o muchas direcciones de e-mail diferentes), aquí vamos a crear un array en PHP para hacer corresponder la clave introducida con el valor (el texto que de verdad escribimos).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php
  // Con esta función calculamos la anchura, altura del texto y dónde está la baseline de nuestro texto
  // con el tipo de letra seleccionado.
function dimensions($ldef, $texto)
{
  // Calcular el ancho
  $box=imagettfbbox ($ldef['size'], 0, $ldef['font'], $texto);
  $width = abs(abs($box[2]) - abs($box[0]));

  // Calcular el alto y la baseline
  $box=imagettfbbox ($ldef['size'], 0, $ldef['font'], 'ILyjgq'); // Tiene caracteres que no terminan en la baseline
  $height = abs($box[7] - $box[1]);
 
  $baseline = abs($box[5]);

  return array($width, $height, $baseline);
}

// Definimos un array con los posibles textos que vamos a presentar
$textArray=array("ejemplo", "de un fragmento de texto", 'gaspy@mi_servidor.com');


// Definimos el tipo de letra y el tamaño
$letra = array ('font'  => './LinLibertine_Bd.ttf',
        'size'  => 12,
        'color' => array(0,0,0),
        );

// Definimos el texto a presentar
$textID  = (isset($_GET['texto']))?$_GET['texto']:0;
$text = $textArray[$textID];

header('Content-Type: image/png');

// Creamos la imagen, calculando primero sus dimensiones dependiendo del tamaño del texto
$box_size=dimensions($letra, $text);
$im = imagecreatetruecolor($box_size[0], $box_size[1]);

// Creamos dos colores, y definimos el color que actuará como transparente
$background = imagecolorallocate($im, 255, 255, 255);
$fontColor  = imagecolorallocate($im, $letra['color'][0], $letra['color'][1], $letra['color'][2]);
imagecolortransparent($im, $background);

// Ponemos el fondo de la imagen transparente
imagefilledrectangle($im, 0, 0, $box_size[0], $box_size[1], $background);


// Creamos el texto
imagettftext($im, $letra['size'], 0, 0, $box_size[2], $fontColor, $letra['font'], $text);

// Generamos la salida
imagepng($im);
imagedestroy($im);
?>

Para el ejemplo he utilizado la fuente Linux Libertine Bold cuyo fichero ttf es LinLibertine_Bd.ttf, en realidad podemos utilizar cualquier fuente de la que dispongamos el fichero TTF.
El script que vemos, si lo llamamos desde el navegador con el parámetro textID=1 mostrará una imagen con el texto “de un fragmento de texto” dibujado dentro con el tipo de letra especificado anteriormente.

Ahora hacemos el siguiente archivo html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Ejemplo de texto</title>
    <style text="text/css">
      <!--
     @font-face {
     font-family: "Linux Libertine";
     src: url(LinLibertine_Bd.ttf);
     }
     body{
     background: #eee;
     font: 12pt Linux Libertine,sans;
     }

     .texto{
     vertical-align:bottom;
     margin: 0;
     padding: 0;
     }
    -->
    </style>
    <meta name="author" content="Gaspar Fernández">
  </head>

  <body>
    <h1>Ejemplo de texto</h1>

    Esto es un ejemplo de inserción <img src="texto.php?textID=1" class="texto"/> incluyendo una imagen en PHP
   
    <hr>
    <address>
      <a href="http://totaki.com/poesiabinaria">Gaspar Fernández</a>,
    </address>
  </body>
</html>

Con la parte CSS:

1
2
3
4
@font-face {
     font-family: "Linux Libertine";
     src: url(LinLibertine_Bd.ttf);
     }

Estamos definiendo el fichero que se descargará para visualizar el tipo de letra, así nos aseguramos de que sea el mismo tipo de letra (el de la imagen y el de nuestro texto). El resultado debería ser algo parecido a esto:
textexample

¿ Y tú qué eres ? Identificando clases heredadas en C++

Viernes, 9 de Diciembre de 2011 Gaspar Fernández Sin comentarios

blogvision_estudios_small

En ocasiones, sobre todo cuando estamos con temas de herencia entre clases, tenemos la necesidad de consultar de qué tipo es una instancia. Es decir, tenemos una clase Mascota, y clases Perro y Gato (subclases de la primera). Imaginad que en el código hay muchas funciones, que queremos que sean comunes para todas las mascotas, y por tanto, aceptarán como parámetro una instancia de Mascota, pero en su ejecución, necesitan en un momento saber de qué tipo concreto son (Perro o Gato). ¿ Cómo podemos averiguarlo ?

Lo podemos hallar con dynamic_cast. dynamic_cast forma parte de la RTTI (RunTime Type Information / Información de tipos en tiempo de ejecución), y es capaz de guardar los datos del tipo específico de una instancia y utilizarla durante la ejecución del programa (no sólo en la compilación). Esto no está en la especificación original de C++ (del 1983), sino que fue introducido en 1991, además, en algunos compiladores no viene activado por defecto (según Wikipedia, es lógico, ya que esto puede causar un coste, tanto en memoria, espacio del ejecutable y uso de memoria.)

Una forma para comprobar el tipo que estamos utilizando es la siguiente:

1
2
3
4
5
Perro *a = dynamic_cast<Perro *>(m);
  if (a!=NULL)
    {
      // Acciones con una instancia de Perro llamada a
    }

En este caso estamos comprobando que nuestra Mascota es un Perro.

A continuación vemos el ejemplo completo, en el que tenemos una serie de mascotas y vamos a llevarlas al veterinario, pero para eso, el veterinario determina qué tipo de mascota es y lo deriva al doctor adecuado (es un ejemplo). El ejemplo estará disponible para descarga al final del post.:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <iostream>
#include <string>

using namespace std;


class Mascota
{
public:
  Mascota(string nombre):nombre(nombre)
  {
  }
  virtual ~Mascota()
  {
  }

  string getNombre()
  {
    return nombre;
  }
private:
  string nombre;
};

class Perro : public Mascota
{
public:
  Perro(string nombre):Mascota(nombre)
  {
  }
};

class Gato : public Mascota
{
public:
  Gato(string nombre):Mascota(nombre)
  {
  }
};

class Iguana : public Mascota
{
public:
  Iguana(string nombre):Mascota(nombre)
  {
  }
};

void veterinarioPerro(Perro *p)
{
  cout << "Ponemos una inyección a tu perro " << p->getNombre() << endl;
}

void veterinarioGato(Gato *g)
{
  cout << "Ponemos una inyección a tu gato " << g->getNombre() << endl;
}

void veterinario(Mascota *m)
{
  cout << "Vas a llevar a tu mascota al veterinario..." << endl;

  Perro *a = dynamic_cast<Perro *>(m);
  if (a!=NULL)
    {
      veterinarioPerro(a);
    }
  else if (Gato *a = dynamic_cast<Gato *>(m))
    {
      veterinarioGato(a);
    }
  else
    {
      cout << "No sé qué tipo de mascota tienes" << endl;
    }
}

int main()
{
  Mascota *m;
  Mascota *m2;
  Mascota *i;

  m=new Gato("Mini");
  m2=new Perro("Rufus");
  i= new Iguana("Igor");

  veterinario(m);
  cout << endl;
  veterinario(m2);
  cout << endl;
  veterinario(i);
}

Tenemos que tener en cuenta un requisito primordial para dynamic_cast, y es que la clase sobre la que hagamos la llamada, tiene que ser una clase polimórfica, y será polimórfica cuando contenga al menos un método virtual. Si no sabemos qué método puede ser virtual en la clase, siempre podemos hacer que sea el destructor.

Aunque, por poner un ejemplo de más utilidad, imaginemos que en lugar de tener variables disponibles, cuyo control podemos llevarlo a mano, tenemos un vector de mascotas, y vamos a llevarlas a todas al veterinario. Sólo pego aquí el main(), en la descarga al final de la página estará el ejemplo completo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int main()
{
  vector <Mascota *>mis_mascotas;
  Mascota *m;
 
  m=new Gato("Mini");
  mis_mascotas.push_back(m);
  m=new Perro("Rufus");
  mis_mascotas.push_back(m);
  m=new Iguana("Igor");
  mis_mascotas.push_back(m);
  m=new Perro("Ralph");
  mis_mascotas.push_back(m);
  m=new Gato("Pitu");
  mis_mascotas.push_back(m);
  m=new Perro("Pluto");
  mis_mascotas.push_back(m);
  m=new Gato("Kity");
  mis_mascotas.push_back(m);
  m=new Iguana("Motorcillo");
  mis_mascotas.push_back(m);

  for (vector<Mascota*>::iterator i=mis_mascotas.begin(); i!=mis_mascotas.end(); i++)
    {
      veterinario(*i);
      cout<<endl;
    }
}

Otra forma es utilizar typeid() para averiguar la clase a la que pertenece la instancia. Lo hacemos en la función veterinario(), es necesario incluir la biblioteca :

1
2
3
4
5
6
7
8
9
10
11
12
void veterinario(Mascota *m)
{
  cout << "Vas a llevar a tu mascota al veterinario..." << endl;

   cout << typeid(*m).name() << endl;
  if (typeid(*m)==typeid(Perro))
    veterinarioPerro((Perro*)m);
  else if (typeid(*m)==typeid(Gato))
    veterinarioGato((Gato*)m);
  else
    cout << "No sé qué tipo de mascota tienes" << endl;
}

Algo que también se puede hacer es averiguar el nombre de la clase e imprimirlo con cout, aunque intuitivamente no podremos, porque normalmente el nombre de los tipos no llegan al programa compilado, cuando se compila con soporte RTTI, tenemos la opción de almacenar estos nombres, así que cuidado al poner los nombres de las clases, que se podrá ver en el resultado final.
Para poner por pantalla los nombres de las clases, debemos hacer lo siguiente (pongo sólo la parte de main() correspondiente a recorrer el vector):

1
2
3
4
for (vector<Mascota*>::iterator i=mis_mascotas.begin(); i!=mis_mascotas.end(); i++)
    {
      cout << typeid(**i).name() << endl;
    }

typeid(instancia).name() nos devuelve en un formato textual el nombre de la clase. En el ejemplo hay ** (dos asteriscos) porque el iterador es un puntero a lo que estamos recorriendo, que son punteros a Mascota.
Hay que tener cuidado, porque esta opción no es igual en todos los compiladores, puede que algunos compiladores te devuelvan el nombre tal cual, y otros te devolverán el nombre con algún número o caracteres especiales en el caso de ser punteros, por ejemplo. El más claro ejemplo es GXX cuyo resultado es:

4Gato
5Perro
6Iguana
5Perro
4Gato
5Perro
4Gato
6Iguana

El número que precede los nombres de clase es el número de caracteres que tiene dicho nombre: Gato = 4, Perro = 5, Iguana = 6 . Si probamos con Mascota, saldrá un 7. Y no hay ninguna forma de hacerlo portable a diferentes compiladores por lo que tendremos que hallar soluciones específicas en el caso de que queramos tener el nombre de la clase en formato plano.

En GXX (g++) podemos utilizar lo siguiente, es una función que arregla el nombre para los humanos, no sólo de clases, sino más cosas. Podemos hacer lo siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
string demangle(const char *input)
{
  int status;
  string res(abi::__cxa_demangle(input, 0, 0, &status));
  return res;
}

int main()
{
  vector <Mascota *>mis_mascotas;
  Mascota *m;
 
  m=new Gato("Mini");
  mis_mascotas.push_back(m);
  m=new Perro("Rufus");
  mis_mascotas.push_back(m);
  m=new Iguana("Igor");
  mis_mascotas.push_back(m);
  m=new Perro("Ralph");
  mis_mascotas.push_back(m);
  m=new Gato("Pitu");
  mis_mascotas.push_back(m);
  m=new Perro("Pluto");
  mis_mascotas.push_back(m);
  m=new Gato("Kity");
  mis_mascotas.push_back(m);
  m=new Iguana("Motorcillo");
  mis_mascotas.push_back(m);

  for (vector<Mascota*>::iterator i=mis_mascotas.begin(); i!=mis_mascotas.end(); i++)
    {
      cout << demangle(typeid(**i).name()) << endl;
    }
}

He utilizado una función para simplificar un poco esto, ya que __cxa_demangle(), tiene varios parámetros y nos interesa tener una función que la llame con los parámetros por defecto, para hacer la tarea más directamente. También tenemos que incluir

En G++ podemos utilizar la opción -fno-rtti si no queremos compilar nuestros programas en C++ con este soporte.

Descárgate los ejemplos: dynamic_types.tar.bz2

Utilizar float con sprintf() y derivados en Arduino

Miércoles, 7 de Diciembre de 2011 Gaspar Fernández 2 comentarios

globo-de-aire
El objetivo de la plataforma Arduino es que los programas sean pequeños, ya que tienen que caber en pocos Kbs. Un pequeño problema que tenemos con eso es que muchas bibliotecas no están implementadas completamente, sino que las encontramos en su versión light, en las que implementan sólo las funcionalidades más normales.
Un ejemplo de ello son los comandos printf() y scanf() cuya funcionalidad no cubre los valores de punto flotante (float, double), y que si hacemos el siguiente programa:

1
2
3
4
5
6
7
8
9
10
11
12
void setup()
{
  Serial.begin(9600);
}

void loop()
{
  char buffer[30];
  sprintf (buffer, "Valor: %f\n", 25.4);
  Serial.println(buffer);
  delay(1000);
}

Veremos constantemente:


Valor: ?
Valor: ?
Valor: ?

Y no muestra el valor deseado. Eso sí, ocupa 4.29Kb

La clave para la solución

El tema está en que el compilador no enlaza con las bibliotecas buenas, sino que enlaza con las ligeras. Por lo que tenemos que hacer que éste enlace con las bibliotecas correctas añadiendo el siguiente parámetro al compilador avr-gcc:

-Wl,-u,vfprintf -lprintf_flt

Haciendo el cambio para SConstruct

Si para compilar tus proyectos de Arduino utilizas este método, tienes que modificar una línea en el archivo SConstruct… pongo parte del contexto, cerca de la línea 170, donde pone:

1
2
3
4
5
6
envArduino.Append(BUILDERS = {'Processing':Builder(action = fnProcessing,
        suffix = '.cpp', src_suffix = '.pde')})
envArduino.Append(BUILDERS={'Elf':Builder(action=AVR_BIN_PREFIX+'gcc '+
        '-mmcu=%s -Os -Wl,--gc-sections -o $TARGET $SOURCES -lm'%MCU)})
envArduino.Append(BUILDERS={'Hex':Builder(action=AVR_BIN_PREFIX+'objcopy '+
        '-O ihex -R .eeprom $SOURCES $TARGET')})

Debemos escribir:

1
2
3
4
5
6
envArduino.Append(BUILDERS = {'Processing':Builder(action = fnProcessing,
        suffix = '.cpp', src_suffix = '.pde')})
envArduino.Append(BUILDERS={'Elf':Builder(action=AVR_BIN_PREFIX+'gcc '+
        '-mmcu=%s -Os -Wl,--gc-sections -o $TARGET $SOURCES-Wl, -u,vfprintf -lprintf_flt -lm'%MCU)})
envArduino.Append(BUILDERS={'Hex':Builder(action=AVR_BIN_PREFIX+'objcopy '+
        '-O ihex -R .eeprom $SOURCES $TARGET')})

Con esta modificación, el programa en Arduino (el binario ocupa 5.8Kb) debe dar:

Valor: 25.400000
Valor: 25.400000
Valor: 25.400000

Más información

La IDE de Arduino hace las llamadas al compilador desde su código fuente en lugar de utilizar un Makefile, por ejemplo, por lo que para compilar incluyendo las instrucciones del linkador en el código fuente del IDE. El problema de esto es que no hay una forma fácil de conmutar el linkado con printf_flt y printf_min (que es como se llama la versión light).

Con scanf() también se puede

scanf() también permite la inclusión de la biblioteca que es capaz de leer floats. La instrucción que hay que escribir es:

-Wl,-u,vfscanf -lscanf_flt

Foto: Eric Lim Photography (Flickr) compartido con CC-by a 30/11/2011

Destructores virtuales en C++

Lunes, 5 de Diciembre de 2011 Gaspar Fernández Sin comentarios

papel destruido

Muchas veces, cuando leemos código de otras personas, hemos visto destructores declarados como virtual en algunas clases. Es una de esas cosas que si hacemos siempre no pasa nada, pero si no hacemos nunca, en ocasiones nos llevamos sorpresas; pero muchos desarrolladores optan por poner sólo en los casos justos.

¿ Para qué valían los métodos virtual ?

Eso está en un post anterior, pero básicamente valen para que las subclases tengan un método propio que se llame de la misma forma que el método declarado como virtual. Aunque cuando se trata de los destructores… en cada clase tendremos un destructor que se llama como mi clase, por lo que parece que esto no tiene demasiado sentido. Aunque está relacionado.

¡Demostración de la destrucción!

Aunque esto difiera de cómo construiríamos esta estructura en realidad, vamos a imaginarnos que nuestras clases formarán un edificio. Tendremos la clase Edificio, la clase Planta, y la clase Habitación, donde Planta será subclase de Edificio y Habitación subclase de Planta.

Para construir la habitación, todo va bien, primero creamos el Edificio, luego la Planta y finalmente la Habitación; pero para destruirlo todo, depende del tipo de objeto que creemos y, en ocasiones, como con las Mascotas, nos interesará declarar los objetos con la clase padre (o superclase) y a la hora de construirlo hacerlo con la clase hija (o subclase) correspondiente.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>

using namespace std;

class Edificio
{
public:
  Edificio()
  {
    cout << "Se construye el edificio" << endl;
  }

  virtual ~Edificio()
  {
    cout << "Sacamos las bombillas del ascensor" << endl;
    cout << "Se destruye el edificio" << endl;
  }

private:
};

class Planta : public Edificio
{
public:
  Planta()
  {
    cout << "Se construye la planta" << endl;
  }

  ~Planta()
  {
    cout << "Sacamos los muebles de los pasillos" << endl;
    cout << "Se destruye la planta" << endl;
  }

private:
};

class Habitacion : public Planta
{
public:
  Habitacion()
  {
    cout << "Se construye la habitación" << endl;
  }

  ~Habitacion()
  {
    cout << "Saco las cosas de la habitación "<<endl;
    cout << "Se destruye la habitación" << endl;
  }

private:
};


int main()
{

  Edificio *h = new Habitacion();

  delete h;
  return 0;
}

La respuesta esperada es la siguiente:

Se construye el edificio
Se construye la planta
Se construye la habitación
Saco las cosas de la habitación
Se destruye la habitación
Sacamos los muebles de los pasillos
Se destruye la planta
Sacamos las bombillas del ascensor
Se destruye el edificio

Pero si quitamos el virtual, la respuesta será la siguiente:

Se construye el edificio
Se construye la planta
Se construye la habitación
Sacamos las bombillas del ascensor
Se destruye el edificio

Muchas veces, los destructores no tendrán nada, cuando no hay nada que hacer, pero imaginad que el Edificio reserva memoria para almacenar ciertos datos, la Planta, reserva también memoria para algo, y la Habitación por su parte también, en ese caso sería necesario pasar por todos los destructores para que cada uno libere su región de información. Así evitamos memory leaks.

En el caso de poner virtual en todos los destructores, no pasa nada.

Más ejemplos

Otro ejemplo puede ser cuando queremos crear un árbol, y los nodos pueden ser de dos tipos (nodo con ramificaciones, o sin ellas):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Nodo
{
public:
  Nodo(string n)
  {
    nombre=n;
    cout << "Construyo el nodo "<<nombre<<endl;
  }

  virtual ~Nodo()
  {
    cout<< "Destruyo el nodo "<<nombre<<endl;
  }
 
  virtual void print(int espacios)
  {
    cout << "Nodo: "<<nombre<<endl;
  }

public:
  string nombre;
};

class Rama: public Nodo
{
public:
  Rama(string n):Nodo(n)
  {  
    cout << "Construyo la rama "<<n<<endl;
  }

  ~Rama()
  {
    while (!nodos.empty())
      {
    delete nodos.back();
    nodos.pop_back();
      }
    cout <<"Destruyo la rama " << nombre << endl;
  }

  void addNodo(Nodo *n)
  {
    nodos.push_back(n);
  }

  void print()
  {
    cout << "Rama: "<<nombre<<endl;
    for (vector<Nodo*>::iterator it=nodos.begin(); it!=nodos.end(); ++it)
      {
    (*it)->print();
      }
  }

public:
  vector<Nodo*> nodos;
};

int main()
{
  Nodo *arbol= new Rama("main");

  Nodo *nodo= new Nodo("Abuelo sin nietos");
  static_cast<Rama*>(arbol)->addNodo(nodo);
  nodo= new Rama("Abuelo 2");
  static_cast<Rama*>(arbol)->addNodo(nodo);
  Nodo *nodo2= new Nodo("Hijo 1");
  static_cast<Rama*>(nodo)->addNodo(nodo2);
  nodo2= new Rama("Padre 1");
  static_cast<Rama*>(nodo)->addNodo(nodo2);
  Nodo *nodo3= new Nodo("Hijo 3");
  static_cast<Rama*>(nodo2)->addNodo(nodo3);
  nodo3= new Nodo("Hijo 4");
  static_cast<Rama*>(nodo2)->addNodo(nodo3);
  nodo3= new Nodo("Hijo 5");
  static_cast<Rama*>(nodo2)->addNodo(nodo3);
  nodo3= new Nodo("Hijo 6");
  static_cast<Rama*>(nodo2)->addNodo(nodo3);
  nodo2= new Nodo("Hijo 2");
  static_cast<Rama*>(nodo)->addNodo(nodo2);

  nodo= new Rama("Abuelo 3");
  static_cast<Rama*>(arbol)->addNodo(nodo);
  nodo2= new Nodo("Hijo 7");
  static_cast<Rama*>(nodo)->addNodo(nodo2);
  nodo2= new Rama("Padre 2");
  static_cast<Rama*>(nodo)->addNodo(nodo2);
  nodo3= new Nodo("Hijo 8");
  static_cast<Rama*>(nodo2)->addNodo(nodo3);

  cout << endl;
  arbol->print();
  cout << endl << endl;

  delete arbol;
}

En este caso, si quitamos el virtual al destructor, podemos ver que sólo se destruye el nodo main, quedándose todo lo demás en memoria.

Otros ejemplos de este uso pueden ser:

  • Sistemas que mantentan archivos, sockets, pipes abiertos desde la superclase (todo tiene que ser cerrado)
  • Instancias cuyas superclases tengan cadenas char* , tenemos que liberar todas las cadenas
  • Estoy abierto a sugerencias en los comentarios

Foto:  <a href=”http://www.flickr.com/photos/randomurl/445352972/”>WagsomeDog</a> (Flickr) Compartido con CC-by a 28/Nov/2011

Visita otras webs de la red