摘要:用具體的參數(shù)和返回值調(diào)用一個編譯的函數(shù),而是一個編譯的函數(shù)的包裹,調(diào)用它會返回一個可以調(diào)用的函數(shù)。如果返回值是或你要指定不同宏,是還是。返回值用于傳給數(shù)據(jù)。對庫文件的限制調(diào)用函數(shù)作為中的函數(shù)指針使用返回一個整數(shù)來表示一個函數(shù)指針。
翻譯:云荒杯傾
本文是Emscripten-WebAssembly專欄系列文章之一,更多文章請查看專欄。
也可以去作者的博客閱讀文章。
歡迎加入Wasm和emscripten技術(shù)交流群,群聊號碼:939206522。
Emscripten提供了多種方法來連接和交互JavaScript和編譯的C或c++,本文逐一介紹。
JavaScript使用ccall/cwrap調(diào)用編譯的c函數(shù)JavaScript中調(diào)用編譯的C函數(shù)的最簡單的方法是使用ccall()和cwrap()。
ccall()用具體的參數(shù)和返回值調(diào)用一個編譯的C函數(shù),而cwrap()是一個編譯的C函數(shù)的包裹,調(diào)用它會返回一個JavaScript可以調(diào)用的函數(shù)。如果你打算多次調(diào)用一個函數(shù)的話,cwrap()用處更大。
舉個例子, 下面代碼是tests/hello_function.cpp文件。int_sqrt()函數(shù)外面套了個extern "C"是為了防止C++對它的名字改編。
#includeextern "C" { int int_sqrt(int x) { return sqrt(x); } }
使用的編譯命令為:
./emcc tests/hello_function.cpp -o function.html -s EXPORTED_FUNCTIONS="["_int_sqrt"]"
編譯完,你可以使用JavaScript調(diào)用cwrap()拿到int_sqrt函數(shù)。繼而可以進行其他操作。
int_sqrt = Module.cwrap("int_sqrt", "number", ["number"]) int_sqrt(12) int_sqrt(28)
第一個參數(shù)是函數(shù)名,第二個參數(shù)是函數(shù)返回類型,第三個是參數(shù)類型。
返回類型和參數(shù)類型中可以用類型有三個,分別是number,string和array。number(是js中的number,對應著C中的整型,浮點型,一般指針),string(是JavaScript中的string,對應著C中的char,C中char表示一個字符串),array(是js中的數(shù)組或類型數(shù)組,對應C中的數(shù)組;如果是類型數(shù)組,必須為Uint8Array或者Int8Array)。
編譯完,你可以運行function.html,在瀏覽器先看一看,實際上啥也沒有,因為tests/hello_function.cpp文件沒有主函數(shù)(main())。打開一個js開發(fā)環(huán)境,敲下下面上面的幾行,就能看到運行結(jié)果,12開方為3,28開方得5。
ccall()類似,不過還要接受其他參數(shù)。下面代碼就是直接用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函數(shù),對進行過函數(shù)名改編的C++函數(shù)不工作。 * 推薦你導出你要用JavaScript調(diào)用的函數(shù)。 A.導出是在編譯階段做的。比如-s EXPORTED_FUNCTIONS="["_main","_other_function"]" 導出了main()和other_function()。 B.導出時給函數(shù)名加下劃線“_”,見A。 C.A中把main也導出了,如果你不導出main,mian就會變成無效代碼,這個導出列表應該是完整 的可以keepalive的函數(shù)列表。 D.Emscripten會做無效代碼清除以減小生成的代碼體積,所以請確保導出了所有你想用js調(diào)的函 數(shù)。 E.如果編譯是優(yōu)化編譯-O2級別及以上,會進行代碼改編,包括函數(shù)名。但是通過-s EXPORTED_FUNCTIONS導出的函數(shù)可以繼續(xù)使用原來的函數(shù)名。 F.如果你想導出一個js庫函數(shù)(比如,src/library*.js這樣的),除了用EXPORTED_FUNCTIONS ,還得用DEFAULT_LIBRARY_FUNCS_TO_INCLUDE。 * 使用Module.ccall調(diào)用,不要直接用ccall。前者在代碼進行的是 優(yōu)化編譯 的情況下也工作。Nodejs與C/C++ API交互
如果你有個C庫,暴露了一些程序/函數(shù)。如下:
//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í)行這個庫中的函數(shù):
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函數(shù)和Node的交互。
JavaScript“直接”調(diào)用編譯的C/C++代碼C中的函數(shù)編譯為js中函數(shù)后,其實你可以直接調(diào)的,比如C中有個a(),在編譯后js用_a()來調(diào),不用非要使用ccall()和cwarp(),不過有時候直接調(diào)用會稍微復雜點。可能需要你調(diào)試一下。
注意上面的_a()中的“_”,直接調(diào)的話是一般是要加的。
note: 用ccall()和cwarp()來調(diào),就不用加下劃線。C中是什么函數(shù)名,就js使用什么函數(shù)名,傳給ccall()或cwarp()。
直接調(diào)的時候,多多注意函數(shù)的參數(shù),確保他們有意義。如果是整型和浮點數(shù)原樣傳遞,指針參數(shù)在編譯后要按簡單整數(shù)來傳。
Pointer_stringify()將C指針轉(zhuǎn)為字符串,將字符串轉(zhuǎn)為指針,請用 ptr = allocate(intArrayFromString(someString), "i8", ALLOC_NORMAL).
還有一些轉(zhuǎn)換字符串的函數(shù),可以在preamble.js中找。
C/C++調(diào)用JavaScriptEmscripten提供兩種方法讓C/C++調(diào)用JavaScript,一種是使用 emscripten_run_script()運行js腳本,一種是寫“內(nèi)聯(lián)JavaScript”。
emscripten_run_script()最直接,但略慢的方式。它是通過eval()來實現(xiàn)的。舉例,在C代碼中插下面一行代碼,將來編譯后就能在瀏覽器彈出alert()。
emscripten_run_script("alert("hi")");
note: 因為alert函數(shù)只有瀏覽器中有,node中沒有,所以一個通用的方式是用Module.print().
第二種,用EM_ASM()和其他相關(guān)宏寫內(nèi)聯(lián)JavaScript,這種方式就稍微快一點。使用這種方式實現(xiàn)上面那個alert,代碼就是:
#includeint 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。
更多內(nèi)容參見emscripten.h部分的API文檔。
note: * 如果返回值是int或double,你要指定不同宏,是EM_ASM_INT還是EM_ASM_DOUBLE。 * 輸入?yún)?shù)用$0,$1等形式表示。 * 返回值用于js傳給c數(shù)據(jù)。 * 好好看一下上面幾段代碼中{}的用法,它用來區(qū)分哪里是參數(shù),哪里是輸入值。 * 使用 EM_ASM 注意用‘’,不要用“”。否則會有語法錯誤。JavaScript中實現(xiàn)C API
在JavaScript中實現(xiàn)C API是有可能的。Emscripten的很多庫,比如SDL1 and OpenGL就用到這個方法。
可以寫js API 讓C/C++來調(diào)用,為實現(xiàn)它,你要定義接口,用extern來標記它是個外部API。然后默認情況系,你去library.js里面實現(xiàn)這個接口。編譯時,編譯器會尋找這些js庫。
默認下,接口實現(xiàn)代碼要寫在library.js里面。你也可以使用編譯選項--js-library把實現(xiàn)接口的代碼放到自定義.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里面調(diào)用了。下面就去library.js這個默認庫里面實現(xiàn)這個接口,代碼如下:
my_js: function() { alert("hi"); },
這樣就相當于在C中調(diào)用一個JS庫的API。
JavaScript對庫文件的限制(todo) 調(diào)用JavaScript函數(shù)作為C中的函數(shù)指針使用Runtime.addFunction返回一個整數(shù)來表示一個函數(shù)指針。把這個整數(shù)傳給C代碼,然后C代碼調(diào)用那個值,則傳給Runtime.addFunction的JavaScript函數(shù)就被調(diào)用。
當你用Runtime.addFunction,會有一個數(shù)組來存這些函數(shù)。這個數(shù)組的大小必須被明確指定,這可以通過編譯設置項RESERVED_FUNCTION_POINTERS來做。舉例,保存20個函數(shù)大小。
emcc ... -s RESERVED_FUNCTION_POINTERS=20 ...JavaScript進行內(nèi)存訪問
你可以使用getValue(ptr,type)和setValue(ptr,value,type)訪問內(nèi)存。第一個參數(shù)ptr是一個指針(代表一個內(nèi)存地址的數(shù)字)。類型type必須為LLVM IR類型,i8、i16、i32、i64、float、double或類似i8 (或只有 )的指針類型。
這是一個比ccall()和cwrap()更底層的操作
您還可以通過操縱表示內(nèi)存的數(shù)組來直接訪問內(nèi)存。這是不推薦的,除非您確定您知道自己在做什么,并且它比getValue()和setValue()需要更多開銷。
如果您想從JavaScript導入大量數(shù)據(jù)讓編譯代碼進行處理,那么可能需要這樣做。例如,下面的代碼分配一個緩沖區(qū),在其中放一些數(shù)據(jù),調(diào)用C函數(shù)來處理數(shù)據(jù),最后釋放緩沖區(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函數(shù),它接收一個整數(shù)參數(shù)(或者一個指針,它們都是32位的整數(shù)),并返回一個整數(shù)。可能是int my_function(char * buf)這樣。
影響執(zhí)行行為Module是一個全局JavaScript對象,它有很多Emscripten生成的代碼在執(zhí)行的時候會調(diào)用的屬性。
開發(fā)者可以提供Module的實現(xiàn)以控制Emscripten消息通知的顯示行為,主循環(huán)運行前加載哪些文件,以及其他很多行為。
環(huán)境變量有時,編譯代碼需要訪問環(huán)境變量(例如,在C中調(diào)用getenv()函數(shù))。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中文站點的一部分內(nèi)容。
第一個主題介紹代碼可移植性與限制
第二個主題介紹Emscripten的運行時環(huán)境
第三個主題第一篇文章介紹連接C++和JavaScript
第三個主題第二篇文章介紹embind
第四個主題介紹文件和文件系統(tǒng)
第六個主題介紹Emscripten如何調(diào)試代碼
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/91915.html
摘要:支持綁定大多數(shù)的結(jié)構(gòu),包括和中引入的。枚舉支持枚舉和枚舉類。雖然還有進一步優(yōu)化的空間,但到目前為止,它在實際應用程序中的性能已經(jīng)被證明是完全可以接受的。 翻譯:云荒杯傾 Embind用于綁定C++函數(shù)和類到JavaScript,這樣編譯代碼就能在js中以一種很自然的方式來使用。Embind也支持從C++調(diào)JavaScript的class。 Embind支持綁定大多數(shù)C++的結(jié)構(gòu),包括C...
摘要:優(yōu)化項也會引發(fā)一些問題。檢查你的代碼是否工作并修復問題。從起,及以上的優(yōu)化級別默認啟動了這項設置。目前正在進行改進。代碼移植系列文章代碼移植主題系列文章是中文站點的一部分內(nèi)容。 作者:云荒杯傾歡迎加入Wasm和emscripten技術(shù)交流群,群聊號碼:939206522。 這是關(guān)于Emscripten的系列文章,更多文章請看下面鏈接。 Emscripten代碼移植系列文章 Emscr...
摘要:優(yōu)化項也會引發(fā)一些問題。檢查你的代碼是否工作并修復問題。從起,及以上的優(yōu)化級別默認啟動了這項設置。目前正在進行改進。代碼移植系列文章代碼移植主題系列文章是中文站點的一部分內(nèi)容。 作者:云荒杯傾歡迎加入Wasm和emscripten技術(shù)交流群,群聊號碼:939206522。 這是關(guān)于Emscripten的系列文章,更多文章請看下面鏈接。 Emscripten代碼移植系列文章 Emscr...
摘要:教程之代碼可移植性與限制一翻譯云荒杯傾本文是專欄系列文章之一,更多文章請查看專欄。下面是正文代碼可移植性與限制幾乎可以編譯任何可移植的代碼到。如果標準機構(gòu)將共享狀態(tài)添加到中,支持多線程代碼將成為可能。 Emscripten教程之代碼可移植性與限制(一) 翻譯:云荒杯傾本文是Emscripten-WebAssembly專欄系列文章之一,更多文章請查看專欄。也可以去作者的博客閱讀文章。歡迎...
閱讀 3794·2023-04-25 16:32
閱讀 2194·2021-09-28 09:36
閱讀 2035·2021-09-06 15:02
閱讀 673·2021-09-02 15:21
閱讀 918·2019-08-30 15:56
閱讀 3513·2019-08-30 15:45
閱讀 1708·2019-08-30 13:09
閱讀 379·2019-08-29 16:05