Java reflection y los tipos genéricos (y II)

Publicado por laparca el 15 de diciembre de 2007 — Publicado en Desarrollo

Después de mucho pegarme con esto conseguí que me funcionase. Debo decir que no me gusta nada el hecho de no poder saber el tipo con el que se ha creado un genérico.

Para que se comprenda qué es lo que he hecho y para qué lo necesitba tengo que decir que en la beca tengo una aplicación realizada en C++ que tiene impletentado su propio tipo de seriado (La palabra en español para serializar es seriado. Como en C++ no hay mecanismos de introspección (como java reflection, system.reflection, etc.), el modo de hacerlo es un poco más feo.

Seriado en C++

El mecanismo de seriado que he utilizado en C++ ha consistido en crear una clase abstracta con dos métodos abstractos, uno se seria los datos a un canal (puede ser un fichero, la red, MPI, etc.) y otro que realiza la operación inversa:

/**
* Clase abstracta que define los dos métodos necesarios para poder
* seriar cualquier clase por un canal de almacenamiento o de
* comunicaciones.
*/
class Serializable {
   /**
   * Seria el objeto a través de flujo os.
   */
   void writeObject(OutputStream &os) = 0;
   /**
   * Lee los datos del objeto desde el flujo is.
   */
   void readObject(InputStream &is) = 0;
};

Es importante hacer notar que cualquier mecanismo de seriado exige que la clase seriable tenga un constructor vacío, para permitir regenerar todos los datos a partir del mecanismo de seriado.

El problema del mecanismo anterior es que el programador debe saber qué es lo que se va a transmitir y, por tanto, sabe qué tipo de dato le va a llegar. Esto no sucede con el sistema de seriado de java, que transmite el tipo de dato a través del canal.

Cuando quise ponerme a realizar esto mismo en java pensé que si ya tenía un mecanismo que me decía qué era lo que tenía que leer, no sería necesario implementar readObject y writeObject en cada clase, ya que puedo consultar cuales son los atributos de la clase. Pero el problema sobrevino cuando llegaron los genéricos.

Los genéricos en Java

Los genéricos en Java funcionan igual que las plantillas de C++, pero con una salvedad. En C++ las plantillas se transforman en una implementación distinta para cada tipo con el que se generase la plantilla, en Java sólo existe una implementación con el tipo más simple de la plantilla (si no se especifica nada, es Object).

Lo anterior significa que si yo tengo un vector de simulaciones, en C++ se creará una implemtanción específica de vector que sólo trabaja con simulaciones (y ningún otro tipo). En Java no sucede lo mismo. En Java sólo hay una implementación que será de tipo Object (todos heredan de Object) pero el control de que sea una simulación se realiza en tiempo de compilación. Esto supone que cuando se ejecute la aplicación ya no conoce si es correcto el dato que se le está pasando, ha sido el compilador el que hico la comprobación. Esto supone que no puedo regenerar de la forma deseada el vector.

Solución actual

En estos momentos he conseguido que funcione. Lo primero que hice fue ponerlo todo igual que lo tenía en C++, pero en Java. De este modo, todas las clases seriables deberán tener un readObject y un writeObject. Y para solucionar el tema de los genéricos hice lo siguiente:

Cree el tipo vector genérico que es seriable por este mecanismo. Para garantizar que funcionará he formazado a la clase a ser abstracta. Con lo que comenté en la anterior entrada sobre cómo obtener el tipo genérico, que dije que no funcionaba, pues sí funciona, pero no como yo esperaba. Para que funcione hay que heredar de la clase genérica de este modo:

abstract class VectorSeriable {
   void readObject(InputStream is) {
   /* Leo utilizando el tipo del generíco como se dijo en la anterior entrada */
   }
   void writeObject(OutputStream os) {
   /* Grabo los objetos */
   }
}
 
/**
* Al heredar de este modo, estamos creando una clase VectorSimulaciones
* que es idéntica a VectorSerializable, pero que ya conoce cual es el tipo
* de su genérico.
*/
class VectorSimulaciones extends VectorSeriable {}

Como se puede apreciar, esto es igual que utilizat typedef en C++. De este modo he conseguido que funcione. Ahora debería ser posible eliminar los metodos readObject y writeObject para automatizar todo ya que es bastante feo tenerlos en cada clase.

Java reflection y los tipos genéricos

Publicado por laparca el 13 de diciembre de 2007 — Publicado en Desarrollo, Opinión

Por temas del trabajo llevo unos días pegandome con java reflection y los tipos de genéricos. Debo reconocer que las dos cosas están muy bien, pero entre ellas no se llevan bien. Para facilitar un poco la cosa voy a poner un ejemplo de un tipo genérico:

1
2
3
4
5
6
7
8
9
10
11
12
public class Lista<tipo> {
   /* para evitar problemas con wordpress: </tipo> */
   tipo leeElementoEnPosicion(int posicion) {
      tipo rtn = null;
      /* Buscamos el elemento y lo devolvemos */
      return rtn;
   }
 
   void agregarElemento(TipoElemento elm) {
      /* Insertamos el elemento */
   }
}

El código anterior no hace nada, pero nos permite ver que creamos una clase Lista que es genérica. El hecho de que sea genérica significa que podemos decir que la lista podrá almacenar cualquier tipo de dato y que se lo indicaremo al crearla (antiguamente en java sólo se podía almacenar un tipo de dato: Object).

Mi problema empieza cuando yo necesito desde una instancia de la clase Lista cuál es el tipo de dato que almacena. Inicialmente sería tan sencillo como hace lo siguiente:

public Class leerTipoElementoLista() {
   return tipo.class;
}

El problema es que, según parece, en la práctica java no utiliza genéricos, sino que sólo los comprueba en tiempo de compilación. Esto significa que una vez compilado el programa, tipo deja de ser un tipo conocido y pasa a ser algo completamente indeterminado, o dicho de otro modo, se convierte en un Object. A causa de esto, la sentencia anterior (tipo.class) genera un error de compilación.

Buscando por el gran Google encontré una referencia a Hibernate donde comentaban un modo de hacerlo:

(Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

Lo he estado probando y lo único que puedo decir es que falla en la ejecución. La sentencia anterior genera una CastException, así que nada.

Y yo necesito poder saber el tipo del genérico :’(

Jugando con Apache Tomcat

Publicado por laparca el 29 de octubre de 2007 — Publicado en Desarrollo, Personal

Hace poco me dio por instalarme tomcat en el ordenador. ¿Por qué? Pues porque me parece que desarrollar webs con java es más seguro que hacerlo con PHP… lo que no tiene porqué ser cierto, pero me gusta más la arquitectura de java (aunque no su rendimiento).

El caso es que me animé más con las clases de Servidores de Información, donde trasteamos también con tomcat. Ahora me rondan varias cosas por la cabeza para realizar, entre ellas un sistema para jugar a rol, que no me gusta nada, pero nada, hacerlo por foro, no están preparados para estas cosas. Además, hay que saber un poco de todo ¿no?

A ver si me pongo y me miro la charle de Mariscal sobre AJAX, que también estoy muy interesado en aplicarlo ^^

El resultado de la CUPCAM

Publicado por laparca el 28 de octubre de 2007 — Publicado en Desarrollo, Personal

Y al final todo llega y nosotros no hicimos buen papel :( Fuimos un total de 19 grupos a participar y nosotros quedamos en el puesto 11 :’(. De todos modos volveremos el año próximo y venceremos.

De momento decir que me quedé pillado con un problema y analizando la solución era igual que la mia, solo que yo no me di cuenta de una cosa y hacía que el tiempo de ejecución de m algoritmo tendiese a infinito :(

La solución del problema es la siguiente. Muchos reconocerán en ella el algoritmo de la mochila o de la estantería. El caso es que yo nunca había oído hablar de él.

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
#include stdlib.h
#include stdio.h
 
#define MAX 1001
 
int T[MAX] = { -1 };
 
int max(int a, int b)
{
   return a>=b ? a : b;
}
 
/**
* Pasamos cuanto espacio tenemos y el ultimo libro de la lista a comprobar.
* Devuelve el maximo espacio que puede ocupar.
*/
int A(int espacio, int libros)
{
   int a, b;
 
   /* Si el espacio libre es 0 o no hay libros que colocar, el espacio
   * que ocupamos es 0 */
   if( !espacio || !libros) return 0;
 
   /* El libro ocupa mas que el espacio que tengo libre,
   * por lo que sigo sin tenerlo en cuenta */
   if( T[libros] > espacio ) return A(espacio, libros-1);
 
   /* Si nuestro libro cabe, devolvemos el maximo entre poner
   * el libro actual e intentarlo con el resto o directamente
   * intentarlo con el resto sin tener en cuenta el libro
   * actual */
   return max( A(espacio, libros-1), T[libros]+A(espacio-T[libros], libros-1) );
}
 
int main(void)
{
   int i;
   int espacio;
   int num_libros;
 
   while(1) {
      scanf("%d %d", &espacio, &num_libros);
      if( espacio == 0 && num_libros == 0 ) break;
 
      for(i = 1; i &lt;= num_libros; i++ ) {
         scanf("%d", &(T[i]));
      }
      printf("%d\n", espacio-A(espacio, num_libros));
   }
   return 0;
}

La solución que yo hice era más como la que sigue:

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
#include stdlib.h
#include stdio.h
 
#define MAX 1001
 
int T[MAX] = { -1 };
 
int max(int a, int b)
{
   return a>=b ? a : b;
}
 
/**
* Pasamos la lista de libros, el espacio que queda libre y el total de libros en la lista.
*/
int A(int *tamanos, int espacio, int libros)
{
   int c;
   int resultado = espacio;
   int temporal;
 
   if( !libros ) return espacio;
 
   /* Calculamos el espacio libre teniendo en cuenta que usamos el libro actual */
   for(c=1; c < libros; c++) {
      temporal = A(tamanos[c], espacio-tamanos[0], libros-1);
      if(temporal<resultado ) resultado = temporal;    
   }
   /* Calculamos el espacio libre sin tener en cuenta que usamos el libro actual */
   for(c=1; c < libros; c++) {
      temporal = A(tamanos[c], espacio, libros-1);
      if(temporal < resultado ) resultado = temporal;
   }
   return resultado;
}
 
int main(void)
{
   int i;
   int espacio;
   int num_libros;
 
   while(1) {
      scanf("%d %d", &espacio, &num_libros);
      if( espacio == 0 && num_libros == 0 ) break;
 
      for(i = 1; i &lt;= num_libros; i++ ) {
         scanf("%d", &a(T[i]));
      }
      printf("%d\n", A(espacio, num_libros));
   }
   return 0;
}

Ahora no recuerdo muy bien si era esactamente así, pero sí se que funcionaba. Como podéis comprobar, nuestro gran fallo (bueno, mi gran fallo) fue que pasaba muchas veces por las mismas convinaciones :@ El bucle ese fue mortal y no nos dismo cuenta ninguno que se podía quitar :’(

La próxima vez lo haremos mejor.

Punteros inteligentes en C++ (smart pointers) 3

Publicado por laparca el 08 de agosto de 2007 — Publicado en Desarrollo

En el artículo anterior nos quedamos con que hacía falta añadir nuevas funcionalidades que nos permitan mejorar las características del invento. Así que vamos a empezar poco a poco.

Para empezar, vamos a suponer que queremos poder asignar un puntero con el operador =. Para ellos hacemos lo siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template<typename _t> class PtrSimple2 {
[..]
   PtrSimple2<_t>&
   // </_t>
   operator=( const PtrSimple2<_t>& ptr ) {
      // Hay que decrementar el puntero viejo, y en caso necesario
      // liberar la memoria
      (*_count) = (*_count) - 1;
      if( *_count == 0 ) {
         delete _count;
         delete _ptr;
      }
      _count = ptr._count;
      _ptr = ptr._ptr;
      (*_count) = (*_count) + 1;
 
      return *this;
   }
[..]
};
//</_t></typename>

Con esto ya tendríamos la asignación, pero sigue existiendo un problema de base: ¿qué pasa con la herencia? Por ejemplo, tenemos una clase A y otra B que hereda de A. Si yo creo que puntero de tipo A y otro de tipo B, me gustaría poder pasar sin problemas de uno a otro y que lo reconozca (¿sería lo lógico, no?). Pues tal y como están las cosas ahora, no lo haría (haced la prueba si no me creéis). Para solventar esto, añadimos los siguientes fragmentos de 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
template<typename _t> class PtrSimple2 {
//</typename>
[..]
public:
   // Para poder leer el puntero sin problemas
   _t* get_ptr() {
      return _ptr;
   }
 
   // Nuevo constructor
   // Estoy simplificando algunas cosas, es posible que no se pueda acceder
   // directamente a los miembros del otro puntero
   template<typename _u> PtrSimple2( const PtrSimple2<_u>& ptr ) {
      _count = ptr._count;
      _ptr = dynamic_cast<_t *>( ptr._ptr );
      (*count) = (*count) + 1;
   } // </_t></_u></typename>
 
   // Con el operador = hay que hacer lo mismo
   template<typename _u> PtrSimple2<_t>& operator=( const PtrSimple2<_u>& ptr ) {
      // Hay que decrementar el puntero viejo, y en caso necesario
      // liberar la memoria
      (*_count) = (*_count) - 1;
      if( *_count == 0 ) {
         delete _count;
         delete _ptr;
      }
      _count = ptr._count;
      _ptr = dynamic_cast<_t *>( ptr._ptr );
      (*_count) = (*_count) + 1;
 
      return *this;
   }
[..]
};
//</_t></_u></_t></typename>

¿Con esto ya estaría todo resuelto, no? Podemos controlar la herencia, los punteros, etc. Eso sí, siempre y para todo hay que utilizar este tipo de dato. Entonces, ¿puede haber casos en los que esto no funcione? La respuesta es sí. Imaginemos que tenemos que pasar a un método el la dirección a la que apuntamos (lo que hacemos con get_ptr) y luego podemos, desde otro lado, tomar dicha dirección en otro puntero.

Esta situación tan incomoda se pude solucionar con una política aún más agresiva de control de punteros. Para esto, debemos separar la parte de los contadores de punteros de la del propio control de los punteros. Vamos a ver otra clase para hacer punteros inteligentes que sigue una política un poco más agresiva:

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
104
105
106
107
108
// ESTO VA EN LA CABECERA
 
void pointer_inc_ref( void *ptr );
bool pointer_dec_ref( void *ptr );
 
/**
 * Creates a smart pointer type that can free the memory used
 * automatically when it isn't necesary.
 * This pointer type can be very slow.
 */
template<typename _t> class pointer_to {
	Type *ptr;
 
public:
	pointer_to( _t *pointer ) {
		ptr = pointer;
		pointer_inc_ref(ptr);
	}
 
	pointer_to( const pointer_to<_t>& pointer ) {
		ptr = pointer.ptr;
		pointer_inc_ref(ptr);
	}
 
	template<typename _u> pointer_to( const pointer_to<_u>& pointer ) {
		ptr = dynamic_cast<_t *>( pointer.get() );
		pointer_inc_ref( ptr );
	}
 
	~pointer_to() {
		if( pointer_dec_ref( ptr ) )
			delete ptr;
	}
 
	_t* get() const {
		return ptr;
	}
 
	_t* operator->() const {
		return ptr;
	}
 
	_t& operator*() const {
		return *ptr;
	}
 
	// </_t>
	pointer_to<_t>& // </_t>
	operator=( const pointer_to<_t>& pointer ) {
		// </_t>
		if( pointer_dec_ref(ptr) )
			delete ptr;
		ptr = pointer.ptr;
		pointer_inc_ref(ptr);
	}
 
	pointer_to<_t>& operator=( _t*pointer ) {
		if( pointer_dec_ref(ptr) )
			delete ptr;
		ptr = pointer;
		pointer_inc_ref(ptr);
		return *this;
	}
 
	template<typename _u> pointer_to<_t>& operator=( const pointer_to<_u>& pointer ) {
		if( pointer_dec_ref(ptr) )
			delete ptr;
		ptr = dynamic_cast<_t>( pointer.ptr );
		pointer_inc_ref(ptr);
		return *this;
	}
}; // class pointer_to
// </_t></_u></_t></typename></_t></_u></typename></_t></typename>
 
 
// ESTO VA EN EL CODIGO
 
typedef map<void *, int> pointer_map_t;
typedef pointer_map_t::iterator pointer_map_iterator;
//</void>
static map<void *, int> pointer_map;
 
void pointer_inc_ref( void *ptr )
{
	if( ptr != 0 ) {
		pointer_map_iterator it;
		it = pointer_map.find( ptr );
		if( it == pointer_map.end() ) {
			pointer_map.insert(pointer_map_t::value_type(ptr, 1));
		}
		else {
			pointer_map[ptr] ++;
		}
	}
}
 
bool pointer_dec_ref( void *ptr )
{
	if( ptr != 0 ) {
		pointer_map[ptr] --;
		if( pointer_map[ptr] == 0 ) {
			pointer_map.erase(ptr);
			return true;
		}
	}
	return false;
}
//</void>

En este caso, estamos haciendo una política muy agresiva, pero ya se controlan casi todos los casos (aún pueden existir problemas, para los que la política a utilizar sería que el puntero inteligente no libere la memoria [por lo que sería una perdida utilizarlo]). Además, existen problemas de dobles enlaces que podrían provocar que la memoria nunca se libere.

Seguro que hay mejores modos de hacerlo, pero este es, el que a partir de algunas cosillas que he leído por ahí he conseguido implementar. Si hay alguna pregunta (y habéis sido capaces de leer hasta aquí) no os cortéis en hacerla.

Punteros inteligentes en C++ (smart pointers) 2

Publicado por laparca el 05 de agosto de 2007 — Publicado en Desarrollo

En el artículo anterior veíamos como crear una clase simple de puntero inteligente, pero no se mostró ningún ejemplo de su uso. Por este motivo, a continuación pasamos a poner un ejemplo y a ver las deficiencias de dicha clase:

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
#include <iostream>
using std::cout;
using std::endl;
 
// Aqui iría nuestra clase de puntero simple
// template...
 
// Clase de ejemplo para mostrar el uso de los punteros inteligentes
class UnaClase {
   UnaClase() {
      cout < < "Soy UnaClase y me estan creando" << endl;
   }
   ~UnaClase() {
      cout << "Soy UnaClase y me están destruyendo" << endl;
   }
   void hacerAlgo() {
      cout << "Soy UnaClase y estoy haciendo algo útil" << endl;
   }
};
 
int main( void ) {
   PtrSimple<UnaClase> ptrUnaClase = new UnaClase();
 
   // Llamamos al método hacerAlgo de UnaClase a través del puntero inteligente.
   ptrUnaClase->hacerAlgo();
 
   // En este punto, C++ destruye la instancia a PtrSimple, eliminando automáticamente
   // la memoria ocupada por este.
}
 
// Para evitar fallos en wordpress:
// 
// </iostream>

Si hacemos la prueba, veremos como el destructor de UnaClase se llama automáticamente al término del programa. Con esto hemos conseguido nuestro primer objetivo, no tener que liberar nosotros mismos la memoria, pero ahora surgen otros problemas: ¿cómo utilizar el puntero en más partes de la aplicación que no sean el propio método?

Antes de continuar vamos a ver una cosita de C++ que nos ayudará mucho: el paso de parametros por referencia. Esto nos permite pasar a un método una referencia a un objeto y si lo modificamos estaremos modificando el objeto original. ¿Cómo se hace esto? pues muy fácil:

1
2
3
4
// Utilizamos nuestra clase UnaClase
void metodo( UnaClase& unaClase ) {
   unaClase.hacerAlgo();
}

Como vemos, al poner los argumentos del método, justo después del tipo se ha añadido un símbolo &. Éste es el que indica que es por referencia. Si no se pone nada sería por valor y si se pusiera un * sería una dirección. De este modo, ya podríamos pasar sin peligro a un método un puntero inteligente:

1
2
3
4
5
6
// Utilizamos nuestra clase UnaClase
void metodo( PtrSimple<unaclase>& unaClase ) {
   unaClase->hacerAlgo();
}
// Para evitar problemas
// </unaclase>

Ahora imaginemos el siguiente caso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <vector>
using std::vector;
// Nuestras definiciones
 
// El codigo
typedef PtrSimple<unaclase> PtrUnaClase;
// Ojo que el worpress cambia algunas cosas (como mayúsculas a minúsculas :@)
vector<ptrunaclase> vectorPunteros;
 
// Utilizamos nuestra clase UnaClase
void metodo( PtrUnaClase& unaClase ) {
   vectorPunteros.add(unaClase);
}
 
// Para evitar problemas
// </ptrunaclase></unaclase></vector>

Estamos haciendo uso de STL para el vector. Este código no funcionará, ya que la clase vector hace uso del constructor de copia de PtrSimple, que no lo hemos definido, por lo que utilizaría el contructor de copia por defecto. Esto derivaría en que en un momento dado el puntero podría liberarse mientras en el vector seguiría existiendo un puntero al mismo, lo que supondrá un fallo de segmentación. Para solventar esto, hay que hacer uso de contadores de uso de punteros, que nos permitirán saber cuantas copias hay del mismo para liberarlo en caso necesario:

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
template<typename _t> class PtrSimple2 {
private:
   _t *_ptr;
   int *_count; // Copias del puntero
 
public:
   // Creamos el puntero a partir de una zona de memoria ya existente
   PtrSimple2( _t* ptr = NULL ) {
      _ptr = ptr;
      if( _ptr == NULL )
         _count = NULL;
      else {
         _count = new int;
         *_count = 0;
      }
   }
 
   // Constructor de copia: tomamos un puntero ya existente e incrementamos
   // el contador de usos.
   PtrSimple2( const PtrSimple2<_t>& ptr ) {
      _ptr = ptr._ptr;
      _count = ptr._count;
      if( _count != NULL )
         (*count) = (*count) + 1;
   }
 
   ~PtrSimple2() {
      if( _count != NULL ) {
         (*count) = (*count) - 1;
         // Si nadie apunta a esta zona de memoria,
         // se libera la memoria
         if( (*count) == 0 )
            delete _ptr;
      }
   }
 
   _t* operator->() const {
      return _ptr;
   }
 
   _t& operator*() const {
      return *_ptr;
   }
};
 
// Para evitar fallos en wordpress
// </_t></typename>

Con la clase anterior, ya no debería haber problemas con el tema de STL ni de que hayan copias por ahí… ¿o no? Siguen existiendo casos conflictivos, por ejemplo, tener que pasar sólo la dirección (no el puntero) y luego desde otra parte del código tomar dicho puntero a una clase. Con este caso, perderiamos los contadores de uso que teníamos, por lo que perdemos el control.

A parte de lo anterior, también es constatable que nos faltan funcionalidades: y si quiero comparar dos puntero, y si quiero asignarle una dirección nueva al puntero cuando este ya tenía otra, etc. Esta y otras dudas, para el siguiente artículo.

Punteros inteligentes en C++ (smart pointers)

Publicado por laparca el 04 de agosto de 2007 — Publicado en Desarrollo

Para mucha gente, la lacra de programar en C/C++ frente a otros lenguajes es el tener que liberar memoria y controlar donde se libera ésta. Es cierto que el control que dan estos lenguajes al programador es mucho y permite hacer cosas magnificas, pero hay que reconocerlo: somos vagos. En este sentido, se puede hacer en C++ una cosa denominada punteros inteligentes que pueden liberar memoria cuando esta ya no sea necesaria.

Los punteros inteligentes son un tipo de clase que haciendo uso de plantillas son capaces de comportarse como punteros y, segun su implementación, liberar la memoria cuando haga falta. Por contra, esto supone un decremento del rendimiento de la aplicación que habrá que considerar antes de utilizarlos.

Hay una gran cantidad de implementaciones en internet, pero aquí sólo me voy a centrar en la idea básica de todas ellas. Para empezar podemos considerar tres tipos de políticas de manejo de punteros:

  • Agresivas: que consisten en mantener un contador de copias por cada puntero en el sistema (es el mecanismo más lento).
  • Simple: sólo se considera un puntero, no puede asignarse a otros, por lo que no se necesita ningún tipo de contador (es el mecanismo más rápido).
  • Intermedio: cualquier política de uso que se encuentre entre simple y agresiva (su rendimiento será dependiente del tipo de política).

Como ejemplo de puntero que sigue una política simple podemos considerar el 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
/**
* PtrSimple permite el manejo de la memoria de forma sencilla dentro de un cuerpo de sentencias.
* Los punteros manejados por el no deben ser liberados por el programador ni deberían utilizarse
* de otro modo que no sea a través de esta clase.
*/
template<typename _T> class PtrSimple {
private:
   _T *_ptr;
 
public:
   PtrSimple( _T* ptr = NULL ) {
      _ptr = ptr;
   }
   ~PtrSimple() {
      if( _ptr != NULL )
         delete _ptr;
   }
 
   T* operator->() {
      return _ptr;
   }
 
   T& operator*() {
      return *_ptr;
   }
};
// Esto es para que no me falle en wordpress :P : </typename>

Sin duda este es el tipo de puntero más sencillo que se puede hacer, ya que sólo controla un único puntero, no pudiendo ser asignado a otra clase, lo que limita mucho sus posibilidades a su uso en un único método. A partir de este se puede empezar a trabajar para añadirle funcionalidades (cosa que dejaré para otra entrada).

De momento lo dejo aquí para no alargar más la cosa. A ver si en un par de días saco otro artículo para seguir con el tema. Ahora a esperar que ferdy me crucifique :P

Subversion con Eclipse

Publicado por laparca el 08 de mayo de 2007 — Publicado en Desarrollo, Opinión

Pues hace poco me ha tocado utilizar para unas prácticas Subversion junto con Eclipse. De partida, Eclipse no lleva soporte para Subversion, por lo que toca instalar un plug-in a parte llamado Subclipse.

El invento funciona muy bien (bueno java es lento y se nota en toda la interfaz). La única pega que tengo son los problemas que da porque cada uno de los que componemos el grupo de prácticas tenemos una versión de java diferente lo que hace que siempre se actualice el fichero de configuración del proyecto :@

Más cosillas de la Wii

Publicado por laparca el 21 de enero de 2007 — Publicado en Desarrollo, GUL

Ayer vi en el otro lado como un tipo se había montado su porpia Wii con monitor. La cosa no deja de ser una frikada, pero me resulto curioso y, sobre todo, me gustó la idea de los infrarojos. A ver si dice pronto cómo lo hizo y nos preparamos nuestro propio disporitivo de infrarojos, que de momento no lo tenemos muy dominado.

Wii con su propia pantalla para hacerla portátil

Wiimote y Bluetooth

Publicado por laparca el 07 de enero de 2007 — Publicado en Desarrollo, Personal

Como comenta Queltosh, estamos trabajando en programar desde linux el mando de la Nintendo Wii. De momento tengo varias cositas hechas, pero de poca importancia, principalmente la detección y conexión de los mandos que halla. La siguiente porción de código es la que se encarga de la detección:

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
static int search_wiimotes()
{
    inquiry_info *ii = NULL;
    int max_rsp, num_rsp;
    int dev_id, sock, len, flags;
    int i;
    char addr[19] = { 0 };
    char name[248] = { 0 };
 
    dev_id = hci_get_route(NULL);
    sock = hci_open_dev( dev_id );
    if (dev_id < 0 || sock < 0) {
        perror("opening socket");
        exit(1);
    }
 
    len  = 4;
    max_rsp = 255;
    flags = IREQ_CACHE_FLUSH;
    ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
 
    num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
    if( num_rsp < 0 ) perror("hci_inquiry");
 
    for (i = 0; i < num_rsp; i++) {
        ba2str(&(ii+i)->bdaddr, addr);
        memset(name, 0, sizeof(name));
        if (hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name), name, 0) < 0)
	        strcpy(name, "[unknown]");
        if( !strcmp( name, "Nintendo RVL-CNT-01" ) ) {
        	strcpy( _wii_handlers[_wii_counter].addr, addr );
        	_wii_counter ++;
        }
    }
 
    free( ii );
    close( sock );
 
    return 0;
}

Con el código anterior se detectan hasta un máximo de 255 dispositivos Bluetooth y luego seleccionamos los que sean Wiimotes.

Una vez hemos detectado todos los Wiimotes, procedemos a conectarnos a ellos. Pues en este punto surje un problema: algunas implementaciones de la biblioteca Bluez (o de la pila de Bluetooth, no estoy seguro), tienen un fallo y no dejan realizar la conexión, diciendo que la clave no es correcta. Parece ser que el fallo ya está solucionado en versiones posteriores, pero en la que trae Ubutu Edgy (la que uso para las pruebas) no :’(. Así que a esperar toca.

Para los que quieran el código que hace la conexión con un Wiimote:

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
/**
 * Crea una conexión con un wiimote.
 * @param addr Dirección del wiimote
 */
static int wii_internal_connect( char addr[19] )
{
	struct sockaddr_l2 loc_addr;
	int fd_in, fd_out;
 
	fprintf( stderr, "Conectando a: %s\n", addr );
 
	fd_in = socket( AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP );
	if( fd_in < 0 ) return -1;
 
	str2ba( addr, &loc_addr.l2_bdaddr );
	loc_addr.l2_family = AF_BLUETOOTH;
	loc_addr.l2_psm = htobs( 0x13 );
 
	if( connect( fd_in, (struct sockaddr *)&loc_addr, sizeof(loc_addr) ) < 0 ) {
		close( fd_in );
		return -1;
	}
 
	fd_out = socket( AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP );
	if( fd_out < 0 ) {
		close( fd_in );
		return -1;
	}
 
	str2ba( addr, &loc_addr.l2_bdaddr );
	loc_addr.l2_family = AF_BLUETOOTH;
	loc_addr.l2_psm = htobs( 0x11 );
 
	if( connect( fd_out, (struct sockaddr *)&loc_addr, sizeof(loc_addr) ) < 0 ) {
		close( fd_out );
		close( fd_in );
		return -1;
	}
 
 
	_wii_handlers[_wii_counter].in  = fd_in;
	_wii_handlers[_wii_counter].out = fd_out;
	_wii_counter ++;
 
	return 0;
}