cheerp資料型別包裝

鍾元大老爺發表於2019-01-14

這個文章主要描述在cheerp環境下, js和c++側資料型別的轉換和包裝內容。

1 基礎資料型別

首先我們知道javascript是弱型別的指令碼語言,開發者在開發的時候不必關注資料的型別和邊界,而c++是靜態程式語言, 在編譯階段就需要確定型別,在編譯器處理的時候可以獲得更好的優化。

我們都知道js所擁有的數值型別,(int,uint,float,double)預設不區分都是double型別儲存,這點和lua很相似。

在一般堆疊機下,如果使用函式呼叫會有大量的push,pop指令來獲取傳遞的引數,還要在內部進行根據型別包裝成可識別的型別。

cheerp的記憶體模型是平坦的,目標如果不是wasm的話, 是和js一致的,可以直接呼叫(翻譯成javascript,不需要push,pop這種指令)。

在cheerp環境下如果交織javascript和c++程式碼, cheerp會通過位移移除來替我們處理型別, 比如int型別,用js的Number左移兩位來標識。我們可以不關心這些基礎資料的轉換。

2複合資料型別

如果是js模式,cheerp會將js物件轉換成struct,或者是class類的對映,預設不推薦使用動態結構。 首先要在cheerp側擁有對應的結構定義,才能獲取和寫入屬性。
從js側生成庫給c++使用:
比如公開一個函式到c++

function window_base64_encode(s) {
        return window.btoa(s);
  }

我們在c++裡定義

String * window_base64_encode(String *);

cheerp 會自動給我們生成函式呼叫引數的原型,並切對映到js側.
如果是傳輸的物件我們可以使用Object* 來標識js側的物件。

Object * window_getData(String * );

如果是wasm模式,cheerp會嚴格遵守c++標準,對資料進行轉換。
如果要傳輸複合型別的資料,比如說物件那麼我們需要用struct對基礎資料進行包裝.而不是直接使基礎型別.

struct Sub {
    int age ;
};

struct Data {
    char hellow [20];
    int age ;
    struct Sub c;
};

等同於{age:20, c:{age:21},hellow:”cheerp”}

從c++呼叫js:

//JSExportAPI::test();
    uint8 Indata[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    for(char i = 0; i < 16; i++) {
        Indata[i] = i; ///用例
    }
    
    JSExportAPI::myJsSayHello(Indata); ///wasm呼叫js
    for(char i = 0; i < 16; i++) {
        std::cout  << (*Indata+i)<< endl; //結果
    }

從js呼叫c++

int myCppSayHello(Data a) {
    cout << "hellow i am from cpp " <<endl;
    cout  <<"hellow="<< a.hellow <<endl;
    cout  <<"age="<< a.age <<endl;
    cout  <<"sub.age="<< a.c.age <<endl;
    return 0;
}
class [[cheerp::genericjs]] JSExportAPI {
    public:
    JSExportAPI() {
    }
    
    static void myJsSayHello (uint8 * buffer) {
        Uint8Array * t = (Uint8Array *)cheerp::MakeArrayBufferView(buffer);
        auto array = cheerp::makeArrayRef(t);

        for( int i = 0 ; i < array->get_length(); i++ ) {
            array[i] = array[i] + 100 ;
            *(buffer+i) = array[i];
        }
        
        struct Data d ;
        d.age = 20;
        d.c   = {21};
        memcpy(d.hellow ,"cheerp", 7);
        myCppSayHello(d);///js呼叫wasm
    }
};

[[cheerp::genericjs]] 欄位是生成js模式的程式碼。

無論是js到c++還是c++到js, cheerp轉換的開銷比一般的(ffi)的技術開銷要小很多。型別包裝也是他效能優化的基礎方式之一。


相關文章