Otra protesta
Cuando te pones a trabajar esperas de forma inconsciente que la única razón que ralentice tu trabajo sea tu propia estupidez, desde luego no esperas que la cabezonería o la estupidez de otros detengan tu progreso (bueno, al menos no esperas que lo detengan tanto).
Mi conocimiento de programación en Windows con lenguajes tipo C es cercano a mínimo, apenas conozco lo que se puede “portar” desde la programación en Linux/Unix, ignorando (casi) todo lo referente a la API Win32 o las (caprichosas) ampliaciones de Visual C++.
Esta mañana estaba intentando una cosa sencilla: una DLL que fuera utilizada por un ejecutable. ¿Sencillo no? Pues yo debo ser estúpido.
El caso es que al linker (vinculador, como ellos lo llaman) de Visual C++ hay que indicarle que símbolos quieres exportar en una DLL con una ampliación al lenguaje que se tiene que anteponer al nombre del símbolo (no lo antepongas al tipo del símbolo, que no funciona) con el precioso nombre de __declspec(dllexport) (al menos utilizan dos subrayados delante de su ampliación, al parecer antes se pasaban por donde ya sabéis las convenciones del lenguaje). No he perdido mucho tiempo buscando esto, pero es un pequeño escollo molesto.
Lo peor ha venido luego, cuando he intentado utilizar la DLL desde el ejecutable (la parte de generar la DLL y su archivo de “importación” ya la había practicado estos días pasados, por lo que no ha sido problema). Tenía un método en una clase que devolvía un puntero a una instancia de esa misma clase (para entendernos menos, un método de factoría) en la que se suponía que el código llamante debía liberar los recursos creados por el método. Ahí estaba yo con mi delete (lo único que verdaderamente merece la pena de C++ sobre C: el new y el delete), pero al ejecutar el código (de hecho ese código era una prueba de unidad de la clase) me suelta no se que de “debug time assertion” y el programa obviamente casca.
Para hacer una historia larga corta, después de un tiempo de pensar (y leer esos mensajes de error) se me ha ocurrido que Windows debe tener la feliz idea de separar los espacios de memoria de las DLLs y de los ejecutables, por lo que no puedes reservar memoria en un lado y liberarla desde el otro.
Alrededor del Visual C++ 5.0 se debieron dar cuenta que esa “característica” hacía más o menos estúpido utilizar clases importadas desde bibliotecas y se inventaron la palabreja que he puesto más arriba junto con otra palabreja denominada __declspec(dllimport) que tienes que poner en el mismo sitio que la anterior (pero no las dos a la vez) al importar el código de la cabecera (tiene cierto sentido: dllexport para exportar y dllimport para importar). Antes de Visual C++ 5.0 los pobrecillos se las veían y se las deseaban para hacerlo.
Lo que no entiendo es una tercera condición (que supongo que tiene sus razones, pero no me voy a poner a buscarlas) que es que tu método destructor esté declarado como virtual. Por mi no hay problema, mientras que funcione.
Lo que no me parece normal es que te tengas que montarte un castillo de ifdefs y defines para poder utilizar la misma cabecera cuando compilas la biblioteca y cuando la utilizas (con nivel adicional para distinguir entre Windows y otros sistemas operativos, que no entienden esta palabra y exportan todo gratis y sin problemas).
De lo que luego me he enterado (tarde, como siempre) es que Visual Studio te crea ese castillo con su macro bien definida cuando le pides un proyecto para una librería, cosa que yo nunca pido porque me gusta mantener el control de mi código y que sea portable, por lo que siempre pido al asistente un proyecto vacío. Quizá utilizar esos proyectos vacios sea mi problema.
Entre todo este día de lucha contra la máquina una agradable sorpresa: CppUnit, una muy buena versión para C++ de JUnit, que como todos sus hermanos *Unit hacen que escribir pruebas sea un pequeño placer en vez de un pita.
Estoy seguro que con Cygwin y GCC lo habría tenido todo más fácil, pero Windows es el futuro ¿no?


24 de Marzo de 2006 a las 10:22
¿El futuro? ¿Quién te ha dicho eso?