Archivo para la categoría ‘Informática’

Implementación básica de soporte de hilos (threads) usando programación orientada a objetos con C++

Después de este título tan largo viene una pregunta: ¿conocéis alguna implementación de hilos (threads) orientada a objetos en C++ que sea medianamente decente?

Si la respuesta es sí, genial, decídmelo en los comentarios para echarle un vistazo.

Si la respuesta es no, no hagáis como yo y os dediquéis a reinventar la rueda y usad la implementación de hilos de la librería Boost. En serio. Es más, usad Boost para todo lo que podáis porque tiene infinidad de cosas muy buenas y muy útiles ya probadas. De hecho, varias cosas de esta librería fueron incluidas en el último estándar de C++, C++11 (antes conocido como C++0x).

Pero, en caso de que os guste investigar y queráis implementar vuestra propia librería de gestión de hilos mediante programación orientada a objetos en C++, aquí tenéis un ejemplo de lo que a mi se me ha ocurrido (y que, de momento, funciona, claro ;) ). Y es un gran ladrillo, avisados estáis.

Lo primero que hay que saber

Lo primero que —de forma resumida— hay que saber es que la implementación de los hilos depende de cada sistema, aunque hay una especificación POSIX llamada pthreads que tienen implementada la mayoría de los sistemas operativos.

Esta implementación generalmente está hecha en C mediante programación estructurada, con lo que tenemos un conjunto de funciones a la que se le pasan parámetros para controlar todo el funcionamiento de los hilos.

Algunas de estas funciones son la de crear e iniciar el hilo —cuyo parámetro principal es un puntero a la función que dicho hilo tiene que ejecutar—, parar el hilo, esperar a que termine dicho hilo, etc.

De lo que trata esta entrada es de encapsular esta funcionalidad en clases para controlar los hilos mediante programación orientada a objetos y así tener un API más consistente, más abstracta y, sobre todo, más moderna. Y aquí es donde los defensores a ultranza de la programación estructurada empiezan a gritar…

Bueno, yo creo que cada cosa está para lo que está y la programación estructurada está muy bien para programar kernels, pero cuando se trata de programar aplicaciones de usuario por parte de un desarrollador de este tipo de aplicaciones (no de sistemas) siempre es mejor facilitarle la tarea a dicho desarrollador. Y con la POO se consigue.

La idea principal

Como comenté arriba, la idea principal es encapsular las funciones de C de gestión de hilos en clases y métodos de C++. Simple y sencillo ¿no? Para ello, diseñaremos las clases necesarias para incluirlas. En principio sólo una, la clase Hilo; o, mejor, la clase Thread, porque me gusta más programar en inglés, por eso de que si compartes tu código llegarás a más gente en este idioma, nos guste o no.

Pero, antes de nada, hay que solucionar un problema importante: a la función de creación del hilo hay que pasarle como parámetro un puntero a la función a ejecutar, pero las clases en C++ no tienen funciones sino que tienen métodos. ¿Y cuál es la diferencia? La principal diferencia es que en el cuerpo de las funciones se tienen los parámetros que se declaran en su definición mientras que en los métodos, además de los parámetros definidos, se tiene un parámetro oculto llamado, generalmente, this (en C++, self en Delphi…) que es un puntero a la instancia del objeto que llama a dicho método.

Y, obviamente, no se puede pasar un puntero a un método a una función que requiere como parámetro un puntero a una función.

La solución está en pasar un método estático de nuestra clase Thread a esta función pasándole como parámetro (visible, no oculto) el puntero a la instancia de nuestra clase Thread. Luego veremos un ejemplo de implementación.

Después de abordar este problema, lo siguiente es encapsular las funciones de gestión de hilos. Aquí, la verdad, no es más que tener un método en nuestra clase por cada función de gestión de hilos que nos interese gestionar. Simple, la verdad.

Lo que hay que ejecutar

El problema de pasar un puntero a una función en lugar de un método como parámetro de la función de creación del hilo está solucionado. Ahora nos queda qué es lo que hay que ejecutar. Y también es sencillo. Se puede declarar un método en nuestra clase Thread llamado execute() que sea el método estándar que siempre ejecute nuestra clase, aunque una solución más elegante (que no más eficiente, sólo estamos hablando de elegancia) sería implementar el operador () (llamada de función) y ser ese el que ejecute nuestro hilo. Repito, cuestión de elegancia, nada más.

Pero, aquí llegamos al segundo problema: ¿qué pasa si quiero ejecutar código diferente sin tener que modificar mi clase Thread cada vez?

La primera solución que se nos ocurre, por obvia, es la herencia. Declaramos la función execute() (o el operador ()) como virtual y hacemos clases derivadas donde lo implementamos. Sin duda, una muy buena solución.

Pero, ¿y si, por cualquier razón, queremos o necesitamos que nuestro hilo pueda ejecutar cualquier método de cualquier otra clase —siempre y cuando cumpla con una definición de parámetros común—?

Aquí es donde nuestra primera planificación de una sola clase Thread puede no ser suficiente.

La implementación

La solución que se me ocurrió pasa por tener dos clases. La primera, una clase Thread como la del texto anterior donde estén declarados e implementados todos los métodos que vamos a usar para gestionar el hilo, teniendo declarado el método execute() como virtual para que se puedan heredar más clases que implementen dicho método. A esta clase la llamaremos AbstractThread (por no permitir tener instancias de la misma y obligar a que sea derivada).

La otra clase sería una clase que hereda de AbstractThread a la que, ahora sí, llamaremos Thread, pero que tiene la peculiaridad de que va a usar plantillas de C++, también conocidas como templates, para indicar qué método de qué clase se va a ejecutar en el hilo.

Al final, contaremos con dos clases: AbstractThread, que es derivable y que es el método execute() el que ejecutará el hilo, y Thread, que está basada en plantillas y será a través de ellas y de su constructor desde donde indicaremos el método a ejecutar y la clase a la que pertenece.

El pseudocódigo de la interfaz de las clases

En este pequeño snippet de código muestro la interfaz (que no la implementación) básica de las dos clases antes mencionadas. En un principio la clase AbstractThread de la que heredan las demás implementando el método execute() y luego la clase Thread usando plantillas.

Clase AbstractThread


typedef int thread_id;

class AbstractThread {
  private:

    /**
     * Función estática que es la que se le pasa a la función
     * 'pthread_create(...)' para que ejecute. El parámetro
     * 'void* arg' será la instancia de la clase cuya función
     * 'execute()' hay que ejecutar. Un ejemplo de implementación
     * está en el siguiente código.
     */
    static void* thread_entry(void* arg) {
      // Código de ejemplo:
      AbstractThread* thread = reinterpret_cast<AbstractThread*>(arg);
      thread->execute();
      return NULL;
    }

  protected:
    /**
     * Función virtual pura que tienen que implementar las clases
     * derivadas y será la que ejecute el hilo.
     */
    virtual void execute() = 0;

  public:
    /**
     * Constructor de la clase.
     */
    AbstractThread();

    /**
     * Destructor de la clase.
     */
    virtual ~AbstractThread();

    /**
     * Devuelve el identificador del hilo (el 'handle').
     */
    thread_id getId();

    /**
     * Inicia el hilo.
     */
    virtual bool start();

    /**
     * Para el hilo.
     */
    virtual bool stop();

    /**
     * Indica si el hilo se está ejecutando o no.
     */
    bool isRunning();

    /**
     * Pide al hilo que pare de ejecutarse. Este es un método
     * de parada conde la implementación de 'execute()' para
     * de forma cooperativa (sin forzar la parada).
     */
    virtual bool requestStop();

    /**
     * Bloquea el hilo que llama a este método hasta que el
     * hilo en cuestión finaliza (espera la finalización del
     * hilo).
     */
    virtual bool wait();

    /**
     * Similar a 'wait()' pero con un tiempo de espera. Si el
     * hilo no finaliza el dicho tiempo de espera, devuelve
     * un error.
     */
    virtual bool timedWait(int microseconds);

    /**
     * Finaliza el hilo de forma abrupta.
     */
    virtual bool kill(int signum);

    /**
     * Devuelve la prioridad del hilo.
     */
    int  priority();

    /**
     * Fija la prioridad del hilo.
     */
    void  priority(int prio);

    /**
     * Función estática que devuelve el identificador
     * del hilo que llama a esta función.
     */
    static thread_id getCurrentThreadId();

    /**
     * Función estática que hace que el hilo que llama a
     * esta función deje de ejecutarse hasta que vuelva
     * a ser planificado por el kernel.
     */
    static void yield();
};

Clase Thread usando plantillas


template <class Worker>
class Thread : public AbstractThread {
  public:
    /**
     * Tipo de dato nuevo que representa la definición del método
     * a ejecutar en este hilo.
     */
    typedef void (Worker::*WorkerMethod)(const Thread<Worker>* thread);

  private:

    /**
     * Puntero a la instancia de la clase cuyo método hay que
     * ejecutar. En caso de que no se pase ninguna instancia en
     * el constructor, se creará (y destruirá) internamente.
     */
    Worker*      fWorker;

    /**
     * Puntero al método a ejecutar dentro de la clase Worker.
     */
    WorkerMethod fMethod;

  protected:

    /**
     * Método heredado de la clase AbstractThread que se implementa
     * para ejecutar el método del Worker en el hilo.
     */
    virtual void execute() {
      if(fWorker != NULL && fMethod != NULL) {
        (fWorker->*fMethod)(this);
      }
    }
  public:

    /**
     * Constructor de la clase donde se le pasa como parámetro
     * un puntero al método a ejecutar. Como no se pasa instancia
     * de la clase a la que pertenece dicho método, se crea una
     * internamente (y se destruye al final).
     */
    Thread(WorkerMethod method = &Worker::execute);

    /**
     * Constructor de la clase donde se le pasa una referencia a
     * una instancia de la clase de donde hay que ejecutar el método
     * y un puntero al método a ejecutar.
     */
    Thread(Worker& worker, WorkerMethod method = &Worker::execute);

    /**
     * Constructor de la clase donde se le pasa un puntero a
     * una instancia de la clase de donde hay que ejecutar el método
     * y un puntero al método a ejecutar.
     */
    Thread(Worker* worker, WorkerMethod method = &Worker::execute);
};

El resto de métodos de la clase Thread serán los mismos que en la clase AbstractThread por lo que no hace falta volver a implementarlos.

Un ejemplo de uso

Un ejemplo del uso de la clase AbstractThread sería la propia clase Thread, donde basta con implementar el método virtual puro execute() para que sea ese código el que se ejecute en el hilo.

Un ejemplo de uso de la clase Thread podría ser:


#include <iostream>

using namespace std;

class Thread;

class MiClase {
  public:
    void ejecutar(const Thread<MiClase>* thread) {
      // Cualquier código a ejecutar en el hilo, por ejemplo:
      int counter = 5;
      while(counter-- > 0) {
        cout << "Hola mundo." << endl;
        sleep(1);
      }
    }
};

MiClase mi_clase* = new MiClase();
Thread<MiClase>* thr = new Thread<MiClase>(mi_clase,&MiClase::ejecutar);

thr->start();
thr->wait();

delete thr;
delete mi_clase;

Algunas notas acerca del código

En los métodos de las clases AbastractThread y Thread se usan valores de retorno para saber si se han ejecutado correctamente o no. Esta decisión está de mano del desarrollador pudiendo usar valores booleanos, números enteros representando códigos de error (usando, por ejemplo, un enum) o excepciones.

Personalmente, aunque la primera implementación que hice fue devolviendo un tipo de dato enumerado representando códigos de error, creo que la mejor forma de hacerlo es usar excepciones debido a que tienen sus ventajas respecto al uso de valores de retorno.

Clases adicionales necesarias

El uso de hilos en las aplicaciones, además de tener que gestionar correctamente la creación de los mismos, implica, al menos, tres cosas más:

  1. Evitar el acceso a la misma zona de memoria de forma simultánea por dos o más hilos.
  2. Sincronizar el orden de acceso de dos o más hilos a un recurso compartido.
  3. Comunicación entre diferentes hilos.

Es por esto que es necesario implementar otras clases (por seguir con el modelo de programación orientada a objetos) con el fin de tener dichos mecanismos.

Por ejemplo, para envitar el acceso a la misma memoria al mismo tiempo habría que implementar una clase Mutex (mutex viene de mutual exclusion) que evita, precisamente, este supuesto, bloqueando el acceso a ciertas zonas de memoria a todos los hilos en caso de que uno de ellos ya esté en dicha zona.

Además, para la sincronización se debería implementar una clase Semaphore que sirve para indicar el orden en que los hilos acceden a los recursos. Además, también podría servir para pasar información (básica) entre hilos.

Y, finalmente, habría que implementar las clases de comunicación teniendo en cuenta los diferentes métodos que se pueden usar como, por ejemplo, la memoria compartida, colas de mensajes, señales, sockets, colas FIFO

Conclusión

Si estáis implementando aplicaciones en C++ que hagan uso de hilos, como dije más arriba, no os compliquéis la vida y usad la librería Boost. Bien implementada, con una buena interfaz y muy, muy probada.

Pero si, por cualquier motivo, tenéis que implementar vuestra propia librería, bien sea porque queréis aprender o porque no queréis que vuestro programa dependa de otras librerías o porque vuestro jefe os lo ha dicho, podéis probar, si queréis, esta idea que he expuesto aquí y, como no, podéis ponerlo en los comentarios… por compartir más que nada ;) .

Y tened en cuenta que esto es muy mejorable, pero la implementación que he hecho funciona bastante bien y es la que estoy usando actualmente. Espero que os sirva ;) .

Tipografía ‘Ubuntu Monospace’

¿Os acordáis de la tipografía Inconsolata que había puesto por aquí como buena fuente de ancho fijo para los editores de desarrollo? Pues he descubierto otra que, si no es tan buena, le está muy cerca. Aunque, bueno, quizás sea más por gustos que por “más buena” o “menos buena”.

Esta es la tipografía ‘Ubuntu Monospace‘, de la familia de tipografías de Ubuntu (que se pueden descargar gratuitamente y que valen para cualquier sistema operativo que soporte fuentes TrueType). Y aquí tenemos dos muestras:

Muestra de la tipografía Ubuntu Monospace en el terminal de Gnome Muestra de la tipografía Ubuntu Monospace en Eclipse

Y estas muestras se pueden comparar con la del terminal con Inconsolata y con la de Eclipse con Inconsolata.

Y a vosotros ¿cuál os gusta más? ¿Cuál usáis para desarrollar?

Mejorando el Explorador de Windows (o no) y otros menesteres

Los chicos de Microsoft se lo curran y han hecho un estudio donde se muestra el uso que los usuarios le damos al Explorador de Windows, esto es, cómo accedemos a las acciones para el tratamiento de archivos —mediante menús, iconos, teclas de acceso rápido, etc.— con el fin de “optimizar” estos componentes para mejorar la experiencia de usuario.

Conforme a este estudio, el nuevo rediseño del Explorador… bueno, deja un poco que desear, teniendo en cuenta que han creado una barra de herramientas mucho más grande e intrusiva que las anteriores con acciones que los usuarios apenas usan (menos del 1 % de las veces se usa la opción de la barra de herramientas en detrimento de otras opciones como el menú contextual o una tecla de acceso rápido).

Pero eso no es lo que quiero destacar aquí. Lo que quiero destacar es este gráfico:

Punto de entrada de los comandos
Punto de entrada de los comandos en el Explorador de Windows

Es decir, según este gráfico, la mayoría de las acciones que un usuario hace en el Explorador de Windows —esto es, tratamiento de archivos— es mediante el menú contextual; luego mediante una combinación de teclas (teclas rápidas); lo siguiente es el uso de la barra de herramientas; y, finalmente, mediante el menú normal de toda la vida.

De este gráfico mi conclusión es que la barra de menús es un punto de entrada de comandos superfluo que, en cuanto estás acostumbrado a usar el software, obvias en favor de los menús contextuales y de las teclas de acceso rápido. Es decir, a mi parecer, la barra de menús sobraría en muchas interfaces de software.

Entonces, me pregunto ¿por qué los chicos de Apple se empeñan en mantener un menú global que ocupa una parte muy importante del escritorio y que, según el estudio de Microsoft, apenas se usaría?

Por poner un sólo ejemplo, esa zona de la pantalla se usa en Google Chrome y en Mozilla Firefox para acceder más rápidamente a las pestañas teniendo en cuenta que el ratón “no se sale” del borde de la pantalla.

Y esto ocurre con todas las interfaces que copian descaradamente la interfaz de Mac OS X (léase Gnome Shell, Unity…). Vamos, para mí es perder espacio y, sobre todo, funcionalidad y usabilidad. Pero no tengáis miedo de que en Mac OS X (y demás) lo quiten alguna vez (aunque sea por configuración). Ante todo hay que tener señas de identidad. Aunque no sean usables.

P.D.: Tengo en mente escribir algún día sobre las disposiciones de los elementos de los escritorio informáticos —esquinas, bordes…— y la tendencia de hoy en día. El problema es la falta de tiempo… y ganas ;) .

Llamadas encadenadas en C++

Esta entrada llevaba tiempo en el tintero, por eso de tener algo de tiempo libre en vacaciones para dedicarlo a leer y elucubrar soluciones.

Por ejemplo, aquí tenemos una prueba de concepto para tener la misma funcionalidad que el operador << de C++ pero usando llamadas encadenas. Y hace (casi) lo mismo: puede enviar a la salida estándar cualquier tipo de variable, incluyendo cadenas, números enteros, números reales y booleanos, sólo que con un formato de llamada diferente.

La implementación de la clase base, pudiendo extenderla lo que se quiera, es la siguiente:

#include <iostream>
#include <string>
#include <sstream>
 

using std::cout;
using std::endl;
using std::flush;
using std::string;
using std::stringstream;
 

class Stdout {
private:
stringstream _buffer;
public:
Stdout() : _buffer() {}
~Stdout() {}
void clear() {
_buffer.str("");
}
Stdout& operator () (const char c) {
string s;
s += c;
_buffer << s;
return *this;
}
Stdout& operator () (const char* text) {
_buffer << text;
return *this;
}
Stdout& operator () (const string text) {
_buffer << text;
return *this;
}
Stdout& operator () (const int i) {
_buffer << i;
return *this;
}
Stdout& operator () (const float f) {
_buffer << f;
return *this;
}
Stdout& operator () (const double d) {
_buffer << d;
return *this;
}
Stdout& operator () (const bool b) {
_buffer << (b ? "true" : "false");
return *this;
}
void flush() {
cout << _buffer.str() << ::flush;
clear();
}
void endl() {
cout << _buffer.str() << ::endl;
clear();
}
};

Y un ejemplo de funcionamiento sería:

int
main() {
Stdout stdout;
stdout("Llamadas encadenadas: enteros: ")(42)(';')(" reales: ")(25.4)(';')(" y booleanos: ")(false)('.').endl();
return 0;
}

Siendo la salida:

$ make && ./test
Llamadas encadenadas: enteros: 42; reales: 25.4; y booleanos: false.
$

No tiene la misma funcionalidad que el operador << porque, entre otras cosas, si se quiere añadir otro elemento imprimible habría que derivar la clase, mientras que con el operador << bastaría con sobrecargarlo. Pero no me digáis que no es elegante ¿eh? :D .

Haiku OS Release 1 Alpha 3

Portada del disco de HaikuOS Release 1 Alpha 3

Todo este tinglado de letras y números en el título de la entrada no es más que para hacerse eco de la salida de la versión Release 1 Alpha 3 del sistema operativo Haiku, el clon libre de mi querido BeOS, un año y un mes después de la Alpha 2 (que no comenté aquí, por cierto).

Los cambios principales son:

  • Hardware: mejor soporte de hardware con nuevos controladores.
  • Sistemas de archivos: soporte para btrfs, ext4 y exFAT.
  • Localización: soporte para traducir y localizar el sistema operativo.
  • Layout API: nuevo API para la colocación de componentes en el desarrollo de aplicaciones.
  • Aplicaciones: mejores en la funcionalidad y en las aplicaciones.
  • Gestión de ventanas: apilado y ordenación de ventanas. Y con accesos directos de teclado.
  • Multimedia: más formatos de audio y vídeo y mejoras en la reproducción de contenidos.
  • Solución de errores: más de 950 incidencias resueltas desde la versión anterior.

En definitiva, el avance es lento de constante, lo que me lleva a pensar que, quizás, dentro de ¿un año? ¿dos años? podamos disfrutar de la primera versión de HaikuOS.

Por supuesto, hoy en día no es alternativa de uso; es, simplemente, para trastear, para aprender, para desarrollar… en definitiva, para quién le gusten las cosas bien hechas. Y sí, he dicho bien hechas. Hay mucha documentación sobre BeOS y Haiku para ver el porqué ;) .

Miniaplicación de indicadores en Ubuntu

Los chicos de Ubuntu han desarrollado en las últimas versiones de este sistema operativo un nuevo concepto de bandeja del sistema (esa zona de la barra de menús donde se concentran iconos de aplicaciones en segundo plano) a lo que ellos llaman miniaplicación de indicadores (indicators applet en inglés); y es algo como esto (los iconos grises de la izquierda):

Indicadores

Esta nuevo sistema pretende imitar la manida bandeja del sistema de Windows donde aplicaciones en segundo plano incrustan su icono para tener constancia de su ejecución y poder realizar acciones sobre las mismas.

Y, a lo que voy: los iconos son muy bonitos, pero la funcionalidad es una basura. Siento ser tan duro, pero me fastidia mucho que involucionemos en lugar de evolucionar. Porque en cada icono de la antigua bandeja del sistema yo podía hacer clic, doble clic y clic con el botón secundario para realizar diferentes acciones, mientras que con este sistema sólo puedo hacer un clic normal y siempre saldrá un menú de acciones.

¿Es esto evolución? ¿Mejora esto la usabilidad del sistema?

A mi entender no. Mismamente, con la aplicación Dropbox (el primer icono empezando por la izquierda de la imagen), antes al hacer clic se abría la carpeta de Dropbox y, con el botón secundario, salía un menú. Ahora siempre sale un menú con el clic. ¿Dónde está el resto de funcionalidad?

¿Creen los desarrolladores de Ubuntu que eliminando funcionalidad se mejora la usabilidad? Yo creo que no.

Hasta aquí mi pataleta de hoy. Otro día seguiré con Unity, que para él tiene…

Manifiesto por la artesanía del software

Como aspirantes a Artesanos del Software estamos subiendo el listón del desarrollo profesional de software mediante su práctica, y el compromiso de ayudar a los demás a aprender el arte. A través de este proceso hemos aprendido a valorar:

No sólo el software que funciona,
sino también el software bien hecho

No sólo responder al cambio,
sino también añadir valor de forma continuada

No sólo los individuos y las interacciones,
sino también una comunidad de profesionales

No sólo la colaboración con el cliente,
sino también las asociaciones productivas

Esto es, en la búsqueda de los elementos de la izquierda hemos encontrado que los elementos de la derecha son indispensables.

Me uno al Manifiesto por la artesanía del software porque yo si considero que hacer software es hacer arte.

Gracias a la traducción desde Mundo Geek.

Actualizar Ubuntu LTS a la siguiente versión en modo consola

Cuando tenemos un servidor corriendo Ubuntu y queremos actualizarlo a la siguiente versión, hay que utilizar el comando:

user@server:~:$ sudo do-release-upgrade

Este comando está en el paquete update-manager-core fácilmente instalable mediante el comando apt-get.

Pero ¿qué pasa cuando estamos en una versión LTS de Ubuntu? Por defecto no nos actualizará ya que sólo actualizará entre versiones LTS. Simplemente dirá “No new release found”.

Para solucionarlo hay que editar el archivo /etc/update-manager/release-upgrades y cambiar la línea que pone Prompt=lts por una que ponga Prompt=normal. Con esto indicaremos al actualizador que busque y actualice a nuevas versiones aunque la versión actual sea LTS y la siguiente no lo sea.

Esta funcionalidad se puede “simular” usando el parámetro -d del comando do-release-upgrade indicando que se quiere actualizar a versiones de desarrollo, con lo que el actualizador tendrá en cuenta todas las versiones, independientemente de si son LTS o no.

Como última nota por seguridad, hay que tener en cuenta que una actualización de este tipo es recomendable hacerla con un terminal in situ ya que, en caso de hacerla a través de ssh, en caso de fallo es muy difícil recuperar el sistema (vamos, eso dice la ayuda, que yo no he llegado a probarlo).

Y es ahora cuando me pongo a hacerlo… ;)

Widgetsoid

De las mejores aplicaciones que he probado últimamente para Android en mi HTC Magic: Widgetsoid.

Capturas de pantalla de Widgetsoid

Esta aplicación sirve para crear widgets del mismo estilo que el gestor de energía de Android 2.x (el widget con los botones para activar el Wifi, el Bluetooth, el GPS, la sincronización y el brillo) pero con un botón para (casi) cualquier acción posible dentro de la configuración de Android.

Además cuenta con varios estilos personalizados para hacerlo lo más parecido al tema que estés usando en cada momento pudiendo elegir tanto el color de fondo, el color de los iconos o el color de la barra que indica si el elemento está activado o no.

Lo mejor, a parte de la gran cantidad de configuración que tiene, es que es gratuita, aunque también existe una versión donate que cuesta 1 €.

Lo peor es que la interfaz no es muy amigable (la gente se empeña en crear su propia interfaz cuando el sistema ya trae la suya; y a veces no es la mejor opción).

Pero el problema más grave que he detectado es que consume muchísima batería. Al menos en estas versiones en mi Magic se pasa de durar la batería día y medio (uso normal) a durar ¡medio día! Además, aunque elimines todos los widgets, queda el servicio de Widgetsoid en ejecución. Espero que solucionen pronto esto porque es una aplicación muy interesante.

Y para que no lo pierda, mi configuración es:

  • En el widget principal: Wifi, Bluetooth, GPS, temporizador de pantalla y brillo.
  • En el secundario: sincronización, sincronizar ahora, activar/desactivar 2G/3G y activar/desactivar acceso a Internet (datos).

Pero, por supuesto, todo esto se puede configurar hasta llegar al extremo ;) .

Lazarus, el clon de Delphi para FreePascal

IDE de Lazarus

En el inicio de la carrera, uno de los lenguajes que usábamos era Pascal1 porque era un lenguaje muy bueno para aprender por su sintaxis y no tan complicado como el C con los punteros (aunque también los tiene).

Después, con el ansia de hacer cosas más «profesionales» descubrí Delphi allá por la versión 3, para tener ya aplicaciones con interfaz gráfica escritas en Pascal avanzado (orientado a objetos y demás).

Es en esta época que mezcla el estudio de la carrera con la programación en Delphi en la que soy más productivo: hice infinidad de aplicaciones, principalmente para aprender a programar (por mi cuenta, todo hay que decirlo) aunque alguna sí que se salvó y usaba uso habitualmente.

Luego, con esto de la salida del estudio para integrarse en el mundo laboral, dejé de lado el Delphi para centrarme en… bueno, en lo que tocara en ese momento.

Pero ahora me he encontrado con que tengo que volver a hacer una aplicación, con interfaz gráfica (un editor de JSON principalmente aunque algo especial) y tiene que funcionar tanto en Windows como en Linux. En su momento probé Java pero necesitaba dos librerías que había que distribuir con el *.jar, así que le volví a dar una oportunidad al Pascal, aunque esta vez de la mano de Lazarus y FreePascal.

¿Y por qué FreePascal/Lazarus? Pues porque me ha sorprendido muy gratamente que el código fuente se compila y ejecuta sin ningún cambio en Windows y Linux (obviamente son ejecutables distintos pero un sólo código fuente). No hay que preocuparse en ningún momento de directivas de compilación, de hacer diferente código para diferentes sistemas operativos y basta con distribuir un sólo ejecutable.

Además, la interfaz gráfica es totalmente coherente con la interfaz general del sistema sin ningún cambio en el código. Si estás en Linux tienes los temas de Linux y si estás en Windows tienes los temas de Windows. Incluso con los efectos de Windows 7.

Cierto es que hay lenguajes más “modernos” como Python, pero digamos que Python y yo no somos muy amigos. Y pongo “modernos” entre comillas porque en realidad no importa la modernidad del lenguaje: C es de 1972, C++ de 1980 y Cobol (el lenguaje que usan muchísimos sistemas bancarios) es de 1960.

En definitiva, si queréis desarrollar rápido una aplicación y habéis hecho vuestros pinitos en Pascal/Delphi hace sus años, Lazarus es una buena opción. Eso sí, no le pidáis peras al olmo porque esto es para lo que es: desarrollo rápido pero casi sin planificación y con un código un poco spaghetti (es lo que tiene el desarrollo dirigido a eventos). Y tened en cuenta también que esta experiencia no os servirá para mucho más. Aún así a mi me está resolviendo la papeleta de una forma muy… elegante ;) .

Nota: Considero que Pascal es un lenguaje de programación excelente para aprender a programar porque abstrae mucho más que C y similares de los entresijos de la arquitectura del ordenador centrando la atención en los algoritmos y en las estructuras de datos. Y lo mismo pienso de la programación orientada a objetos: no se debería ver hasta tener una buena base de programación estructura y, ni mucho menos, iniciarse en la programación usando POO. Y parece que esto último no soy el único que lo piensa.

Lanzar excepciones de forma polimórfica en C++

Otro de mis ladrillos sobre C++. Esta vez sobre lanzamiento de excepciones polimórficas en C++ sobre Linux. Vamos al lío:

Para ponernos en antecedentes, digamos que tenemos una aplicación multihilo escrita en C++ en Linux. Cuando en uno de los hilos se produce una excepción, esta debe ser atrapada (try { ... } catch(...) { ... }) dentro del propio hilo. En caso de que no se haga así, la excepción se propaga al hilo principal (main()) sin posibilidad de atraparla, con lo que la aplicación finaliza.

Para resumir este punto: las excepciones no se propagan entre diferentes hilos de ejecución por lo que hay que atraparlas y procesarlas dentro del propio hilo.

Pero ¿qué pasa cuando queremos que una excepción sí se propague a otros hilos? Es decir, que un error que se produce en un hilo se notifique a otro hilo para que tome medidas. Por ejemplo, tenemos un hilo que gestiona las comunicaciones y otro que gestiona la interfaz. Cuando se produce un error de comunicación es la interfaz la que tiene que notificar al usuario de dicho error.

Soluciones existen varias. Una de ellas es enviar un mensaje con la excepción que se produce al hilo que la procesa aunque hay que tener en cuenta que debe haber una arquitectura de paso de mensajes entre hilos previa.

Por ejemplo, en pseudocódigo, el hilo de comunicaciones:

void
hilo_comunicaciones() {
while(mensaje = cola_mensajes.getMensaje()) {
try {
enviar_mensaje(mensaje);
mensaje = recibir_respuesta();
} catch(Exception& e) {
notificar_interfaz(e);
}
}
}

Y ahora el hilo de proceso de las notificaciones en la interfaz:

void
hilo_proceso_notificaciones_error_comunicaciones(Exception& e) {
try {
throw e;
} catch(ComunicationException& e) {
// procesar excepciones relativas a la conexión
} catch(TimeOutException& e) {
// procesar excepciones relativas al fallo de la conexión por tiempo
} catch(ProtocolException& e) {
// procesar excepciones por errores de protocolo
} catch(…) {
// procesar excepciones generales
}
}

El problema del proceso de procesado de excepciones en la interfaz es que al relanzar la excepción que se recibe como parámetro (throw e;) esta no es la excepción recibida en el hilo de comunicaciones, es decir, no hay polimorfismo por lo que siempre se procesa como una excepción general en lugar de la correspondiente.

La opción de usar typeid(e).name() en la excepción para procesar su tipo no es viable porque es demasiado costosa (el operador typeid es bastante costoso) y porque el nombre de la excepción no es estándar, esto es, en GNU C++ (g++) el nombre está “retorcido”, mangled en inglés.

La opción recomendable es añadir a la excepción base de tu aplicación, teniendo en cuenta que para tu aplicación has desarrollado una jerarquía de excepciones acorde con su funcionalidad, un método virtual que implementen las excepciones derivadas donde, simplemente, se lancen a sí mismas.

Un código de ejemplo sería el siguiente:

// Excepción base de la jerarquía de excepciones de mi aplicación
class Exception : public runtime_error {
public:
Exception(const string msg) : runtime_error(msg) {}
virtual void raise() { throw *this; }
};

// Excepción que se lanza cuando hay un error de acceso denegado
class AccessDeniedException : public Exception {
public:
AccessDeniedException(const string msg) : Exception(msg) {}
virtual void raise() { throw *this; }

};

// Resto de excepciones derivadas conforme a los
// errores que se quieren notificar

Con esta forma de declarar las excepciones, el código de gestión de las notificaciones de error en la interfaz quedaría:

void
hilo_proceso_notificaciones_error_comunicaciones(Exception& e) {
try {
e.raise();
} catch(ComunicationException& e) {
// procesar excepciones relativas a la conexión
} catch(TimeOutException& e) {
// procesar excepciones relativas al fallo de la conexión por tiempo
} catch(ProtocolException& e) {
// procesar excepciones por errores de protocolo
} catch(…) {
// procesar excepciones generales
}
}

Con lo que funcionaría sin ningún problema ya que la excepción se lanza de forma polimórfica al usar un método virtual en la clase base que implementan todas las clases derivadas.

Para hacer una prueba se puede descargar un archivo con un programa de ejemplo (1,4 KB). Para compilarlo basta con ejecutar el comando g++ -o test exception-test.cpp.

Y por ir un poco más allá, se podría pensar que para implementar todas las excepciones que usa una aplicación habría que escribir bastante código, además, la mayoría sería repetido; lo único que cambiaría sería el nombre de la excepción y del constructor, el resto es todo igual.

Bueno, pues para eso están las macros de C: se puede crear una macro a la que se le pase como parámetro el nombre de tu nueva excepción y que ella genere todo el código restante. Pero no olvidéis que las macros son malignas en 4 formas diferentes.

Consumo excesivo de memoria de nm-applet en Ubuntu

¿Es normal que el nm-applet, la aplicación que gestiona las conexiones a redes en Ubuntu (el applet del NetworkManager), consuma más de 500 MB de memoria RAM después de tener el sistema arrancado menos de un día?

Consumo de memoria de nm-applet

Después de buscar un rato, una de las razones que se dan es que, debido a las actualizaciones en lugar de a una reinstalación limpia del sistema operativo, puede que queden archivos de configuración incorrectos que dan lugar a este consumo excesivo de memoria.

Y después de seguir buscando… no aparece nada más excepto que este problema se da en bastante usuarios.

Una forma de mitigarlo es matar el proceso nm-applet en el administrador de procesos y, posteriormente, iniciarlo como root presionando Alt+F2 y ejecutando gksu nm-applet --sm-disable.

Así que, de momento, no hay resolución. Habrá que estar atento para que el proceso no se desboque y podamos seguir trabajando sin reiniciar.

El mandamiento «Donut»

Y el señor dijo a los hombres:

“Aquel que actualice el repositorio rompiendo la compilación deberá pagar una penitencia en donuts”.

Esa es la ley.

Lo vi en MundoGeek y lo pongo porque me ha gustado… y porque hoy lo he hecho. He roto la compilación :oops: . Por cierto, la he roto en la revisión 1666 ;) .

Además tienen ustedes una imagen con esta “ley” y con una foto. Eso sí, en inglés.

Actualización: También hay una versión personalizada en español ;) .

Solucionar la espera infinita a la acción del usuario en GRUB2 cuando la máquina no se apaga correctamente

Hace tiempo que comenté el problema de que GRUB2 se quedaba esperando la respuesta del usuario al hibernar Ubuntu.

Pues ese problema se produce en más ocasiones y, una de ellas, es que cuando el sistema operativo no se apaga correctamente, es decir, no se apaga con reboot o poweroff sino que hay un fallo en el sistema operativo o hay un fallo de corriente, al reiniciar de nuevo, a GRUB2 le da por esperar hasta que el usuario seleccione una de las entradas de su lista de sistemas operativos.

Eso no está nada mal cuando es un ordenador de escritorio, así puede saber que ha ocurrido un error y entrar en el modo seguro para solucionarlo. El problema viene cuando el sistema operativo se está ejecutando en un servidor que no tiene ni pantalla ni teclado. Te puedes tirar un día intentando conectar por ssh probando diferentes IP y diferentes cables hasta que, por fin, te decides ir a la sala donde está y poner un monitor y un teclado para ver, con entre sorpresa y cabreo, que llevas una jornada intentando conectar con GRUB porque está esperando a que algún usuario compasivo presione Enter.

Este es un error de GRUB, no es que haya una configuración rara por ahí, así está programado en sus scripts. Existe una variable de este cargador de arranque que se llama recordfail que es la que se fija cuando se apaga correctamente y que es la que hace esperar cuando hay un error.

Para intentar aplicar un parche para solucionarlo hay que editar dos archivos. El primero es el archivo /etc/default/grub. En ese archivo hay que comentar (con una almohadilla como primer carácter de la línea: #) la línea que contiene el texto GRUB_HIDDEN_TIMEOUT="0". Además, si estás en un servidor, también se pueden quitar los parámetros por defecto del kernel, quiet splash, para que salga la información de inicio en lugar de la agradable imagen de inicio del sistema.

El segundo archivo que hay que modificar es el archivo /etc/grub.d/00_header. Hay que modificar la función make_timeout() (a partir de la línea 238, más o menos) comentando (también con una almohadilla como primer carácter de la línea: #) la línea que pone set timeout=-1 y añadiendo, justo debajo, la línea set timeout=15, donde 15 es el número de segundos que esperará GRUB a iniciar en caso de error (en caso de que recordfail esté a 1). Y digo comentar en vez de sustituir la línea para que, en caso de que no funcione, se pueda volver sin problemas a la versión anterior de este archivo.

Con estos cambios conseguiremos que aunque el equipo no se apague correctamente, sí se inicie correctamente cuando vuelva la luz (o cuando se inicie de nuevo). Y, en caso de que el fallo sea de hardware o de software, no os preocupéis que el propio Linux ya indicará información sobre el error.

Clases en C++ con constructores con parámetros variables (variadic functions)

Tanto C como C++ tienen la característica, mediante macros, de crear funciones con un número indefinido de argumentos. Esto es lo que se conoce como variadic functions.

Un ejemplo de una función en C con argumentos variables sería el siguiente:

#include <stdarg.h>

int
variadic_function(int arg_count,...) {
va_list arg_list;
va_start(arg_list,arg_count);
int i, sum;
for(i = 0; i < arg_count; i++) {
sum += va_arg(arg_list,int); // acciones con el argumento
}
return sum;
}

Para declarar una función que tenga un número indefinido de argumentos es necesario incluir los puntos suspensivos (…) como argumento, pero al menos debe tener uno que no sea indefinido, esto es, primero un argumento y, posteriormente, los puntos suspensivos. Esto es así porque en la función va_start() hay que pasarle, como segundo argumento, el nombre del último argumento conocido de la función. Si no tuviese al menos un argumento conocido no se podría pasar este argumento a la función va_start().

También se pueden crear métodos (funciones dentro de clases) que tengan la lista de argumentos variable, incluidos los constructores de las clases, de la forma:

class MyClass {
public:
MyClass(...);
};

Y dirán ustedes ¿y aquí por qué no se pone al menos un argumento como nos acabas de indicar?

Y yo voy a responder con otra pregunta: ¿qué es lo que te enseñan el primer día de curso de programación orientada a objetos sobre los métodos de una clase? Que los métodos no estáticos de una clase tienen un argumento implícito que es un puntero a la instancia de dicha clase. El famoso puntero this en lenguajes como C++, Java, D y alguno que otro más.

Y como este argumento es el primero del método, se puede pasar sin problemas como segundo argumento de la función va_start() para indicar que es el último definido de la lista. La implementación sería, más o menos, así:

#include <cstdarg>

using namespace std;

#include "MyClass.hpp"

MyClass::MyClass(...) {
int size = X; // el tamaño lo tenemos que tener especificado de alguna forma
int sum = 0;
va_list arg_list;
va_start(arg_list,this);
for(int i = 0; i < size; i++) {
sum += va_arg(arg_list,int);
}
va_end(arg_list);
}

Esto de los argumentos indefinidos en C y C++ es una de las chapuzas que hay para tener esta funcionalidad ya que, en su mayoría, se hace con macros (aunque están implementadas de forma interna al compilador).

Hasta que no tengamos el estándar C++0x (el nuevo estándar de C++ que supongo que ya quedará poco para que salga definitivamente) no tendremos esta característica incluida en el lenguaje. Incluso se ha ampliado a parámetros variables en las plantillas (variadic templates), con lo que podremos tener tuplas de n elementos de distinto tipo. Pero, de momento, nos conformaremos ;) .

Desarrollar librerías en linux usando otras librerías (y sus curiosos efectos)

Después del pequeño tostón del otro día donde se comentaba la ruta de las librerías en Linux, hoy toca comentar el gran tostón de cómo se desarrollan librerías que usen otras librerías y varios efectos durante el enlazado. Si no estáis muy interesados en el desarrollo de librerías en Linux no sigáis leyendo porque quizás me odiéis. Y si sí desarrolláis librerías… bueno, quizás tampoco deberíais seguir… quedáis avisados.

Lo primero, una descripción rápida de como programar una libraría en Linux:

Creas el archivo de código fuente, por ejemplo mylib.cpp, para luego compilarlo con la opción del compilador de generar código independiente de la posición (PIC):

$ g++ -Wall -fPIC -c mylib.cpp

Luego enlazas todos los archivos objeto resultantes (*.o) en el binario de la librería indicándole al linker, además, el nombre de la librería (soname):

$ g++ -shared -Wl,soname,libmylib.so.0 -o libmylib.so.0.1 *.o

Una vez hecho esto se puede instalar la librería en algún directorio como /usr/local/lib u /opt/lib, aunque no es necesario para hacer estas pruebas.

Para usar la librería, basta con crear un programa, por ejemplo myprog.cpp, y enlazar esta librería en él. Eso sí, no os olvidéis de indicarle al compilador dónde están los archivos de cabecera (con la opción -I) y de exportar la variable LD_LIBRARY_PATH indicando el directorio de vuestra librería (como vimos el otro día):

$ g++ -I/path/to/mylib/include -o myprog -lmylib myprog.cpp

Una vez tenemos la librería y nuestro programa que la usa vamos con los curiosos efectos:

En el caso de que nuestra librería use otra librería de forma interna, a la hora de compilar es necesario indicarle donde están los archivos de cabecera de la librería a usar (opción -I) pero no es necesario indicarle, a la hora de enlazar, la librería con la que enlazar:

$ g++ -Wall -fPIC -I/path/to/otherlib/include -c mylib.cpp
$ g++ -shared -Wl,soname,libmylib.so.0 -lotherlib -o libmylib.so.0.1 *.o

A la hora de compilar nuestro programa con nuestra librería enlazada de la forma anterior, es necesario que enlacemos nuestro programa a la otra librería, sino ni siquiera enlazará porque no se pueden resolver los símbolos de otherlib que usa mylib. Por ello nuestro programa se compilaría:

$ g++ -o myprog -lmylib -lotherlib myprog.cpp

En caso de enlazar mylib con otherlib (usando la opción -lotherlib antes comentada), no sería necesario enlazar myprog con otherlib. Resumiendo este tostón incomprensible: hay que enlazar nuestro programa con todas las librerías que use y todas las que usen éstas y no lo estén ya en las mismas, aunque siempre es mejor enlazar cada cosa con lo que utiliza.

Un ejemplo:

  • Compilo mylib sin enlazar con otherlib:
    • Compilo myprog enlazando a mylib: error porque no enlaza con otherlib.
    • Compilo myprog enlazando a mylib y otherlib: todo correcto.
  • Compilo mylib enlazando con otherlib:
    • Compilo myprog enlazando a mylib: todo correcto porque, aunque hace uso de otherlib, esta ya está enlazada en mylib.
    • Compilo myprog enlazando a mylib y otherlib: todo correcto aunque innecesario porque otherlib ya está enlazada con mylib.

Buf… bueno, yo lo entiendo porque me ha pasado y he investigado un rato para resolverlo, pero visto aquí, así, sin anestesia… pues cuesta. Pero bueno, aquí queda por si en un futuro se me olvida. Y para quién le sea útil, por supuesto.