Cómo llamar a una función de una biblioteca compartida?
-
22-09-2019 - |
Pregunta
¿Cuál es la forma más fácil y más seguro para llamar a una función de una biblioteca / DLL compartida? Me interesa sobre todo en hacer esto en Linux, pero sería mejor si hubiera una manera independiente de la plataforma.
Podría alguien proporcionar el código ejemplo para mostrar cómo hacer el siguiente trabajo, en el que el usuario ha compilado su propia versión de foo
en una biblioteca compartida?
// function prototype, implementation loaded at runtime:
std::string foo(const std::string);
int main(int argc, char** argv) {
LoadLibrary(argv[1]); // loads library implementing foo
std::cout << "Result: " << foo("test");
return 0;
}
Por cierto, sé cómo compilar la librería compartida (foo.so
), sólo necesito saber una manera fácil de cargar en tiempo de ejecución.
Solución
Nota: está de paso C ++ objetos (en este caso las cadenas STL) alrededor de las llamadas bibliotecas. Hay no estándar C ++ ABI a este nivel , así que o tratar de evitar el paso C ++ objetos alrededor, o asegurar que tanto su biblioteca y su programa han sido construidos con el mismo compilador (idealmente el mismo compilador en la misma máquina, para evitar sorpresas relacionadas con la configuración sutiles.)
No se olvide de declarar sus métodos exportados extern "C"
dentro de su código de la biblioteca.
Lo anterior después de haber sido dicho, aquí es algún código implementación de lo que ha dicho que quiere lograr
typedef std::string (*foo_t)(const std::string);
foo_t foo = NULL;
...
# ifdef _WIN32
HMODULE hDLL = ::LoadLibrary(szMyLib);
if (!hDll) { /*error*/ }
foo = (foo_t)::GetProcAddress(hDLL, "foo");
# else
void *pLib = ::dlopen(szMyLib, RTLD_LAZY);
if (!pLib) { /*error*/ }
foo = (foo_t)::dlsym(pLib, "foo");
# endif
if (!foo) { /*error*/ }
...
foo("bar");
...
# ifdef _WIN32
::FreeLibrary(hDLL);
# else
::dlclose(pLib);
# endif
Puede Resumen Este más
#ifdef _WIN32
#include <windows.h>
typedef HANDLE my_lib_t;
#else
#include <dlfcn.h>
typedef void* my_lib_t;
#endif
my_lib_t MyLoadLib(const char* szMyLib) {
# ifdef _WIN32
return ::LoadLibraryA(szMyLib);
# else //_WIN32
return ::dlopen(szMyLib, RTLD_LAZY);
# endif //_WIN32
}
void MyUnloadLib(my_lib_t hMyLib) {
# ifdef _WIN32
return ::FreeLibrary(hMyLib);
# else //_WIN32
return ::dlclose(hMyLib);
# endif //_WIN32
}
void* MyLoadProc(my_lib_t hMyLib, const char* szMyProc) {
# ifdef _WIN32
return ::GetProcAddress(hMyLib, szMyProc);
# else //_WIN32
return ::dlsym(hMyLib, szMyProc);
# endif //_WIN32
}
typedef std::string (*foo_t)(const std::string);
typedef int (*bar_t)(int);
my_lib_t hMyLib = NULL;
foo_t foo = NULL;
bar_t bar = NULL;
...
if (!(hMyLib = ::MyLoadLib(szMyLib)) { /*error*/ }
if (!(foo = (foo_t)::MyLoadProc(hMyLib, "foo")) { /*error*/ }
if (!(bar = (bar_t)::MyLoadProc(hMyLib, "bar")) { /*error*/ }
...
foo("bar");
bar(7);
...
::MyUnloadLib(hMyLib);
Otros consejos
LoadLibrary es una función de Windows para cargar archivos DLL. Puede comprobar si el símbolo de la existencia con GetProcAddress . En Linux / Unix desea dlopen / dlsym . Para hacer esto en varias plataformas, que podría escribir una función que llama a cualquiera de estos métodos que utilizan pre-procesador, así, algo como:
int loadlibrary(char* library)
{
#ifdef _WIN32
/* do windows code */
#endif
#ifdef _LINUX
/* do linux code */
#endif
}
Esta es una forma de lograr este tipo de cosas. También puede hacerlo mediante la inclusión de un encabezado diferente en su propio árbol de código fuente para las implementaciones específicas de la plataforma de funciones. Esta es probablemente una mejor manera. En cualquier caso, la idea es hacer abstracción de la API subyacente.
En Linux es necesario utilizar dlsym . Ver un ejemplo al final de la página. En Ventana:. GetProcAddress