2013-05-31 45 views
6

Tôi cần liên kết động với chức năng thư viện khi chạy trong Mac OS X. Theo sau Apple's example, tôi khai báo con trỏ hàm và gán nó với kết quả của dlsym(). Ví dụ sau đây biên dịch thành công dưới dạng tệp C (.c) đơn giản. Nhưng tôi cần điều này trong một tệp C++ và nếu tôi biên dịch ví dụ này dưới dạng tệp C++ (.cpp), trình biên dịch clang sẽ cho tôi biếtChức năng gán con trỏ hoạt động trong C nhưng không phải C++

Không thể khởi tạo biến loại 'void () (char *)' với giá trị của loại 'void '

Tại sao nó lại hoạt động trong đồng bằng 'C' và cách khắc phục điều này?

#include <dlfcn.h> 

void Test() { 
    // Load the library which defines myFunc 
    void* lib_handle = dlopen("myLib.dylib", RTLD_LOCAL|RTLD_LAZY); 

    // The following line is an error if compiled as C++ 
    void (*myFunc)(char*) = dlsym(lib_handle, "myFunc"); 

    myFunc("Hello"); 

    dlclose(lib_handle) ; 
} 

Trả lời

7

dlsym trả về void*. Trong POSIX (nhưng không phải chuẩn C, như James chỉ ra) có một chuyển đổi ẩn từ void* thành kiểu con trỏ tới hàm, do đó việc gán cho myFunc chỉ hoạt động. Trong C++ không có chuyển đổi ngầm (vì nó không phải là loại an toàn), vì vậy bạn cần phải nói với trình biên dịch mà bạn thực sự có nghĩa là nó bằng cách thêm một dàn diễn viên:

void (*myFunc)(char*) = (void(*)(char*))dlsym(lib_handle, "myFunc"); 

(hoặc bạn có thể nhận được ưa thích với một reinterpret_cast).

+0

... hoặc có thể 'static_cast': http://stackoverflow.com/questions/310451/should-i-use-static-cast-or-reinterpret-cast-when-casting-a-void-to- bất cứ điều gì –

+2

Phiên bản của tiêu chuẩn C Tôi đã nói rằng một con trỏ đến 'void' có thể được chuyển đổi thành hoặc từ một con trỏ đến bất kỳ loại _object_ nào (nhấn mạnh thêm). Hàm không phải là kiểu đối tượng và C chưa bao giờ cho phép chuyển đổi (rõ ràng hoặc ẩn) giữa 'void *' và con trỏ đến hàm. Tôi không chắc chắn, nhưng tôi nghĩ rằng một chẩn đoán là bắt buộc. (Tôi không nghĩ rằng đó là hành vi không xác định.) Và diễn viên rõ ràng (thậm chí 'reinterpret_cast') cũng không nên hoạt động. (Hầu hết các trình biên dịch Unix không phù hợp với vấn đề này, nhưng vì nó là phần mở rộng, mỗi trình biên dịch tự do làm những gì nó thích.) –

+0

@JamesKanze đúng, C không có chuyển đổi ngầm từ 'void *' sang bất kỳ con trỏ nào sang loại chức năng. Trình biên dịch C phù hợp phải đưa ra chẩn đoán (có thể chỉ là cảnh báo); một trình biên dịch C không phù hợp, như hầu hết là theo mặc định, có thể làm bất cứ điều gì nó thích. –

0

Vì trình biên dịch C bị hỏng. Không có chuyển đổi (rõ ràng hoặc ẩn) giữa void* và một con trỏ đến hàm , không phải trong C cũng như trong C++.

Posix thêm một hạn chế đến C, và đòi hỏi rằng void* và con trỏ đến chức năng có kích thước và đại diện tương tự, vì vậy rằng:

void (*myFunc)(char *); 
*(void (**myFunc)(char*))(&myFunc) = dlsym(...); 

sẽ làm việc.

Trong C++, bạn có thể muốn sử dụng cái gì đó như:

class GetFunctionHelper; 
GetFunctionHelper getFunction(void* dlHandle, std::string const& functionName); 

class GetFunctionHelper 
{ 
    void* fromSystem; 
    freind GetFunctionHelper getFunction(void* , std::string const&); 
    GetFunctionHelper(void* fromSystem) : fromSystem(fromSystem) {} 
public: 
    template <typename Ptr> operator Ptr() const 
    { 
     return *reinterpret_cast<Ptr const*>(&fromSystem); 
    } 
}; 

GetFunctionHelper 
getFunction(void* dlHandle, std::string const& functionName) 
{ 
    return GetFunctionHelper(dlsym(dlHandle, functionName.c_str())); 
} 

(với một kiểm tra lỗi hơn chút, tất nhiên).

+0

Một chuyển đổi rõ ràng (tức là, một diễn viên) từ 'void *' thành kiểu con trỏ tới hàm không, theo như tôi có thể nói, vi phạm bất kỳ ràng buộc nào. Nhưng vì tiêu chuẩn không xác định hành vi của một chuyển đổi như vậy, hành vi của nó là không xác định. POSIX có thể xác định hành vi, vì nó hoàn toàn miễn phí. –

+0

Cảm ơn bạn, @James. Tôi không đủ thông minh để nhận xét về sự thiếu hụt trình biên dịch. Ngoài ra, dòng thứ hai của bạn không biên dịch. Ngoài lỗi đánh máy 'u' so với 'y', cũng có một dấu ngoặc đơn đúng bị thiếu, nhưng tôi không thể tìm ra nơi nên đi. –

+0

@KeithThompson Tôi không chắc chắn về C; trong C++, cần có chẩn đoán. Cho đến gần đây, Posix về cơ bản đã sử dụng ví dụ đầu tiên của tôi ở trên; khi tôi nhìn nó lên lần này, tuy nhiên, họ nói để sử dụng một phần mở rộng trình biên dịch. Vì vậy, tôi không biết (liên quan đến C). Trong thực tế, tôi đã luôn luôn sử dụng một cái gì đó giống như phiên bản C++ của tôi, do đó, vấn đề không bao giờ phát sinh (cũng có, tôi chưa bao giờ thực sự nhìn thấy một trình biên dịch Unix thực thi quy tắc này). –

Các vấn đề liên quan