亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Emscripten教程之連接C++和JavaScript(三)

gyl_coder / 2661人閱讀

摘要:用具體的參數和返回值調用一個編譯的函數,而是一個編譯的函數的包裹,調用它會返回一個可以調用的函數。如果返回值是或你要指定不同宏,是還是。返回值用于傳給數據。對庫文件的限制調用函數作為中的函數指針使用返回一個整數來表示一個函數指針。

翻譯:云荒杯傾
本文是Emscripten-WebAssembly專欄系列文章之一,更多文章請查看專欄。
也可以去作者的博客閱讀文章。
歡迎加入Wasm和emscripten技術交流群,群聊號碼:939206522。

Emscripten提供了多種方法來連接和交互JavaScript和編譯的C或c++,本文逐一介紹。

JavaScript使用ccall/cwrap調用編譯的c函數

JavaScript中調用編譯的C函數的最簡單的方法是使用ccall()和cwrap()。

ccall()用具體的參數和返回值調用一個編譯的C函數,而cwrap()是一個編譯的C函數的包裹,調用它會返回一個JavaScript可以調用的函數。如果你打算多次調用一個函數的話,cwrap()用處更大。

舉個例子, 下面代碼是tests/hello_function.cpp文件。int_sqrt()函數外面套了個extern "C"是為了防止C++對它的名字改編。

    #include 

    extern "C" {

    int int_sqrt(int x) {
      return sqrt(x);
    }

    }

使用的編譯命令為:

./emcc tests/hello_function.cpp -o function.html -s EXPORTED_FUNCTIONS="["_int_sqrt"]"

編譯完,你可以使用JavaScript調用cwrap()拿到int_sqrt函數。繼而可以進行其他操作。

    int_sqrt = Module.cwrap("int_sqrt", "number", ["number"])
    int_sqrt(12)
    int_sqrt(28)

第一個參數是函數名,第二個參數是函數返回類型,第三個是參數類型。
返回類型和參數類型中可以用類型有三個,分別是number,string和array。number(是js中的number,對應著C中的整型,浮點型,一般指針),string(是JavaScript中的string,對應著C中的char,C中char表示一個字符串),array(是js中的數組或類型數組,對應C中的數組;如果是類型數組,必須為Uint8Array或者Int8Array)。

編譯完,你可以運行function.html,在瀏覽器先看一看,實際上啥也沒有,因為tests/hello_function.cpp文件沒有主函數(main())。打開一個js開發(fā)環(huán)境,敲下下面上面的幾行,就能看到運行結果,12開方為3,28開方得5。

ccall()類似,不過還要接受其他參數。下面代碼就是直接用int_sqrt計算28的開方。

    // Call C from JavaScript
    var result = Module.ccall("int_sqrt", // name of C function
      "number", // return type
      ["number"], // argument types
      [28]); // arguments

    // result is 5
使用 ccall()或者cwrap()的注意事項:
    *  這些方法用于編譯的C函數,對進行過函數名改編的C++函數不工作。
    *  推薦你導出你要用JavaScript調用的函數。
        A.導出是在編譯階段做的。比如-s EXPORTED_FUNCTIONS="["_main","_other_function"]"               導出了main()和other_function()。
        B.導出時給函數名加下劃線“_”,見A。
        C.A中把main也導出了,如果你不導出main,mian就會變成無效代碼,這個導出列表應該是完整             的可以keepalive的函數列表。
        D.Emscripten會做無效代碼清除以減小生成的代碼體積,所以請確保導出了所有你想用js調的函             數。
        E.如果編譯是優(yōu)化編譯-O2級別及以上,會進行代碼改編,包括函數名。但是通過-s                       EXPORTED_FUNCTIONS導出的函數可以繼續(xù)使用原來的函數名。
        F.如果你想導出一個js庫函數(比如,src/library*.js這樣的),除了用EXPORTED_FUNCTIONS             ,還得用DEFAULT_LIBRARY_FUNCS_TO_INCLUDE。
    *  使用Module.ccall調用,不要直接用ccall。前者在代碼進行的是 優(yōu)化編譯 的情況下也工作。
Nodejs與C/C++ API交互

如果你有個C庫,暴露了一些程序/函數。如下:

    //api_example.c
    #include 
    #include 

    EMSCRIPTEN_KEEPALIVE
    void sayHi() {
      printf("Hi!
");
    }

    EMSCRIPTEN_KEEPALIVE
    int daysInWeek() {
      return 7;
    }

使用編譯命令:

emcc api_example.c -o api_example.js

可以用以下代碼執(zhí)行這個庫中的函數:

var em_module = require("./api_example.js");

em_module._sayHi(); // direct calling works
em_module.ccall("sayHi"); // using ccall etc. also work
console.log(em_module._daysInWeek()); // values can be returned, etc.

這就是簡單的編譯C函數和Node的交互。

JavaScript“直接”調用編譯的C/C++代碼

C中的函數編譯為js中函數后,其實你可以直接調的,比如C中有個a(),在編譯后js用_a()來調,不用非要使用ccall()和cwarp(),不過有時候直接調用會稍微復雜點。可能需要你調試一下。

注意上面的_a()中的“_”,直接調的話是一般是要加的。

note:
用ccall()和cwarp()來調,就不用加下劃線。C中是什么函數名,就js使用什么函數名,傳給ccall()或cwarp()。

直接調的時候,多多注意函數的參數,確保他們有意義。如果是整型和浮點數原樣傳遞,指針參數在編譯后要按簡單整數來傳。

Pointer_stringify()將C指針轉為字符串,將字符串轉為指針,請用 ptr = allocate(intArrayFromString(someString), "i8", ALLOC_NORMAL).

還有一些轉換字符串的函數,可以在preamble.js中找。

C/C++調用JavaScript

Emscripten提供兩種方法讓C/C++調用JavaScript,一種是使用 emscripten_run_script()運行js腳本,一種是寫“內聯JavaScript”。

emscripten_run_script()最直接,但略慢的方式。它是通過eval()來實現的。舉例,在C代碼中插下面一行代碼,將來編譯后就能在瀏覽器彈出alert()。

emscripten_run_script("alert("hi")");
note:
因為alert函數只有瀏覽器中有,node中沒有,所以一個通用的方式是用Module.print().

第二種,用EM_ASM()和其他相關宏寫內聯JavaScript,這種方式就稍微快一點。使用這種方式實現上面那個alert,代碼就是:

#include 

    int main() {
      EM_ASM(
        alert("hello world!");
        throw "all done";
      );
      return 0;
    }

你也可以在C中傳值給JavaScript,那就用EM_ASM_(比EM_ASM多了“_”),舉例:

    EM_ASM_({
      Module.print("I received: " + $0);
    }, 100);

輸出:I received: 100。

也可以有返回值,用EM_ASM_INT。舉例

    int x = EM_ASM_INT({
      Module.print("I received: " + $0);
      return $0 + 1;
    }, 100);
    printf("%d
", x);

返回101。
更多內容參見emscripten.h部分的API文檔。

note:
* 如果返回值是int或double,你要指定不同宏,是EM_ASM_INT還是EM_ASM_DOUBLE。
* 輸入參數用$0,$1等形式表示。
* 返回值用于js傳給c數據。
* 好好看一下上面幾段代碼中{}的用法,它用來區(qū)分哪里是參數,哪里是輸入值。
* 使用 EM_ASM 注意用‘’,不要用“”。否則會有語法錯誤。
JavaScript中實現C API

在JavaScript中實現C API是有可能的。Emscripten的很多庫,比如SDL1 and OpenGL就用到這個方法。

可以寫js API 讓C/C++來調用,為實現它,你要定義接口,用extern來標記它是個外部API。然后默認情況系,你去library.js里面實現這個接口。編譯時,編譯器會尋找這些js庫。

默認下,接口實現代碼要寫在library.js里面。你也可以使用編譯選項--js-library把實現接口的代碼放到自定義.js文件中。

舉例:

    extern void my_js(void);

    int main() {
      my_js();
      return 1;
    }
note:
如果你用的C++,請用extern "C" {}把extern void my_js();括起來。如下:
        extern "C" {
          extern void my_js();
        }

上面定義了接口,并且還在main里面調用了。下面就去library.js這個默認庫里面實現這個接口,代碼如下:

    my_js: function() {
      alert("hi");
    },

這樣就相當于在C中調用一個JS庫的API。

JavaScript對庫文件的限制(todo) 調用JavaScript函數作為C中的函數指針

使用Runtime.addFunction返回一個整數來表示一個函數指針。把這個整數傳給C代碼,然后C代碼調用那個值,則傳給Runtime.addFunction的JavaScript函數就被調用。

當你用Runtime.addFunction,會有一個數組來存這些函數。這個數組的大小必須被明確指定,這可以通過編譯設置項RESERVED_FUNCTION_POINTERS來做。舉例,保存20個函數大小。

emcc ... -s RESERVED_FUNCTION_POINTERS=20 ...
JavaScript進行內存訪問

你可以使用getValue(ptr,type)和setValue(ptr,value,type)訪問內存。第一個參數ptr是一個指針(代表一個內存地址的數字)。類型type必須為LLVM IR類型,i8、i16、i32、i64、float、double或類似i8 (或只有 )的指針類型。

這是一個比ccall()和cwrap()更底層的操作

您還可以通過操縱表示內存的數組來直接訪問內存。這是不推薦的,除非您確定您知道自己在做什么,并且它比getValue()和setValue()需要更多開銷。

如果您想從JavaScript導入大量數據讓編譯代碼進行處理,那么可能需要這樣做。例如,下面的代碼分配一個緩沖區(qū),在其中放一些數據,調用C函數來處理數據,最后釋放緩沖區(qū)。

    var buf = Module._malloc(myTypedArray.length*myTypedArray.BYTES_PER_ELEMENT);
    Module.HEAPU8.set(myTypedArray, buf);
    Module.ccall("my_function", "number", ["number"], [buf]);
    Module._free(buf);

這里my_function是一個C函數,它接收一個整數參數(或者一個指針,它們都是32位的整數),并返回一個整數。可能是int my_function(char * buf)這樣。

影響執(zhí)行行為

Module是一個全局JavaScript對象,它有很多Emscripten生成的代碼在執(zhí)行的時候會調用的屬性。

開發(fā)者可以提供Module的實現以控制Emscripten消息通知的顯示行為,主循環(huán)運行前加載哪些文件,以及其他很多行為。

環(huán)境變量

有時,編譯代碼需要訪問環(huán)境變量(例如,在C中調用getenv()函數)。emscripten生成的JavaScript無法直接訪問計算機的環(huán)境變量,因此提供了一個“虛擬化”的環(huán)境。

JavaScript對象ENV包含虛擬化環(huán)境變量,通過修改它可以將變量傳遞給編譯后的代碼。必須注意確保ENV變量在修改前已由Emscripten初始化。Module.preRun可以做這個。

例如,要設置一個環(huán)境變量MY_FILE_ROOT為“/ usr/lib/test/”,您可以將以下JavaScript添加到Module設置代碼中:

Module.preRun.push(function() {ENV.MY_FILE_ROOT = "/usr/lib/test"})
C++和JavaScript綁定---WebIDL Binder和Embind(todo) Emscripten代碼移植系列文章

Emscripten代碼移植主題系列文章是emscripten中文站點的一部分內容。
第一個主題介紹代碼可移植性與限制
第二個主題介紹Emscripten的運行時環(huán)境
第三個主題第一篇文章介紹連接C++和JavaScript
第三個主題第二篇文章介紹embind
第四個主題介紹文件和文件系統(tǒng)
第六個主題介紹Emscripten如何調試代碼

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://www.ezyhdfw.cn/yun/91915.html

相關文章

  • Emscripten教程C++JavaScript綁定(

    摘要:支持綁定大多數的結構,包括和中引入的。枚舉支持枚舉和枚舉類。雖然還有進一步優(yōu)化的空間,但到目前為止,它在實際應用程序中的性能已經被證明是完全可以接受的。 翻譯:云荒杯傾 Embind用于綁定C++函數和類到JavaScript,這樣編譯代碼就能在js中以一種很自然的方式來使用。Embind也支持從C++調JavaScript的class。 Embind支持綁定大多數C++的結構,包括C...

    warkiz 評論0 收藏0
  • Emscripten教程優(yōu)化你的代碼

    摘要:優(yōu)化項也會引發(fā)一些問題。檢查你的代碼是否工作并修復問題。從起,及以上的優(yōu)化級別默認啟動了這項設置。目前正在進行改進。代碼移植系列文章代碼移植主題系列文章是中文站點的一部分內容。 作者:云荒杯傾歡迎加入Wasm和emscripten技術交流群,群聊號碼:939206522。 這是關于Emscripten的系列文章,更多文章請看下面鏈接。 Emscripten代碼移植系列文章 Emscr...

    Jokcy 評論0 收藏0
  • Emscripten教程優(yōu)化你的代碼

    摘要:優(yōu)化項也會引發(fā)一些問題。檢查你的代碼是否工作并修復問題。從起,及以上的優(yōu)化級別默認啟動了這項設置。目前正在進行改進。代碼移植系列文章代碼移植主題系列文章是中文站點的一部分內容。 作者:云荒杯傾歡迎加入Wasm和emscripten技術交流群,群聊號碼:939206522。 這是關于Emscripten的系列文章,更多文章請看下面鏈接。 Emscripten代碼移植系列文章 Emscr...

    bladefury 評論0 收藏0
  • Emscripten教程代碼可移植性與限制(一)

    摘要:教程之代碼可移植性與限制一翻譯云荒杯傾本文是專欄系列文章之一,更多文章請查看專欄。下面是正文代碼可移植性與限制幾乎可以編譯任何可移植的代碼到。如果標準機構將共享狀態(tài)添加到中,支持多線程代碼將成為可能。 Emscripten教程之代碼可移植性與限制(一) 翻譯:云荒杯傾本文是Emscripten-WebAssembly專欄系列文章之一,更多文章請查看專欄。也可以去作者的博客閱讀文章。歡迎...

    yangrd 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<