2012-12-02 107 views
1

通過原生我的意思是寫在C++或C如何從VM調用本地函數?

我正在做基於一種編程語言爪哇,因爲它有一個虛擬機,並字節碼編譯的語言。

實現語言的功能,如for循環,變量,算術等對我來說不是問題;不過,執行像Java這樣的本地函數可以。

我需要本地函數,以便使用我的語言編寫的程序可以創建窗口,與硬件和操作系統進行交互,並執行幾乎不是簡單的數學操作。

我聽說過JNI,它絕對看起來像我想要的東西,但是,我不知道如何實現這樣的東西。

正如我的虛擬機是用C++實現的,我知道我可以擁有它我的本地函數#include HPP文件在編譯時間,然後它可以動態加載dll的或so的,然而,這並未「這似乎是一個很好的解決方案,因爲每當你希望它能夠執行另一個本地函數時,你都必須重新編譯虛擬機。

問題歸結爲:C++程序(VM)如何動態(在運行時按字節碼指示)更精確地加載C++函數庫,然後執行那些函數而不用它們在一些頭文件中預先聲明?

+1

請問你提供一些代碼樣本你想要什麼? –

+0

在Windows上,您使用LoadLibrary()和GetProcAddress()。 Linux具有不同名稱的等同功能。這些無助於您瞭解參數和返回值的數量和類型。 –

+0

@BhavikAmbani當然,只要幾個小時回家。 – corazza

回答

1

看看libffi。它提供方法來調用給定函數地址和調用簽名的任何函數。

如何確定簽名應該取決於您的上下文。您可以根據參數類型推斷大範圍的調用。 JNA推斷來自顯式Java接口,方法聲明或動態調用參數的本地調用簽名。

超越簡單函數調用來處理構造函數,內存管理和對象方法調度比較複雜,但仍基於相同的基本原則。

+0

這絕對看起來像我需要的東西。然而,我並不確定我該如何在一些動態加載的共享對象('dlopen()')中獲得指向某個函數的指針。我知道名稱,參數數量,參數類型和函數本身的返回類型,因爲這是用我的語言描述的,但我不知道如何獲得指向本地函數的指針。如果我知道本地函數將被稱爲'X',我可以得到一個指向它的指針嗎? – corazza

+0

'dlsym()'查找給定名稱的符號。通常,導出的符號是可以調用的函數或用於數據訪問的全局變量的地址。 – technomage

+0

現在我已經有了'dlopen()'和'dlsym()',LibFFI的實際需求在哪裏? – corazza

0

我沒有見過的一個話題是如何將新語言VM函數調用的參數傳遞給C++堆棧,以及如何將C++函數的返回值傳遞迴虛擬機。

假設你想讓你的新語言提供pow(3)函數。 作爲提醒,戰俘()簽名是

double pow (double base, double power) 

最簡單的方法是這樣的

void 
language::pow(VM * pVM) 
{ 
    double arg2 = pVM->PopDouble(); 
    double arg1 = pVM->PopDouble(); 
    double result = pow(arg1, arg2); 
    pVM->PushDouble(result); 
} 

但是,這聽起來並不像你所追求的。結合dlopen()的&的dlsym()得到你像

void 
language::pow(VM * pVM) 
{ 
    double arg2 = pVM->PopDouble(); 
    double arg1 = pVM->PopDouble(); 
    void *handle = dlopen("libm", RTLD_LAZY); 
    if (!handle) { /*...return; ...*/ } 
    typedef double (* pfPow) (double, double); 
    pfPow pPow = (pfPow) dlsym(handle, "pow"); 
    if (!pPow) { /*...return; ...*/ } 
    double result = (* pPow)(arg1, arg2); 
    pVM->PushDouble(result); 
} 

但是,這更是雪上加霜。您仍然需要每個C++函數的存根函數,您希望您的語言能夠訪問它。

這聽起來像你希望你的語言有類似

double result = eval_double("libm", "pow", arg1, arg2); 

我不知道如何實現,在C++。 Varags支持獲取任何類型的C++參數。但是沒有推送任意類型的C++參數的API。