Primero, la diferencia entre
librería dinámica y
librería estática:
Las librerías estáticas quedan incluidas en el ejecutable, mientras las dinámicas son ficheros externos, con lo que el tamaño de la aplicación (nuestro ejecutable) es mayor en el primer caso que en el segundo. Esto puede ser de capital importancia en aplicaciones muy grandes, ya que el ejecutable debe ser cargado en memoria de una sola vez
Las librerías dinámicas son ficheros independientes que pueden ser invocados desde cualquier ejecutable, de modo que su funcionalidad puede ser compartida por varios ejecutables. Esto significa que solo se necesita una copia de cada fichero de librería (DLL) en el Sistema. Esta característica constituye la razón principal de su utilización, y es también origen de algunos inconvenientes, principalmente en sistemas como Windows en los que existen centenares de ellas.
Referencia:
Tipos de librerías en C y C++Construcción de una DLLCuando se construye un fuente que será compilado para producir una DLL, los objetos (funciones y clases) que deban ser accesibles desde otros ejecutables, se denominan
exportables, también
callbacks si son funciones, en atención a una denominación muy usual en la literatura inglesa ("callback functions"). Esta circunstancia debe ser conocida por el compilador, por lo que es necesario especificar qué recursos se declaran "exportables"; además debe instruirse al "linker" para que genere una librería dinámica en vez de un ejecutable normal.
En los compiladores para la plataforma Windows existen varias formas para declarar una función o recurso como exportable, pero aquí veremos uno de los más simples y directos, el especificador dllexport.
Especificador dllexportLos recursos "exportables" pueden ser también declarados mediante el especificador __declspec(dllexport).
Sintaxis:Existen dos formas:
__declspec(dllexport) valor-devuelto funcion (argumentos);
class __declspec(dllexport) nombre-de-clase;
__declspec(dllexport) tipo-de-dato nombre-de-variable;
Ejemplosextern "C" __declspec(dllexport) double MayorValor(double, double);
class __declspec(dllexport) A { /* ... */ };
__declspec(dllexport) int x;
Referencia:
Crear una librería dinámicaAhora sí, a la acción. Supongamos que tenemos tres archivos, así:
Archivo | Descripción |
---|
ClaseA.hpp | Cabecera de la clase "ClaseA" |
ClaseA.cpp | Archivo fuente con la definición de la clase "ClaseA" |
main.cpp | Código del ejecutable |
Donde la
ClaseA estará contenida en la librería que queremos crear. Miremos el código de cada una de los archivos:
ClaseA.hpp |
#ifndef CLASEA_HPP #define CLASEA_HPP
class __declspec(dllexport) ClaseA { public: ClaseA(int iId = 0);
int id() const; void id(const int &iId);
protected: int _iId; };
__declspec(dllexport) ClaseA * nuevaReferencia(int iId = 0); __declspec(dllexport) void eliminarReferencia(ClaseA * pClaseA);
// Definición de métodos inline inline int ClaseA::id() const { return _iId; }
inline void ClaseA::id(const int &iId) { _iId = iId; }
#endif
|
ClaseA.cpp |
#include<ClaseA.hpp>
ClaseA::ClaseA(int iId) { _iId = iId; }
ClaseA * nuevaReferencia(int iId) { return new ClaseA(iId); }
void eliminarReferencia(ClaseA * pClaseA) { delete pClaseA; } |
main.cpp |
#include<iostream> #include<ClaseA.hpp>
using std::cout; using std::endl;
int main(int argc, char **argv) { cout << "Ejemplo de creación de librerías dinámicas" << endl;
ClaseA * pClaseA = nuevaReferencia();
cout << "id:\t" << pClaseA->id() << endl;
pClaseA->id(3);
cout << "id:\t" << pClaseA->id() << endl;
eliminarReferencia(pClaseA);
return 0; }
|
Tenemos una clase llamada
ClaseA que estará contenida en nuestra librería. Y un archivo
main.cpp que contiene el código de nuestro ejecutable.
Ahora creamos el archivo de configuración de CMake:
CMakeLists.txt |
project(libreria) cmake_minimum_required(VERSION 2.8)
include_directories( ${CMAKE_BINARY_DIR} . )
add_library(Core SHARED ClaseA.hpp ClaseA.cpp )
add_executable(main main.cpp)
target_link_libraries(main Core)
|
Creamos una librería dinamica (
SHARED) llamada
Core, y un ejecutable llamado
main.
Miremos que no es necesario declarar como
exportables los métodos y miembros de la clase, ya que la clase es
exportable.
En el momento de construcción de la librería ciertos identificadores deben ser declarados
dllexport. Pero para poder usar esta en la construcción de otras librerías o ejecutables es necesario importarlos. Para esto usamos el especificador
dllimport. Para esto es necesario que en el momento de construcción el especificador
dllexport sea cambiado por
dllimport. Para automatizar esto, usaremos un cuarto archivo
LibExport.hpp:
LibExport.hpp |
---|
#ifndef LIBEXPORT_HPP #define LIBEXPORT_HPP
#ifdef _WIN32 # ifdef Core_EXPORTS # define EXPORT __declspec( dllexport ) # else # define EXPORT __declspec( dllimport ) # endif #else # define EXPORT #endif
#endif
|
En el momento en que se está construyendo la librería, CMake define un macro llamado <nombre de la librería>_EXPORTS. Lo que hacemos es aprovechar esto y decir, que cuando se esté construyendo la librería,
EXPORT adquiera el valor de
__declspec( dllexport ), de lo contrario tendrá el valor de
__declspec( dllimport ) (siempre y cuando el sistema operativo sea Windows). Luego de esto, modificamos
ClaseA.hpp para que soporte tanto
dllexport como
dllimport:
ClaseA.hpp |
---|
#ifndef CLASEA_HPP #define CLASEA_HPP
#include<libexport.hpp>
class EXPORT ClaseA { public: ClaseA(int iId = 0);
int id() const; void id(const int &iId);
protected: int _iId; };
EXPORT ClaseA * nuevaReferencia(int iId = 0); EXPORT void eliminarReferencia(ClaseA * pClaseA);
// Definición de métodos inline inline int ClaseA::id() const { return _iId; }
inline void ClaseA::id(const int &iId) { _iId = iId; }
#endif
|
Cuando compilamos el proyecto, podemos ver que se crean tres archivos;
main.exe,
Core.dll,
Core.obj. Esta ultima una librería estática clásica que sirve como índice o diccionario de la dinámica.