Bibliotecas dinámicas en KDE 4

Versión corta: si desarrollas una biblioteca dinámica para KDE, debes marcar tus clases con la macro KNOMBREBIBLIOTECA_EXPORT (de forma que su definición quede como “class KNOMBREBIBLIOTECA_EXPORT nombreClase: public claseBase {…“), y definir esa macro en un archivo de cabecera knombrebiblioteca_export.h (los nombres no tienen por qué ser necesariamente así, pero por seguir la práctica común) con un contenido adaptado a la biblioteca en cuestión. Véase como ejemplo cualquiera de los archivos include/kloquesea_export.h de KDE 4, así como los archivos de cabecera de las clases que los usan.

Para quien quiera aburrirse con la versión larga y detallada de mis aventuras y desventuras hasta llegar a tan sencilla conclusión, a continuación viene la versión larga.

“El miedo es el camino hacia el Lado Oscuro. El miedo lleva a la ira, la ira lleva al odio, el odio lleva al sufrimiento. Veo mucho miedo en ti.” Con estas palabras el maestro Yoda expresaba su preocupación ante el entrenamiento en los caminos de la Fuerza del joven Anakin Skywalker. Pues bien, si hoy el maestro Yoda me hubiese examinado a mí, miedo no habría encontrado. Ahora que de ira, odio y sufrimiento iba servido.

Este cúmulo de sensaciones tan poco agradables eran debidas al cansancio y desesperación acumuladas durante las ¿5? horas que había pasado intentando averiguar por qué cuando convertí la biblioteca KTutorial de estática a dinámica el enlazado desde una aplicación de prueba fallaba (una larga retahíla de “undefined reference to `vtable for NombreClase::metodo()‘” en los lugares de la aplicación de prueba que se usaba la biblioteca).

Pero, ¿por qué? Inicialmente pensé que tal vez habría que hacer algo más que añadir la opción SHARED en el objetivo para crear la biblioteca (“kde4_add_library(ktutorial SHARED ${ktutorial_LIB_SRCS})“) y por ese motivo fallaba. Sin embargo, el amigo Google no me devolvía nada concluyente a este respecto. Según parecía, con añadir esa opción ya era suficiente para crear la biblioteca.

Así que busqué en las kdelibs algún archivo de CMake que generase una biblioteca dinámica, para ver si había algo que se me escapase. La mayoría eran archivos “grandes”, aunque buscando, llegué al archivo de CMake para kdelibs/kde3support/kunittest. Generaba una biblioteca y un ejecutable que enlazaba con ésta, era pequeño y fácil de entender. ¡Excelente!

O no tanto. Porque resulta que el archivo de CMake tenía lo mismo que los que utilizaba yo. Tras varias pruebas, entre ellas aislar esa biblioteca y generarla de 0 (por si acaso CMake utilizaba objetivos heredados de los directorios padre) y ver que funcionaba, supuse que el problema no estaría en los objetivos de CMake. Pero, ¿dónde entonces?

Pensé que el problema podía residir en que la biblioteca dinámica enlazaba con una biblioteca estática temporal. Esta biblioteca estática simplemente contiene las clases de la vista, que están separadas del modelo en un subdirectorio. Me parecía extraño que fuese el problema, ya que anteriormente había trabajado con un esquema similar, aunque en una biblioteca que no era de KDE, y no presentaba problemas. Pero como no sabía dónde buscar, intenté por ese lado. Y como era de esperar… seguía sin funcionar.

Puestos a dar palos de ciego, decidí enlazar la biblioteca no con una aplicación de verdad, sino con la mínima expresión de un programa de C++, a ver qué pasaba. Así que hice simplemente un método main que crease un objeto de una clase de la biblioteca. Enlacé y… ¡funcionó! Por probar, cree un objeto de otra clase, y enlacé de nuevo. Y esta vez, fallaba al enlazar con el nuevo objeto. Ni que decir tiene que miraba incrédulo a la pantalla, esperando que mi querido ordenador sacase un diálogo llamándome inocente y diciéndome que ya paraba con la broma.

Pero en vista de que eso no ocurría, decidí mirar dónde estaba la diferencia entre ambas clases, para que una enlazase y la otra no. Y la diferencia era que la que enlazaba era una clase normal, mientras que la que no, era derivada de QObject. Me encanta Qt, adoro todas sus características, me emociono sólo de pensar en señales y ranuras. Pero en ese instante quise que David el gnomo acabase con todos los trolls (por si alguien no sabe a qué me refiero, Qt es obra de Trolltech, a cuyos empleados se les conoce como “los trolls”. Sí, es el peor intento de chiste que leíste en mucho tiempo, lo sé. Pero qué quieres, estoy cansado 😛 ).

Con todo esto, decidí volver sobre el código de kunittest. El sistema de construcción era igual al que yo usaba, por lo que la diferencia tenía que estar en el código de las clases. Lo primero que advertí era que dichas clases incluían en los cpp los archivos moc generados por la herramienta de Qt para hacer su magia.

No me constaba que en KDE 4 fuese necesario (aunque, hasta donde sé, con KDE 3 sí), pero dado que a esas alturas si alguien me decía que mi problema se solucionaría dando tres vueltas sobre mi mismo y diciendo “Oh todopoderoso enlazador yo te invoco” en arameo antes de construir la biblioteca yo lo intentaría, pues probé incluyendo los archivos moc.

Jamás adivinaríais qué pasó. Exacto, siguió sin funcionar. A punto de subirme a la silla gritando “Je suis Napoléon!”, vi algo que me llamó la antención en un archivo .h de kunittest: “class KUNITTEST_EXPORT Runner : public QObject“. Tenía que ser eso. Miré qué valor tenía la macro, y cuando se construía la biblioteca éste era “KDE_EXPORT“. “Eso suena a exportar símbolos de la biblioteca”, pensé.

Así que hice una prueba rápida y añadí dicha macro en la clase que utilizaba en mi main simplificado y que no enlazaba. Y un coro celestial bajó a arroparme entre cánticos de júbilo y felicidad. ¡¡¡AL FIN!!!

Tras esto, hice los cambios necesarios en la biblioteca para que todas las clases derivadas directa o indirectamente de QObject utilizasen dicha macro, y todo funcionó como era de esperar 🙂

Realmente no estoy seguro de la explicación técnica de por qué ocurre esto. Mis conocimientos sobre enlazado de C++ en Linux no llegan a tanto, y tampoco tengo tiempo de ponerme a investigarlo. Pero sí se la regla de oro: si haces una biblioteca dinámica en KDE, tienes que marcar tus clases públicas para que se pueda enlazar con ellas. Para ver cómo deben marcarse, a la versión corta del artículo me remito 😉

Una vez arreglado esto, a seguir con los objetivos de instalación de CMake para poder usar la internacionalización.

Y tras este artículo es cuando el anterior pasa a ser claramente anacrónico, ya que no pude dedicarme la tarde entera a investigar lo narrado en él, cuando en éste se ve cómo estuve haciendo otra cosa distinta 😉

Advertisements

Tags: ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: