Pergunta

Qual é a maneira mais fácil e segura de chamar uma função de uma biblioteca / DLL compartilhada? Estou principalmente interessado em fazer isso no Linux, mas seria melhor se houvesse uma maneira independente da plataforma.

Alguém poderia fornecer código de exemplo para mostrar como fazer o seguinte trabalho, onde o usuário compilou sua própria versão de foo em uma biblioteca compartilhada?

// 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;
}

Btw, eu sei como compilar a lib compartilhada (foo.so), Eu só preciso conhecer uma maneira fácil de carregá -lo em tempo de execução.

Foi útil?

Solução

NOTA: Você está passando por objetos C ++ (neste caso STL Strings) em torno das chamadas da biblioteca. Há Sem C ++ padrão Abi Nesse nível, Portanto, tente evitar passar objetos C ++ por perto ou garantir que sua biblioteca e seu programa tenham sido construídos com o mesmo compilador (idealmente o mesmo compilador na mesma máquina, para evitar surpresas sutis relacionadas à configuração.)

Não se esqueça de Declare seus métodos exportados extern "C" dentro do seu código da biblioteca.

O acima foi dito, aqui está algum código implementando o que você disse que deseja alcançar:

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

Você pode Resumo mais isso:

#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);

Outras dicas

Loadlibrary é uma função do Windows para carregar DLLs. Você pode verificar a existência de símbolos com GetProcaddress. No Linux/Unix, você quer Dlopen/dlsym. Para fazer isso na plataforma cruzada, você pode escrever uma função que chama qualquer um desses métodos usando o pré-processador, então, algo como:

int loadlibrary(char* library)
{
#ifdef _WIN32
    /* do windows code */

#endif
#ifdef _LINUX
    /* do linux code */

#endif
}

Esta é uma maneira de alcançar esse tipo de coisa. Você também pode fazer isso, incluindo um cabeçalho diferente em sua própria árvore de origem para implementações específicas de plataforma de funções. Provavelmente é uma maneira melhor. Em ambos os casos, a idéia é abstrair da API subjacente.

No Linux, você precisa usar dlsym. Veja um exemplo no final da página. Na janela: GetProcaddress.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top