¡Volando! tic() … toc()

En estos días he descubierto un gran lugar donde aprender y practicar nuevas técnicas de programación y mejorar nuestra habilidades: hackerrank.com. De ahí he podido, además, sacar una comparativa de rendimiento de lenguajes de programación basada en el entorno de compilación / ejecución que se le asignan a cada lenguaje en las máquinas virtuales (ver entorno).

Se puede ver que los lenguajes a los que se les da menos tiempo de ejecución (por ende los más rápidos) son: C, C++ (de esperar), Objective-C (esperable), Pascal (curioso, pero no de extrañar)… y… ¡tarán! ¡Swift!. Además, todos ellos están en el mínimo de memoria (512MB), aunque en este caso sí es de notar que muchos otros lenguajes se suman al carro (salvo Go con 1GB, y en los problemas más exigentes, Java 7 y Python con 1GB, ¡Java 8 con 2GB!).

Y si queréis leer un poco más de Swift, podéis entrar en la página oficial del lenguaje.

Optimización 90-10

Ya dicen que el 90% del tiempo de ejecución está en el 10% del código, y en situaciones como la que menciono hoy es muy patente.

En un código para leer un determinado tipo de fichero tenía la siguiente secuencia (la función completa es unas 7 veces más larga):

for (ii = 0; ii < 3; ++ii) {
 if (tokens[ii].empty()) {
 break;
 }
 elem.clear();
 boost::split(elem, tokens[ii], boost::is_any_of("/"));

 if (elem.empty()) {
 break;
 }
 a[ii] = stoi(elem[0]) - 1;
}

Este fragmento se repite unas 80000 veces en el fichero de pruebas (los ficheros más grandes pueden requerir más de 2 millones de iteraciones). La función contenedora ya estaba optimizada en términos de uso de memoria, creación de objetos temporales, rutas más probables, etc.

Aunque en la versión de despliegue la función es suficientemente rápida (0,45 segundos de media), en modo depuración se vuelve insoportablemente lenta, en torno a los 60 segundos.

Analizando el problema se ve que lo único importante es el primer elemento del vector, es decir, lo que va desde el comienzo de tokens[ii] hasta el primer carácter “/”, por lo que la función puede re-escribirse como sigue:

for (ii = 0; ii < 3; ++ii) {
 if (tokens[ii].empty()) {
 break;
 }

 int p = tokens[ii].find('/');
 a[ii] = stoi(tokens[ii].substr(0, p)) - 1;
}

En modo depuración ahora tarda 28 segundos: ¡50% menos!. En modo final la optimización es menos notable para ficheros pequeños, ya que la función de Boost en cuestión es mucho más eficiente en este modo. De todas formas, los tiempos han mejorado y ahora tardo menos en abrir mis ficheros para corregir errores.

Más bocaditos de Qt

Sigo de lleno con Qt, eso es parte del motivo por el que lleve algo de tiempo sin escribir. Irónicamente, no tengo mucho para contar en esta ocasión, pero aprovecho para escribir algunos “bocaditos”.

Tipografías incrustadas

Dar un toque de distinción a nuestras aplicaciones puede incluir el utilizar tipografías que no son estándares, o que sólo vienen incluidas con instalaciones de determinados programas que no podemos asumir estén siempre presentes (ejemplo típico, MS Office).

La opción más conveniente es la de incluirlas con nuestra aplicación (no hablaré de licencias ni nada parecido, asumo que todo está en orden). Para esto se pueden bien copiar junto al ejecutable, o incluir en un fichero de recursos (QRC). En cualquier de ambos casos habrá que registrar las fuentes antes de poder utilizarlas, especialmente si deseamos usarlas desde hojas de estilos.

QFontDatabase::addApplicationFont(":/fonts/arialn.ttf");
QFontDatabase::addApplicationFont(":/fonts/arialnb.ttf");
QFontDatabase::addApplicationFont(":/fonts/arialnbi.ttf");
QFontDatabase::addApplicationFont(":/fonts/arialni.ttf");

Esto podemos hacerlo en nuestra función main(), justo después de instanciar la aplicación por ejemplo. A partir de ese momento podremos utilizar las fuentes por su nombre como si se tratase de otra fuente del sistema.

Qt5 y la “plaftorms/qwindows.dll”

Desde Qt5 existe una DLL nueva de la que no está muy bien documentada su importancia en el proceso de distribución de la aplicación. Se trata de la platforms/qwindows.dll. Junto a las DLL base, como Qt5Core.dll, Qt5Widgets.dll, etc. El caso es que si Qt no está instalado en el ordenador destino, debemos especificar la ruta hasta esta DLL. La forma más sencilla que he encontrado hasta ahora es especificar la ruta de la carpeta de plugins (donde también deberán estar las DLL para cargas imágenes, por cierto).

En Qt4 podíamos especificar la carpeta de plugins de la siguiente forma:

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

 QApplication::addLibraryPath(qApp->applicationDirPath() + "/qtplugins/");

 // ...
}

De esta forma tendríamos una carpeta “qtplugins” junto a nuestro ejecutable, y siempre podríamos obtener la ruta hasta ella. La diferencia ahora está en que en Qt5 debe hacerse antes de instanciar la aplicación, ya que es ahí cuando se cargará dicha DLL. El problema es que “qApp” es una macro de conveniencia a “QCoreApplication::instance()” (patrón singleton), por lo que es necesario que hayamos instanciado la clase antes de utilizarla. Esto nos lleva a que tenemos que asumir que el directorio actual sigue siendo el del ejecutable:

int main(int argc, char *argv[])
{
 QApplication::addLibraryPath("./qtplugins/");

 QApplication a(argc, argv);

 // ...
}

Esto sirve siempre y cuando no lancemos la aplicación desde un acceso directo que tenga cambiado el directorio de trabajo.

Extendiendo clases

Esto no es exclusivo de Qt, pero el ejemplo lo tomo de ahí. En Qt4, el método “tabBar()” de la clase QTabWidget era protegido (a partir de Qt5 es público). Si queríamos acceder a él teníamos que recurrir a la herencia para crear un método público tal que

class MyTabWidget : public QTabWidget {
public:
 QTabBar *tabBar()
 {
 return QTabWidget::tabBar();
 }
};

Ahora bien, una cosa que no todos los programadores de C++ sabrán, pero que verán claro como el agua después de leerlo: no es necesario crear el nuestro TabWidget utilizando el objeto MyTabWidget, podemos hacerlo con QTabWidget (por ejemplo, desde Qt Designer), y luego hacer un simple cast para acceder al método público.

((MyTabWidget*)ui.tabWidget)->tabBar()->setTabText(0, tr("Customers"));

Este “truco” podemos hacerlo con cualquier clase y con cualquier número de métodos, accediendo a atributos y métodos protegidos (nunca privados), siempre y cuando no declaremos nuevos atributos. Esto se debe a que la definición de nuevos métodos no modifica el contenido del objeto, por lo que el casting sigue siendo válido (el objeto sigue teniendo la estructura del tipo base), pero con una tabla de métodos extendida.

Objective-C tiene un mecanismo similar, aunque algo más elegante (y con una implementación diferente debido a que usa paso de mensajes en lugar de llamadas a funciones, pero eso es harina de otro costal). Este mecanismo se llama “categorías“, y consiste en agregar nuevos métodos a una clase existente. Este método puede acceder a atributos privados (en Objective-C no existen atributos protegidos, y cualquier método puede ser invocado siempre que se conozca su cabecera). Lo más destacable de este mecanismo es que no hace falta realizar un casting al objeto, sino que automáticamente la clase adquiere los métodos de las categorías a las que tiene acceso (incluidas en un fichero de cabecera previo). Pueden imaginar la de plugins que pueden hacerse con esto, y lo limpio que queda el código. El único cuidado que hay que tener es el de documentar bien el código a fin de no confundir métodos estándar de categorías propias.

Traducción de recursos

La traducción de cadenas de textos en Qt está muy documentada, pero no me pareció lo mismo al tratar de incorporar soporte multi-idioma en los recursos de las aplicaciones. Así que acá va. Imaginemos que tenemos una imagen “:/hola.png” que queremos traducir. Nuestro QRC originalmente contendrá algo tal que:

<qresource>
 <file>hola.png</file>
</qresource>

Para agregar las traducciones, debemos crear alias a dicha imagen desde varios ficheros (en el ejemplo, el castellano es el idioma por defecto):

<qresource>
 <file alias="hola.png">hola_es.png</file>
</qresource>
<qresource lang="en">
 <file alias="hola.png">hola_en.png</file>
</qresource>

A modo resumen, para traducir un texto debemos borrar el traductor anterior de la aplicación (si lo hubiera), crear un nuevo traductor desde nuestro fichero QM (fichero compilado de traducciones), instalar el nuevo traductor, re-traducir todas nuestras interfaces abiertas mediante “::retranslateUi()” (las ventanas sin abrir usarán la nueva traducción automáticamente al crearlas), y volver a generar los textos que programáticamente hayamos generado.

Con los ficheros de recursos debemos cambiar el parámetro global de localización de la aplicación antes de re-cargar los recursos. Para ello:

QLocale::setDefault(QLocale("en"));

Donde “en” es el código de localización deseado (“es”, “en”, “de”, “it”, etc).

Una sugerencia personal si queremos que el idioma cambie dinámicamente (sin tener que cerrar y volver a abrir nuestra aplicación) es crear una señal que se emita desde la configuración del idioma después de haber configurado el nuevo traductor y la localización, y que reciban todos los objetos que deban traducir sus textos o recursos.

Hasta la próxima, ¡espero disfruten de los bocaditos!

Anec-notas (III)

Las vacaciones no siempre son vacaciones al 100%, cuando se trabaja en el mundo informático lo normal es pasear por todo el país con el portátil a cuestas y tener que realizar cambios sobre la marcha o pequeños ajustes a algún proyecto. Y dado que ya estaba en ésas he acá la tercera entrega de anéc-notas.

QImage

Entre los ajustes que he tenido que hacer hay uno que se refiere a la adquisición de imágenes a 60fps desde diferentes modelos de sensores. El flujo de datos involucra la visualización de dichos fotogramas en una interfaz programada en Qt. Sin entrar en más detalles, la imagen la adquiría en un QImage para su representación. Luego, con el fin de procesarla con otros algoritmos intermedios, obtenía el puntero al bloque de datos mediante un QImage::bits(). De ahí surgen los dos comentarios de esta sección.

El primero es que dicho arreglo de bytes puede contener un padding final, por lo que los píxeles de una fila no están a continuación de la anterior, sino después de un pequeño espacio. Esto no ocurre si la imagen ha sido creada desde un puntero (lo cual evita hacer una copia del bloque de memoria), pero si la imagen se manipula o se crea de otra forma hay que tener en cuenta este espacio.

El otro detalle me pareció muy curioso. Este método tiene dos variantes, una “const” y otra “no-const”. La diferencia principal es que la variante “no-const” crea una copia antes de devolver el puntero, por lo que cualquier optimización que uno crea que está haciendo al acceder directamente al bloque de memoria puede irse al pozo si no tenemos cuidado, por ejemplo, si hacemos algo como:

swap_rgb_to_gbr(img.bits());

Y la función es algo de este estilo:

void swap_rgb_to_gbr(unsigned char *pixels);

La mejor solución que he podido encontrar implica forzar un casting:

const unsigned char *buffer = img.bits();
swap_rgb_to_gbr(const_cast&lt; unsigned char* &gt;(buffer));

Ahora bien, cuando se trata de cambiar el tamaño de una imagen, QImage es poco más que un niño de 2 años con unos lápices de colores en la mano. Lo mejor es utilizar opciones más potentes, como por ejemplo OpenCV (asumiremos que src es ya una imagen de OpenCV válida):

cv::Mat dst;
cv::resize(src, dst, cv::Size(nuevo_ancho, nuevo_alto), 0.0, 0.0, cv::INTER_LANCZOS4);

QImage resizedimg(nuevo_ancho, nuevo_alto, QImage::Format_RGB888); // 24bits por píxel

const unsigned char *dstdata = dst.data;
const int rowsize = nuevo_ancho * 3; // RGB
for (int ii = 0; ii &lt; nuevo_anchoe; ++ii, dstdata += rowsize)
 memcpy(resizedimg.scanLine(ii), dstdata, rowsize);

QMenu

En lo particular me gusta tener un menú “Debug” en el cual puedo incorporar funcionalidades de depuración o que sólo deban estar presente durante el desarrollo, tales como recargar el fichero de estilos, por ejemplo. A fin de simplificar el desarrollo, este menú está siempre presente, y si la aplicación no está siendo compilada en modo depuración se quita.

Hago mención de dicho proceso acá porque no me fue evidente encontrar el cómo se hacía, así que lo dejo por escrito.

ui.menubar-&gt;removeAction(ui.menuDebug-&gt;menuAction());

Otro detalle sobre los menús es cómo establecer su color de fondo y de texto. Acá dejo un stylesheet de ejemplo.

/* Barra de menu */
QMenuBar
{
 background-image: url(:/someimage.jpeg);
}

/* Elementos de la barra de menú: título de cada menú */
QMenuBar::item
{
 background-color: transparent;
 color: #999999;
}
QMenuBar::item:selected
{
 background-color: transparent;
 color: #ffffff;
}

/* Cada menú */
QMenu
{
 background-color: #444444;
 color: #999999;
}

/* Cada acción del menú */
QMenu::item
{
 background-color: #444444;
}
QMenu::item:selected
{
 color: #ffffff;
}

Anec-notas (II)

Esta semana ha sido bastante movida ya que tenemos la entrega de un producto para la semana que viene, ya sabéis, afinar detalles y esas cosas, así que en esta segunda emisión de Anec-notas tendremos un par de puntos prácticos sobre Qt.

QLineEdit

El control QLineEdit es probablemente de los más utilizados a la hora de crear formularios. Normalmente se fija su contenido con un QLineEdit::setText(QString) al cargar el formulario, y se lee de vuelta su valor con un (QString)QLineEdit::text() al cerrar o validar el mismo. Lo que ocurre en este caso es que el flujo de la aplicación no establece puntos “finales” claros en los que validar u obtener el contenido del control (es decir, no hay un botón “Aceptar” o “Guardar”), sino que debe hacerse continuamente (cada vez que hay un cambio), guardando el contenido a la base de datos.

Con este fin es posible utilizar las señales textChanged(QString) y textEdit(QString), que se emiten cada vez que el contenido del control es modificado. La principal diferencia entre ellas radica en que textEdit sólo se emite cuando el contenido del control es modificado desde la interfaz (por el usuario), mientras que textChanged es emitida también si se cambia programáticamente mediante setText(QString). Para evitar que cada vez que inicialice el control tenga que escribir en la base de datos (operación innecesaria ya que el contenido en ese momento es el mismo), se utiliza la señal textEdit(QString).

Menús contextuales

Cuando se despliega un menú contextual, éste se apropia del mouse (grabMouse) lo que evita que los widgets que han solicitado seguir continuamente el movimiento del cursor no reciban el evento mientras el menú contextual esté desplegado. Como ejemplo práctico, imaginemos un widget que renderiza elementos gráficos propios, como un widget de OpenGL, y que mediante eventos MouseMove cambia el estado “highlighted” de los elementos que están debajo del cursor. Al desplegar el menú contextual para ese elemento, el widget deja de recibir el evento de MouseMove, por lo que cuando se oculta el menú contextual, el mouse puede estar en otra posición, resultando en una incoherencia entre el elemento resaltado y el cursor.

Los menús tienen una señal, aboutToHide(), que se emite cuando el menú se oculta, bien sea por seleccionar una entrada del menú o por desactivar el menú. Con esta señal se puede actualizar el estado de los elementos forzando un evento MouseMove. Ahora bien, este evento se emite antes de llamar al método asociado a la entrada del menú, por lo que si éste necesita del estado actual (por ejemplo, el elemento resaltado) debe tenerse este hecho en cuenta.

Anec-notas (I)

Con este post me gustaría comenzar una nueva sección en el blog la cual, por eso de ponerle nombre a todo, he decidido llamar “anec-notas”. Pretendo acá combinar historias técnicas de la semana, problemas resueltos y tópicos que pueda comentar públicamente sin romper cláusulas de confidencialidad, tratando a su vez de hacer un diario personal el cual consultar en un futuro. La idea no es hacer tutoriales completos sobre cada tema, sino contar detalles sobre ellos, así que no haré grandes introducciones en la medida de lo posible. Esta sección se inaugura con una historia de la semana pasada en el trabajo.

Detalles sobre funciones y métodos con parámetros variables

Para usar argumentos variables en C y C++ (antes de la última versión) se escriben unos puntos suspensivos y se usa un tipo de dato especial llamado va_list.

void foo(int arg1, ...)
{
 va_list args;
 va_start(args, arg1);

 float a = va_arg(args, float);
 printf("%f\n", a);

 va_end(args);
}

Básicamente, va_list es un puntero al stack, que es donde están almacenados los parámetros de la función. Con la función va_arg, junto al tipo de dato esperado, se van obteniendo cada uno de los parámetros de forma consecutiva. Por este motivo, es necesario conocer la posición del primer parámetro, por lo que es obligatorio que haya al menos un parámetro formal en la función, el cual se usa en la inicialización.

Todo esto surgió cuando tuve que diseñar una biblioteca de control de cámaras de vídeo. Esta biblioteca debe controlar varios modelos de cámara, cada una con propiedades diferentes. Para simplificar el diseño de la clase, se decidió utilizar dos métodos del tipo SetProperty(int prop, …) y GetProperty(int prop, …).

Hay dos detalles que me gustarían comentar. El primero es respecto al código de arriba: es incorrecto. Al llamar a la función ‘foo’, cualquiera sea el valor que se pase como segundo parámetro, siempre se mostrará 0.0. Esto me tuvo de cabeza un rato cuando trataba de procesar la propiedad FPS, que es un float. Esto se debe a que va_args interpreta los floats como doubles. La forma correcta sería:

 float a = (float)va_arg(args, double);

Esto no pasa con los punteros a float (float*), ya que todos los punteros son realmente el mismo tipo de dato, sólo que el compilador los interpreta de una forma u otra únicamente al desreferenciar la dirección de memoria.

El segundo detalle es que no es posible utilizar parámetros variables en funciones virtuales. Me explico, se puede, pero una clase que quiera reimplementar dicho método no tendrá forma de pasarle los parámetros variables. Esto llevaba a que no podía hacer una jerarquía de clases que me permitiera implementar el control de las propiedades más comunes en clases generales y dejar las propiedades específicas de cada cámara en especializaciones progresivas. La forma más sencilla de resolver este problema es utilizar dos métodos, uno propio de la clase padre que extrae los parámetros variables, y otro virtual, posiblemente privado, que recibe un va_list y que es quien realmente los interpreta:

class A
{
public:
 void foo(int n, ...)
 {
 va_list args;
 va_start(args, n);
 _bar(n, args);
 va_end(args);
 }

protected:
 virtual void _bar(int n, va_list args) = 0;
};

class B : public A
{
protected:
 virtual void _bar(int n, va_list args)
 {
 // ...
 }
};

En este caso es importante saber que una vez procesados los parámetros variables, no es posible recuperarlos, por lo que hay que tenerlo en cuenta en caso de que los métodos deban ser llamados a lo largo de la jerarquía de clases.

¡Espero verlos la semana que viene con más anec-notas!

Algo de OpenSSL

De cara a un proyecto que he tenido que hacer en estos días, me he topado con la necesidad de utilizar OpenSSL para ciertas operaciones relacionadas con RSA, el cual no había tenido el placer de probar hasta la semana pasada.

Dejo acá un poco mi experiencia y resumen de uso, tanto en la línea de comandos como en el uso de la biblioteca de programación.

Nota: lo he usado sólo en entorno Windows, por lo que no estoy seguro si la sintaxis sea exactamente igual.

Generar el par de claves pública y privadaPrimero generamos la clave privada en formato PEM (el número al final es el tamaño de la clave, si se omite será de 512 bits):openssl genrsa -out private.pem 1024

Ahora, extraemos la clave pública:openssl rsa -pubout -in private.pem -out public.pem

Firmar ficherosPara el proyecto que mencioné necesitaba verificar la autenticidad de ciertos ficheros. Para ello decidí utilizar un protocolo de verificación bastante tradicional consistente en firmar los ficheros en cuestión con la clave privada y al recibir los ficheros desencriptarlos con la clave pública y comprobar que el contenido coincide con el del fichero sin firmar. Ambos pasos son necesarios (desencriptar y comprobar) para evitar que versiones correctamente firmadas, pero no las deseadas, puedan enviarse en lugar del fichero correcto.

Para firmar un fichero:openssl rsautl -sign -in plain.txt -out signed.txt -inkey private.pem

Podemos comprobar nosotros mismos que el fichero firmado es correcto usando:openssl rsautl -verify -in signed.txt -inkey public.pem -pubin

Se debería mostrar el contenido del fichero. En caso de que el fichero no estuviese firmado con la clave privada OpenSSL dará un error.

Verificar el fichero firmado utilizando C/C++Acá fue donde más problemas tuve y lo que me llevó a escribir este post, de forma que otros puedan aprovecharse de mis golpes al aire. Haré la menor cantidad de suposiciones posibles a fin de que esté todo claro. Sólo asumiré que las rutas de los ficheros de cabeceras y todo eso está configurado.

Ficheros de cabecera de OpenSSL necesarios:

#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>

Al cargar la clave pública, contemplo dos opciones (aunque soy partidario de la segunda):

  1. Desde un fichero. El problema está en que fácilmente pueden cambiar la clave pública por otra y falsificar los ficheros firmados. El código sería:

    BIO *pubkeyin = BIO_new(BIO_s_file());
    if (BIO_read_filename(pubkeyin, "public.pem") <= 0)
    // Error leyendo clave pública

  2. Desde una cadena de caracteres. Esta opción es más genérica y permite, además de cargar la cadena desde un fichero, incrustarla en el ejecutable o descargarla desde un servidor, por ejemplo.

    BIO *pubkeyin = BIO_new_mem_buf(_public_key, strlen(public_key_str));

    Donde “public_key_str” es una cadena de caracteres de tipo char[].

    Nota importante si se incrusta la cadena de caracteres, y es el incluir los saltos de línea al copiar la clave al código fuente.

    char public_key_str[] = "-----BEGIN PUBLIC KEY-----\n"
    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMe9XS8VDg6H5dgqoWeJGhwJw4\n"
    "wz4YZfj1AZnSjWJyqnVnCf4YWolwZ0xGKuR6hktsCYj759AADkRx9/2+SwFGb0pE\n"
    "6kdxeIL8V/yNSN6LJB84+LmVLjdP9or/hZ/l/XyAc9LT8q0Dl6p8mNzOoTe7e6CJ\n"
    "pz8UCfFig0TGGzmRbwIDAQAB\n"
    "-----END PUBLIC KEY-----\n";

Extraer la clave pública y preparar las estructuras de datos necesarias:

EVP_PKEY *pkey = PEM_read_bio_PUBKEY(pubkeyin, NULL, NULL, NULL);
RSA *rsa = EVP_PKEY_get1_RSA(pkey);
EVP_PKEY_free(pkey);

El contenido del fichero firmado deberá estar en una cadena de caracteres de tipo unsigned char[]:

unsigned char *rsa_in = (unsigned char *)OPENSSL_malloc(keysize * 2);
BIO *in = BIO_new_file(_targetURL.toLocal8Bit().data(), "rb");
int rsa_inlen = BIO_read(in, rsa_in, keysize * 2);

Finalmente, desencriptar el fichero firmado (RSA_PKCS1_PADDING es el valor estándar de padding al firmar ficheros):

unsigned char *rsa_out = (unsigned char *)OPENSSL_malloc(keysize + 1);
int rsa_outlen = RSA_public_decrypt(rsa_inlen, rsa_in, rsa_out, rsa, RSA_PKCS1_PADDING);
if (rsa_outlen <= 0)
// Error, fichero firmado incorrectamente
// Acá su código de error
else {
// Importante convertir la cadena leída en una cadena de caracteres válida para C
rsa_out[rsa_outlen] = 0;
}

Para comprobar la validez del fichero, habrá que comprobarlo contra el contenido esperado (en este caso almacenado en la variable “valid_str”):

bool valid = (strcmp((char *)rsa_out, valid_str) == 0);

Al finalizar, borrar las estructuras creadas. Omitiré las creadas con métodos tradicionales y dejaré las creadas mediante OpenSSL:

OPENSSL_free(rsa_out);
RSA_free(rsa);
BIO_free(pubkeyin);

Doxygen + namespaces + Qt

Desde hace algunos días me estoy dedicando a documentar unas bibliotecas para C++ que he desarrollado en mi trabajo. Para ello, como no, estoy utilizando Doxygen. Como utilizamos CMake para generar los proyectos, pues qué mejor lugar para generar la documentación. Muestro a continuación la configuración, simplemente a modo de ilustración:

#-- Add an Option to toggle the generation of the API documentationOPTION(BUILD_DOCUMENTATION "Use Doxygen to create the HTML based API documentation" ON)IF(BUILD_DOCUMENTATION)FIND_PACKAGE(Doxygen)IF(NOT DOXYGEN_FOUND)MESSAGE(FATAL_ERROR "Doxygen is needed to build the documentation. Please install it correctly.")ENDIF()#-- Configure the Template Doxyfile for our specific projectCONFIGURE_FILE(Doxyfile.in ${PROJECT_BINARY_DIR}/Doxyfile @ONLY IMMEDIATE)FILE(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/doc)#-- Add a custom target to run Doxygen when ever the project is builtADD_CUSTOM_TARGET(DocsCOMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/DoxyfileSOURCES ${PROJECT_BINARY_DIR}/Doxyfile)# IF you do NOT want the documentation to be generated EVERY time you build the project# then leave out the 'ALL' keyword from the above command.ENDIF()

El fichero Doxyfile.in contiene la siguiente configuración:

PROJECT_NAME = @LIBRARY_NAME@PROJECT_NUMBER = @LIBRARY_VERSION_MAJOR@.@LIBRARY_VERSION_MINOR@.@LIBRARY_VERSION_PATCH@OUTPUT_DIRECTORY = "@PROJECT_BINARY_DIR@/doc"STRIP_FROM_PATH = "@PROJECT_SOURCE_DIR@/lib"INPUT = "@PROJECT_SOURCE_DIR@/lib"RECURSIVE = YESCREATE_SUBDIRS = YESEXTRACT_PRIVATE = NOGENERATE_TODOLIST = YESGENERATE_BUGLIST = YESWARNINGS = NOWARN_IF_UNDOCUMENTED = NO

Donde @LIBRARY_NAME@ y demás son variables de entorno definidas en el fichero CMake y que hacen alusión al nombre de la biblioteca y la versión actual de la misma.

No entraré en más detalle sobre el uso de Doxygen ya que hay mucho escrito, salvo por un pequeño “problema” que tuve.

Resulta que si generamos la documentación a palo seco, los namespaces que hayamos utilizado serán incluidos en la documentación. Esto está bien hasta cierto punto: si la documentación es de una API o de una biblioteca, es muy común que todo esté dentro de un namespace común (por ejemplo, libname). En este caso toda la documentación estará plagada de libname::, libname::, libname::… y no es precisamente agradable a la vista ni fácil de leer.

Buscando en Google, rápidamente di con una posible solución (como verán, la he adaptado para incluir mi versión, pero la seguiré contando acá de todas formas):

http://stackoverflow.com/questions/6811816/doxygen-strip-top-level-namespace/11231222

El código propuesto consiste en definir una serie de macros, y reemplazar el uso del namespace principal por ellas.

#ifndef DOXY_PARSER#define LIB_NAMESPACE_STARTS namespace lib_namespace { /##/#define LIB_NAMESPACE_ENDS } /##/#define LIB_NAMESPACE lib_namespace#else#define LIB_NAMESPACE_STARTS /##/#define LIB_NAMESPACE_ENDS /##/#define LIB_NAMESPACE#endif

Con esta solución hay un problema, si usamos Qt en el proyecto (como podrán suponer era mi caso), el proceso de generación de los ficheros moc se salta por completo las macros (no las expande), y por ende el fichero .cxx generado no dispone de la información de los namespaces, lo cual, adivinaron, finaliza en un montón de errores por parte del comilador.

Una posible solución sería utilizar un #define QT_NAMESPACE lib_namespace, pero esto movería todo el espacio de nombres de Qt al de la biblioteca, y eso no es bueno.

La solución que encontré al final es más general, y funciona tanto en proyectos que utilizan Qt como los que no.

#ifndef DOXY_PARSER#define LIB_NAMESPACE lib_namespace#define LIB_NAMESPACE_STARTS namespace LIB_NAMESPACE { /##/#if (defined MOCED_FILE)#define LIB_NAMESPACE_ENDS } using namespace LIB_NAMESPACE; /##/#else#define LIB_NAMESPACE_ENDS } /##/#endif#define USING_LIB_NAMESPACE using namespace LIB_NAMESPACE; /##/#else#define LIB_NAMESPACE_STARTS /##/#define LIB_NAMESPACE_ENDS /##/#define LIB_NAMESPACE#define USING_LIB_NAMESPACE /##/#endif

Donde `MOCED_FILE` es una macro definida únicamente para los ficheros moc. Esto es sencillo si se utiliza CMake:

QT4_WRAP_CPP(mocSources qt_file1.h qt_file2.h)SET_SOURCE_FILES_PROPERTIES(${mocSources} PROPERTIES COMPILE_FLAGS "-DMOCED_FILE")

Si bien la intención original del post era comentar mis andanzas con este problema de los namespaces, decidí agregar una pequeña introducción al uso de Doxygen con CMake ya que por la red no lo encontré a la primera.

Hermosos templates en C++ (parte II)

Como muchos saben, soy un amante de las automatizaciones de tareas, especialmente en mi trabajo: scripts, macros… templates! Hace tiempo que no hacía un segundo post al respecto, pero acá va.

Un asuntito que muchos saben pero a la vez no, es que un template debe estar completamente visible a la hora de especializarlo. En cristiano: el código de la clase (o función) debe estar en el “.h”. Esto es algo que sabemos todos; si no se hace eso, el compilador (mejor dicho, el “linker”) se queja con un “unresolved external symbol” o parecido. Esto es así y nada podemos hacer, el compilador necesita tener visible el código de la clase para poderla especializar. No hay nada que hacer, estamos 100% seguros de ello y tendremos que conformarnos con incluir cientos de líneas de código en cada fichero de cabecera, con la consiguiente ralentización de la compilación.

¿O no? ¿Hay alguna manera…? ¿Igual…?

Pues sí, hay una forma de evitar poner todo el código en el “.h” y tenerlo en nuestros queridos “.cpp”.

Ahora bien, hay que cumplir una premisa: conocemos los tipos de datos a los que se especializará la clase y el template no es más que una ayuda para evitar reescribir código, no una generalización completa. Además, el “ahorro” de tiempo de compilación será sólo visible si estamos creando una biblioteca (ya que la compilaremos una sola vez, mientras que habrán muchos proyectos que la usen). ¡Al código!

Imaginemos que el fichero “myclass.h” contiene a una clase sencilla:

template< typename T > class MyClass {
public:
MyClass();
~MyClass();

void setA(T a) { _a = a; }
T getA() { return _a; }

void calc();

protected:
T _a;
};

Ahora bien, “myclass.cpp”:

template< typename T > MyClass< T >::MyClass() {
std::cout << \\"MyClass()\\" << std::endl;
_a = static_cast< T >(0);
}

template< typename T > MyClass< T >::~MyClass() {
std::cout << \\"~MyClass()\\" << std::endl;
}

template< typename T > void MyClass< T >::calc() {
std::cout << \\"calc()\\" << std::endl;
// a lot of useful code...
_a = _a + _a;
}

Como vemos, la clase MyClass está pensada para operar con números, pero el método calc() es muy grande ;), y no conviene ponerlo en ".h", además, somos un poco cerrados y no queremos que nadie sepa cómo se inicializan los objetos de nuestra clase. Todo eso debe estar en un ".cpp".

Pero volvemos al problema de especialización. Alguien incluye el fichero "myclass.h", enlaza con nuestra súper biblioteca, especializa e instancia la clase con parámetro entero, y usa el método calc(). Cuando compile obtendrá un hermoso "unresolved external symbol" y se quedará con cara de circunstancias pensando en nuestra querida madre y en el paquete de cereales del que sacamos nuestro título.

Ahora, el meollo del post: ¿cómo resolverlo?. La solución pasa por la forma en la que los compiladores funcionan. Éstos generan tablas con todas las declaraciones de métodos, así como con sus definiciones (código) cuando las encuentran. Esto es, rudimentariamente hablando, una biblioteca: una gran tabla de definiciones.

Vayamos al programa que usa la clase. Cuando llamamos al método calc(), el compilador primero busca si está declarado (y lo está, dado que tenemos el ".h"), y cuando vaya a generar el ejecutable, buscará el código correspondiente, el cual no encuentra porque la especialización sólo ha podido tener lugar con las definiciones que había en el ".h", y la biblioteca (que sí conoce la definición) no lo tiene porque no se especializó.

Dado que sabemos los tipos de datos con los que trabajaremos (recordemos que era la premisa de la solución), podemos forzar al compilador a generar el código que necesitamos dentro de la biblioteca, especializando dichas clases en el mismo fichero ".cpp" de la clase.

template< typename T > void __MyClass_specialization__() {
MyClass< T > foo;
foo.calc();
}

void __all_MyClass_specializations__() {
__MyClass_specialization__< int >();
__MyClass_specialization__< short >();
__MyClass_specialization__< long >();
__MyClass_specialization__< float >();
__MyClass_specialization__< double >();
}

¡Y listo! ahora cualquiera de las especializaciones estará disponible, la compilación de las aplicaciones será rápida (recordemos que calc() es un método súper complicado y largo) y no habremos tenido que reescribir varias veces el mismo código.

Un par de comentarios finales de esta solución. Es interesante ver que no hace falta llamar a la función __all_MyClass_specializations__(); el simple hecho de que esté presente ya hace que el compilador genere el código necesario, por lo que los métodos que se llamen dentro de la función mágica __MyClass_specialization__() no tienen que tener coherencia entre sí, ni parámetros válidos ni estar completamente inicializados. Lo importante es llamar a todos los métodos que no estén definidos en el ".h" sino en el ".cpp", salvando aquéllos que sean llamados por otros (por ejemplo, si el método _long_calc() es llamado desde calc(), el primero no hay que ponerlo en la función mágica). El constructor se crea al declarar el objeto "foo" (si hay varios constructores habrá que crear varios objetos, uno por constructor), y el destructor se especializa también dado que el objeto no es un dinámico.

Finalmente, dado que una clase puede tener muchos métodos, la función mágica es un template también para simplificar la actualización de la clase y la inclusión de nuevos tipos de datos.

Mi primera aplicación en iOS y lo que ello ha implicado (Parte I)

Como hobby durante el verano he decido meterme a desarrollador de aplicaciones móviles, específicamente para iOS. Me parece una plataforma estupenda, no sólo como usuario, sino por lo que he visto de sus APIs y lo que muchos han hecho. Así que quería subirme al carro.

Después de consultar para arriba y para abajo los temas de impuestos acá en España, y dado mi status de residente, he decidido (por ahora) tenerlo como lo que ya dije, un simple hobby, sin cobrar por mis aplicaciones; no se alegren mucho que en cuanto pueda ya colaré alguna que otra paga ;).

Lo primero fue registrarme al programa de desarrollo. Los dos primeros pasos ya los tenía hechos: tenía mi cuenta Apple ID (con la que compro en la App Store y me registro en los demás servicios de Apple), y estaba dado de alta como desarrollador (en su versión light, gratuita). El tercer paso fue comprar el programa de desarrollo para iOS, 79€ (que, lo crean o no, es casi casi el cambio desde el dólar, al menos en estos días), das tus datos, en mi caso usé la misma tarjeta de crédito que la que tengo asociada a la App Store, pago y a esperar a que me den de alta.

El proceso fue rápido, salvando un pequeño problema: las tildes en la dirección de mi piso se trastocaron en el proceso y llegaron como caracteres erróneos, y el proceso automático de validación de mi tarjeta se frenó en seco. Esos datos resulta que no se pueden modificar directamente sino que hay que solicitar un cambio. Después de escribir al servicio de atención al cliente, esperé. A los dos días recibo una llamada directa desde California, donde un chico me comenta que se procederá a hacer el cambio. La llamada era para explicar personalmente lo que había pasado, pedir disculpas por las molestias, y decirme que me mandarían un email con las instrucciones, incluida una autorización de modificación de datos. Simplemente debía responder sobre el email y autorizarles a cambiar los datos. Después de 10min de haber hablado con él me llega el correo, el cual contesto inmediatamente. 10min después ya tenía acceso al programa de desarrollo. Aunque tuve este problema durante el registro, el servicio de atención al cliente de Apple hizo honor a su fama una vez más, y el detalle de la llamada la verdad que me emocionó.

Bueno, una vez en casa descargo todo: entorno de desarrollo (4.2 beta 2), sistema operativo (iOS 5 beta 2), iTunes correspondiente (10.5 beta 2). Claro, yo estoy de fiebrúo que quería probarlo todo. Ya he tomado nota de que, si me dedico más en serio a esto, comprar un dispositivo adicional, un iPod Touch, para tener las versiones beta, e incluso dejarlo en el portátil, desde el que no sincronizo nada, porque, como buenas betas, tienen sus fallos, bugs, no necesariamente están todavía optimizadas. Gracias a Dios no sufrí ninguna pérdida de datos, pero fue arriesgado. En fin, lo instalé todo y comencé a desarrollar.

La primera aplicación la elegí por sencillez, dado que necesito una rampa de aprendizaje y no tengo presiones de proyectos (que por otra parte no vendrían mal). La aplicación en cuestión es una adaptación de la cámara del iPhone, para poder tomar fotos y ver exactamente cómo quedarán cuando se usen como fondo de pantalla. La cámara toma con una relación de aspecto de 4:3, mientras que la pantalla es 3:2, por lo que la foto que uno toma no siempre se ve bien como fondo: bordes negros, la cara de alguien es tapada por la interfaz del reloj, etc. Lo que hice fue simular dicha interfaz en la cámara (usando un overlay). Aunque no está disponible en la App Store todavía, pueden ver algunos screenshots en la página de la aplicación.

Creo, opinión personal, que la parte más complicada de comenzar a programar en iOS es el tema del MVC (Model View Controller), que es bastante confuso al principio. No ahondaré acá en ello en estos momentos, así que remito a la bibliografía para más información. Una vez superada esta barrera, el resto es lo típico de cualquier API nueva: el nombre de las funciones, buscar cómo se comunica esto con aquello. El lenguaje de programación no ha sido problema: Objective-C no es un conocido C++ o un Java, es bastante diferente en cuanto a algunas sintaxis (métodos, declaración de clases, destructores, gestión de memoria), pero es un buen hijo de C y se aprende rápido; de hecho, una cosa que me ha gustado mucho es lo autodocumentado que queda el código gracias a métodos como

UIImage *image = [[UIImage alloc] initWithCGImage:imageOr.CGImage scale:imageOr.scale orientation:UIImageOrientationRight];

Esta aplicación tuvo una componente artística que prácticamente era una tercera parte del proyecto: simular la pantalla de bloqueo. Ahora bien, es cierto que el preparar los recursos de una aplicación siempre conlleva un tiempo no despreciable, y se nota cuándo un desarrollador no sabe pelearse con un programa de diseño o no tiene a un diseñador en el equipo. Bueno, el caso es que dupliqué la interfaz, con cambios suficientes para adaptarla a ser una “cámara”, y cambiando pequeños detalles para no confundir al usuario. El icono de la aplicación y los fondos también me llevaron su tiempo, pero, repito, eso es necesario: una aplicación debe verse bien, no sólo funcionar bien (y no entraré en una pelea de lo que pasa con muchos proyectos open source… pero lo dejo caer :p).

Probar la aplicación en el móvil no fue mayor problema: hay que bajarse los certificados con los que se firmarán las aplicaciones y registrar el dispositivo como “de desarrollo”. Una vez hecho esto, el propio Xcode sube la aplicación, ejecutando a la par el depurador. Sugiero que, si no se está buscando un fallo específico, sino probando la aplicación para ver cómo va la cosa, se suba, al menos, con el depurador desactivado, ya que ralentiza bastante la carga de las vistas (iniciar aplicación con el debugger: 10seg, sin el debugger: casi instantáneo).

Una vez terminada la aplicación (un par de semanas, trabajando 4 tardes a la semana y un par de madrugones), hay que subirla. Acá sufrí un poco más, porque el proceso está documentado bien para la versión 3.2 de Xcode, pero con la 4.2 algunas cosas han cambiado de lugar y no han actualizado todo. Los pasos, más o menos, a seguir, son: registrarse en el iTunes Connect, registrar la aplicación (luego una nota sobre el Bundle ID y el nombre de la aplicación), subir los iconos, screenshots, descripción, etc. Una vez hecho esto, la aplicación pasa a estar en estado “Waiting for upload”. En este momento, vamos a Xcode, la compilamos en modo “distribution” (que creamos nosotros como una copia del modo “release” pero firmando con el certificado de distribución en vez del de desarrollo), y la “archivamos”. En el Organizer del Xcode validamos que hemos firmado bien la aplicación y que su fichero de información es correcto (es un proceso automático), y la subimos para revisión. Más no puedo contar porque ahí estoy ahora… esperando.

Respecto al Bundle ID, es importante saber que debe ser algo como: 23487BJ343.com.nosotros.aplicacion, donde 23487BJ343 es nuestro App ID que se nos ha asignado en el Provisioning Portal (donde obtenemos los certificados). Este Bundle ID debe ser idéntico tanto en el Info.plist de la aplicación, como en la página de iTunes Connect; es, en pocas palabras, el que identifica a nuestra aplicación y el que permite que cuando subamos una actualización los usuarios sean informados de ello. Del nombre de la aplicación, una vez asignado, si se borra la aplicación, no se puede volver a usar. Así que escoger bien.

Por último, lo del “distribution” y el “archivar”, estos valores se controlan desde los “esquemas”. Sugiero crear dos básicamente: el de desarrollo (depuración), que es el que se creará cuando creemos el proyecto, y uno en modo “release” con el “archivar” en modo “distribution”. De esta forma, se tiene un esquema con el cual probar al final del ciclo de desarrollo y probar la aplicación en su forma final. Nota: el certificado con el que se firma en modo “distribution” no sirve para ejecutar la aplicación en nuestro terminal de desarrollo, así que asignar este certificado sólo al “archivar”, no al “ejecutar”.

Bueno, un post algo largo y con muchas palabras, pero que creo que resume lo que ha sido mi experiencia en este primer desarrollo, hasta ahora bastante satisfactoria. En una siguiente entrega hablaré más de aspectos técnicos y, si ya me han aprobado la aplicación, daré la noticia también. ¡Hasta luego!