Как вызвать функцию из общей библиотеки? - PullRequest
15 голосов
/ 29 марта 2010

Какой самый простой и безопасный способ вызова функции из общей библиотеки / dll? Я в основном заинтересован в том, чтобы делать это на Linux, но было бы лучше, если бы существовал независимый от платформы способ.

Может ли кто-нибудь предоставить пример кода, чтобы показать, как заставить следующую работу работать, когда пользователь скомпилировал свою собственную версию foo в общую библиотеку?

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

Кстати, я знаю, как скомпилировать общую библиотеку (foo.so), мне просто нужно знать простой способ загрузить ее во время выполнения.

Ответы [ 3 ]

26 голосов
/ 29 марта 2010

ПРИМЕЧАНИЕ: Вы передаете объекты C ++ (в данном случае строки STL) вокруг библиотечных вызовов. На этом уровне нет стандартного C ++ ABI , поэтому либо попытайтесь избежать передачи объектов C ++, либо убедитесь, что ваша библиотека и ваша программа были собраны с помощью одного и того же компилятора (в идеале один и тот же компилятор на той же машине, чтобы избежать каких-либо тонких сюрпризов, связанных с конфигурацией.)

Не забудьте объявить экспортированные методы extern "C" внутри кода вашей библиотеки.

Выше было сказано, вот некоторый код, реализующий то, что, как вы сказали, вы хотите достичь :

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

Вы можете абстрагировать это дальше :

#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);
1 голос
/ 29 марта 2010

LoadLibrary - это функция Windows для загрузки DLL. Вы можете проверить существование символа с помощью GetProcAddress . В Linux / Unix вы хотите dlopen / dlsym . Чтобы сделать это в кроссплатформенном режиме, вы могли бы написать функцию, которая вызывает любой из этих методов с использованием препроцессора, например что-то вроде:

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

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

#endif
}

Это один из способов достижения такого рода вещей. Вы также можете сделать это, включив другой заголовок в свое собственное дерево исходных кодов для конкретных реализаций функций платформы. Это, вероятно, лучший способ. В любом случае идея состоит в том, чтобы абстрагироваться от базового API.

0 голосов
/ 29 марта 2010

В Linux вам нужно использовать dlsym . Смотрите пример в конце страницы. В окне: GetProcAddress .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...