سؤال

ما هي الأسهل والأكثر أمانًا لاستدعاء وظيفة من مكتبة / 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;
}

راجع للشغل ، أعرف كيفية تجميع lib المشترك (foo.so) ، أنا فقط بحاجة إلى معرفة طريقة سهلة لتحميله في وقت التشغيل.

هل كانت مفيدة؟

المحلول

ملاحظة: أنت تمرر كائنات C ++ (في هذه الحالة سلاسل STL) حول مكالمات المكتبة. هنالك لا يوجد C ++ قياسي أبي على هذا المستوى, ، لذا ، تحاول إما تجنب تمرير كائنات 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);

نصائح أخرى

مكتبة التحميل هي وظيفة Windows لتحميل DLLs. يمكنك التحقق من وجود الرمز مع getProcaddress. على Linux/Unix تريد dlopen/DLSYM. للقيام بذلك في منصة متقاطعة ، يمكنك كتابة وظيفة تستدعي أيًا من هذه الطرق باستخدام المعالجة المسبقة ، لذلك ، شيء مثل:

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

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

#endif
}

هذه طريقة واحدة لتحقيق هذا النوع من الأشياء. يمكنك أيضًا القيام بذلك عن طريق تضمين رأس مختلف في شجرة المصدر الخاصة بك لتطبيقات منصة محددة للوظائف. ربما تكون هذه طريقة أفضل. في كلتا الحالتين ، الفكرة هي التجريد من واجهة برمجة التطبيقات الأساسية.

على Linux تحتاج إلى استخدامها DLSYM. انظر مثال في نهاية الصفحة. على النافذة: getProcaddress.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top