2009-08-30 155 views
5

我試圖做類似下面的dlsym /與運行參數

enum types {None, Bool, Short, Char, Integer, Double, Long, Ptr}; 
    int main(int argc, char ** args) { 
    enum types params[10] = {0}; 
    void* triangle = dlopen("./foo.so", RTLD_LAZY); 
    void * fun = dlsym(triangle, ars[1]); 

    <<pseudo code>> 
    } 

哪裏僞代碼是一樣的東西

fun = {} 
for param in params: 
     if param == None: 
     fun += void 
     if param == Bool: 
      fun += Boolean 
     if param == Integer: 
      fun += int 
     ... 
returnVal = fun.pop() 
funSignature = returnval + " " + funName + "(" + Riffle(fun, ",") + ")" 
exec funSignature 

謝謝

+0

>我想要做下面的事情 那麼當你嘗試這個時會發生什麼? – a2800276 2009-08-30 18:32:24

+0

@ a2800276:編譯器抱怨許多語法問題。更深層的問題是誤解'dlopen()'和'dlsym()'等提供的服務。 – 2009-08-30 19:01:05

回答

19

其實,你幾乎可以做所有你想要的。在C語言中(例如與C++不同),共享對象中的函數僅由它們的名稱引用。因此,要找到- 最重要的,請致電 - 正確的功能,您不需要其完整的簽名。你只需要它的名字!這既是優勢也是劣勢 - 但這就是你選擇的語言的本質。

讓我來演示一下它是如何工作的。

#include <dlfcn.h> 

typedef void* (*arbitrary)(); 
// do not mix this with typedef void* (*arbitrary)(void); !!! 

int main() 
{ 
    arbitrary my_function; 
    // Introduce already loaded functions to runtime linker's space 
    void* handle = dlopen(0,RTLD_NOW|RTLD_GLOBAL); 
    // Load the function to our pointer, which doesn't know how many arguments there sould be 
    *(void**)(&my_function) = dlsym(handle,"something"); 
    // Call something via my_function 
    (void) my_function("I accept a string and an integer!\n",(int)(2*2)); 
    return 0; 
} 

事實上,你可以用這種方式調用任何函數。但是,有一個缺點。你實際上需要在編譯時間中知道你函數的返回類型。默認情況下,如果您在該typedef中省略void *,則將int假定爲返回類型 - 是的,它是正確的C代碼。問題是編譯器需要知道返回類型的大小才能正確操作堆棧。

您可以通過招數變通辦法,例如,通過聲明前幾個函數類型與預先大小不同的返回類型,然後選擇你真正要調用哪一個。但更簡單的解決方案是要求插件中的函數始終返回void *或int;實際結果是通過作爲參數給出的指針返回的。

您必須確保的是您始終使用它應該接受的參數的確切數量和類型來調用函數。密切關注不同整數類型之間的區別(你最好的選擇是將參數明確地賦予它們)。

幾位評論者報告稱上面的代碼不保證可用於可變參數函數(如printf)。

+1

@pavel:我能做點像 工會類型{ int i; double d; float f; }類型; (type)my_printf(...)? – adk 2009-08-30 20:26:56

+0

@adk:我沒有看到工會有什麼問題。事實上,我完全忘了他們,所以謝謝你提高我的答案! :) – 2009-08-30 20:42:37

+0

Pavel:'printf'是一個不好的例子,因爲空參數列表聲明與可變參數函數不兼容。 (順便說一句,'printf'的返回類型是'int',而不是'void *')。 – caf 2009-08-31 00:41:35

17

什麼dlsym()回報dlopen的通常是一個函數指針 - 僞裝成void *。 (如果你問一個全局變量的名稱,它會返回一個指向你到全局變量,太)

你然後調用,就像你可能會使用任何其他指針函數功能:

int (*fun)(int, char *) = (int (*)(int, char *))dlsym(triangle, "function"); 

(*fun)(1, "abc"); # Old school - pre-C89 standard, but explicit 
fun(1, "abc");  # New school - C89/C99 standard, but implicit 

我是老派;我更喜歡顯式表示法,以便讀者知道「f​​un」是指向函數的指針,而不需要查看它的聲明。使用新的學校符號,您必須記住在嘗試查找名爲'fun()'的函數之前先查找變量'fun'。

請注意,你不能動態地構建函數調用,你正在做的 - 或者,不一般。要做到這一點需要更多的工作。你必須事先知道函數指針在參數方面的期望以及它返回的結果以及如何解釋它。

系統,其管理更加動態的函數調用,如Perl,有關於函數的調用方式和參數傳遞和不調用(可以說是無法調用)函數任意簽名的特殊規則。他們只能用事先知道的簽名來調用函數。一種機制(Perl不使用)是將參數壓入堆棧,然後調用一個知道如何從堆棧中收集值的函數。但即使被調用函數操縱這些值然後調用任意其他函數,該函數也會爲任意其他函數提供正確的調用順序。

C中的反射很難 - 非常困難。這是不可撤消 - 但它需要的基礎設施,以支持它和紀律來使用它,它只能調用支持基礎設施的規則功能

+0

我知道提前的參數是什麼,返回類型是什麼,我的問題是我是否可以動態創建轉換。 謝謝 – adk 2009-08-30 18:48:09

+1

不;您無法動態創建演員表。 C是一種編譯語言;你想要做的是解釋C代碼,這不是一個微不足道的命題! – 2009-08-30 18:51:31

+0

謝謝。這是我想知道的 – adk 2009-08-30 19:04:45