2010-03-29 16 views
13

Cách dễ nhất và an toàn nhất để gọi một hàm từ thư viện/dll được chia sẻ là gì? Tôi chủ yếu quan tâm đến việc làm điều này trên Linux, nhưng nó sẽ là tốt hơn nếu có một cách độc lập nền tảng.Cách gọi hàm từ thư viện được chia sẻ?

Ai đó có thể cung cấp mã ví dụ để cho biết cách thực hiện công việc sau, trong đó người dùng đã biên dịch phiên bản foo của riêng mình thành một thư viện được chia sẻ?

// 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, tôi biết làm thế nào để biên dịch các lib chia sẻ (foo.so), tôi chỉ cần biết một cách dễ dàng để tải nó tại thời gian chạy.

Trả lời

25

LƯU Ý: Bạn đang truyền các đối tượng C++ (trong trường hợp này là chuỗi STL) xung quanh các cuộc gọi thư viện. Có không có tiêu chuẩn C++ ABI ở mức này, vì vậy hãy cố gắng tránh các đối tượng C++ xung quanh hoặc đảm bảo rằng cả thư viện và chương trình của bạn đã được xây dựng với cùng trình biên dịch (lý tưởng là cùng trình biên dịch trên cùng một máy) bất kỳ sự ngạc nhiên liên quan đến cấu hình tinh tế nào.)

Đừng quên khai báo các phương thức đã xuất extern "C" bên trong mã thư viện của bạn.

Trên đây đã được nói, đây là một số mã thực hiện những gì bạn nói bạn muốn đạt đượ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 

Bạn có thể trừu tượng này hơn nữa:

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

Nếu bạn đề cập đến đó tiêu đề để đưa vào Unix/Linux ... –

+0

Xong trong khối mã thứ hai. – vladr

+0

Điều gì về mang tên hàm trong C++, điều đó sẽ không phức tạp? Ngoài ra, bạn đã viết sai chính tả ở đây '#include ' – sbk

0

Trên Linux, bạn cần sử dụng dlsym. Xem ví dụ ở cuối trang. Trên cửa sổ: GetProcAddress.

1

LoadLibrary là chức năng Windows để tải tệp DLL. Bạn có thể kiểm tra sự tồn tại của biểu tượng với GetProcAddress. Trên Linux/Unix bạn muốn dlopen/dlsym. Để làm điều này trong nền tảng chéo, bạn có thể viết một hàm gọi một trong hai phương pháp này bằng cách sử dụng bộ xử lý trước, vì vậy, như sau:

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

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

#endif 
} 

Đây là một cách để đạt được điều này. Bạn cũng có thể làm điều đó bằng cách bao gồm một tiêu đề khác trong cây nguồn của riêng bạn để triển khai các chức năng nền tảng cụ thể. Đây có lẽ là một cách tốt hơn. Trong cả hai trường hợp, ý tưởng là trừu tượng từ API cơ bản.