Archivo

Entradas Etiquetadas ‘macros’

Número variable de argumentos en una función de C

Lunes, 24 de Enero de 2011 Gaspar Fernández 3 comentarios

Si habéis programado en C, seguramente la primera función que aprendisteis a utilizar era printf() ; y en todo el tiempo que pasasteis estudiando C no encontrasteis una función similar. Y es que esta función puede tener un número indeterminado de parámetros y es capaz de analizarlos todos.

Y es que quitando printf(), scanf() y derivadas de estas, la necesidad de una función con múltiples parámetros casi no se nos presenta. Aunque tal vez, alguna vez necesitemos crear una función con parámetros opcionales, es decir, si están presentes se tomarán y si no, se tomará un valor por defecto.

Esto lo podemos hacer con la biblioteca stdarg.h la cual nos proporciona macros para hacer esto posible.

Echemos un vistazo al siguiente 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
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

void poneargs(int nargs, ...)
{
  va_list ap;
  int i;
  int num;

  va_start(ap, nargs);
  for (i =0; i<nargs; i++)
    {
      num=va_arg(ap, int);
      printf("Argumento %d = %d\n", i, num);
    }
  va_end(ap);
  printf("\n");
}

int main(int argc, char *argv[])
{
  poneargs(5, 6, 77, 445, 34, 23, 12, 43, 32);
  poneargs(2, 23, 94);
  poneargs(0);

  return EXIT_SUCCESS;
}

Aquí vemos cómo la función poneargs() nos coloca los valores de los argumentos que le hemos pasado, siendo el primero de ellos el número total de argumentos que tenemos.
Con esto, hay que dejar claro que no tenemos forma de contar los argumentos que pasamos (igual que printf, podíamos pasar tantos argumentos como %[x] tuviéramos en la cadena de formato). Así que las técnicas para llegar al límite de los comandos son:

  • Poner un argumento indicando el número de argumentos a colocar.
  • Poner un argumento con un valor fijo al final que nos indique que hemos llegado al último parámetro.
  • Poner códigos con los argumentos que tenemos que leer. Idea de printf()

Lo que sí que tenemos que tener en cuenta es que, como estamos acostumbrados cada argumento tiene un tipo fijo, y se lo tenemos que decir a va_arg() en su segundo argumento; y va_arg() nos va a devolver una variable del mismo tipo que le dijimos (éste es el valor del argumento).

En el siguiente ejemplo creamos una función new_cliente() que puede tener hasta cuatro argumentos de tipo cadena de caracteres. De hecho son opcionales. Cuando creamos un cliente en un programa real puede que no tengamos todos sus datos y puede que algunos sean opcionales:

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
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

void new_cliente(int nargs, ...)
{
  va_list ap;

  char *valor;

  va_start(ap, nargs);

  switch (nargs)
    {
    case 4:
      valor=va_arg(ap, char*);
      printf("Twitter: %s\n", valor);      
    case 3:
      valor=va_arg(ap, char*);
      printf("URL: %s\n", valor);      
    case 2:
      valor=va_arg(ap, char*);
      printf("Blog: %s\n", valor);      
    case 1:
      valor=va_arg(ap, char*);
      printf("Nombre: %s\n", valor);
      break;
    default:
      printf("Número de parámetros incorrecto\n");
    }
 
  va_end(ap);
  printf("\n");
}

int main(int argc, char *argv[])
{
  printf("Un parámetro:\n");
  new_cliente(1, "Gaspy");

  printf("Dos parámetros:\n");
  new_cliente(2, "Gaspy", "Poesía Binaria");

  printf("Tres parámetros:\n");
  new_cliente(3, "Gaspy", "Poesía Binaria", "http://totaki.com/poesiabinaria/");

  printf("Cuatro parámetros:\n");
  new_cliente(4, "Gaspy", "Poesía Binaria", "http://totaki.com/poesiabinaria/", "http://twitter.com/blakeyed");

  return EXIT_SUCCESS;
}

Esto también podría valernos como una pequeña simulación de sobrecarga de funciones (estilo C++) aunque en C no cumpliría la tarea de hacer más legible el código ya que en la misma función tendríamos que incluir las opciones de los argumentos.

Creando macros de escritorio I: xmacroplay

Martes, 17 de Agosto de 2010 Gaspar Fernández Sin comentarios

Voy a hablar de dos programas para crear macros, aunque hay más, claro que sí. Los dos programas están basados en la grabación de acciones del servidor X y la simulación de movimientos del ratón, clicks y acciones de teclado.

En principio xmacroplay; lo podemos ejecutar así:

$ xmacrorec2 > mi_macro

donde mi_macro es el archivo donde vamos a grabar; este archivo, si vemos su contenido tendrá los comandos en un lenguaje muy sencillo e inteligible, lo que nos permite crear macros directamente programándolos, no grabándolos.
Lo primero que debemos hacer tras ejecutar xmacrorec2 es pulsar una tecla, ésta tecla será la que deberemos pulsar para detener la grabación.

Para ejecutar las macros, lo podemos hacer de la siguiente forma:

$ xmacroplay :0 < mi_macro

donde :0 es la pantalla, el DISPLAY y mi_macro es el nombre de la macro.

Aunque, al menos en la versión 20000911, la última descargable desde sf.net (del año 2001, un poco antigua), se lleva un poco mal con los arrastres de ratón; vamos para hacer macros con clicks bien, pero cuando arrastramos algo, a veces no se reproducen bien las opciones. Se debe a un bug en xmacroplay.cpp, en las siguientes líneas dentro de la función eventLoop(), hay que añadir el código que tiene comentario

1
2
3
4
5
6
7
8
9
10
11
12
13
14
else if (!strcasecmp("ButtonPress",ev))
        {
          cin >> b;
          cout << "ButtonPress: " << b << endl;
          XTestFakeButtonEvent ( RemoteDpy, b, True, Delay );
          XFlush ( RemoteDpy ); /* AÑADIR */
}
        else if (!strcasecmp("ButtonRelease",ev))
        {
          cin >> b;
          cout << "ButtonRelease: " << b << endl;
          XTestFakeButtonEvent ( RemoteDpy, b, False, Delay );
          XFlush ( RemoteDpy );/* AÑADIR */
        }

Con esta modificación todo debe ir bien, podemos, por ejemplo programar ciertas teclas en nuestro ordenador para automáticamente grabar y reproducir macros de teclado y ratón.

Aunque como dije antes, siempre podemos programar nosotros las macros a mano, o modificar lo que hemos grabado para un mejor comportamiento con los siguientes comandos:

  • MotionNotify X Y : Mueve el rató a un punto X,Y de la pantalla
  • ButtonPress N : Pulsa el botón N del ratón (1=izquierdo, 2=rerecho, 3=centro, 4,5=rueda del ratón en algunos sistemas, etc)
  • Delay N : Retrasa el script N segundos, deteniendo su ejecución un tiempo
  • ButtonRelease N : Suelta el botón N del ratón
  • KeyCodePress C : Pulsa la tecla con el código C (podemos usar xev para ver los códigos de teclas)
  • KeyCodeRelease C : Suelta la tecla con el código C
  • KeyStrPress S : Pulsa la tecla llamada S (Es el nombre de forma de cadena de caracteres, por ejemplo “Escape”, “F5″, “Control_L”, etc; podemos utilizar xev para ver los nombres también)
  • KeyStrRelease S : Suelta la tecla llamada S

Depurando…

Sábado, 30 de Mayo de 2009 blakeyed Sin comentarios

A veces es muy útil saber el contenido de una variable en mitad de ejecución del programa, pero cuando queremos compilar la versión definitiva, eliminar todas las trazas de depuración de una vez, aunque cuando estemos desarrollando, es interesante volverlas a tener.

Podemos hacerlo de forma sencilla con algunas macros de preprocesador de C; podemos poner un pequeño fragmento de código al empezar cada programa, y lo mejor de todo es que la versión definitiva seguirá ocupando lo mismo, ya que si desactivamos la depuración es como si en el código final no se escribieran las trazas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include

#define depuracion

#ifdef depuracion
#define depura_int(variable) printf("%s: Linea: %d (Variable \"%s\" vale: %d)\n", __FILE__, __LINE__, #variable, variable);
#define depura_string(variable) printf("%s: Linea: %d (Variable \"%s\" vale: \"%s\")\n", __FILE__, __LINE__, #variable, variable);
#else
#define depura_int(variable)
#define depura_string(variable)
#endif
int main()
{

int numero=99;
char strtest[20]="CADENA DE PRUEBA";

depura_int(numero)
depura_string(strtest)

return 0;
}

Observad lo que hacen las macros depura_int y depura_string, nos darán el número de línea y el archivo donde están, así como el nombre de la variable y el valor que tiene en ese momento.

Cuando no queramos depurar comentamos la línea que contiene #define depuracion y listo.

Visita otras webs de la red