JS调用C函数

一、在vue中调用c函数

https://emscripten.org/docs/compiling/WebAssembly.html

WebAssembly默认情况下是异步编译,这意味着您必须先等待编译完成,然后才能调用已编译的代码(通过等待main(),或onRuntimeInitialized回调等)。您可以通过设置来关闭异步编译WASM_ASYNC_COMPILATION=0,但是由于当前的限制,该设置可能无法在Chrome中运行。

关闭了异步编译,获取WebAssembly二进制文件也可能需要异步操作(因为Web不允许在主线程上下载同步二进制文件)。

二、在webworker中调用c函数

webwoker:

(function (event) {
    importScripts('test.js');
    onmessage = function (event)
    {
        var eventData = event.data;
        var res = 0;
        switch (eventData.command)
        {
            case "testThread":
                res = Module._testThread();
                if(res)
                {
                    postMessage({'function': "testThreadFailed", 'errorCode': res});
                }
                break;
            default:
                break;
        }
    };
})();

test.c

#include 
#include 
// 实现一个加法
#ifdef __cplusplus
extern "C"{
#endif	
	int add(int a,int b) {
		printf("sum:%i\n",a+b);
		return a+b;
	}
	
#ifdef __cplusplus
}
#endif

在emcc中进行编译

emcc -O2 test.c -o test.js –s EXPORTED_FUNCTIONS="['_testThread']"

运行时就可以看到结果

三、在node中调用c函数

1.编写C代码

暴漏接口的方法:

  • 在C++中,加入emscripten.h和EMSCRIPTEN_KEEPALIVE来暴漏接口(每个接口前都要加EMSCRIPTEN_KEEPALIVE)
  • 在编译时,使用编译指令emcc add.c -o add.js -s EXPORTED_FUNCTIONS="['_add','_area']"来暴漏接口
  • 注意:如果是C++文件请用extern "C" 包裹,否则编译到时方法名会被加上指纹,JS调用的话就要根据指纹规则调用了
#include 
// 实现一个加法
EMSCRIPTEN_KEEPALIVE
int add(int a,int b) {
    return a+b;
}

2.进行编译,指定输出文件为add.js否则默认输出a.out.js

emcc add.c -o add.js

当看到add.js和add.wasm文件就说明成功了。add.wasm文件可以说是wasm文件,add.js就是wasm和js文件交互的桥梁。

3.调用C++接口

直接require之前编译好的add.js来运行即可,其中注释的ccall和cwrap需要编译的时候需要暴露方法,具体编译指令也写在注释中。调用方法有三种,如下。

  • 使用引用后的文件加下划线调用方法
  • ccall和cwrap调用方法。其中ccall和cwrap的第一个参数是方法名,第二个参数是方法的返回值,第三个参数是传入参数的类型。而ccall第四个参数是传入值并直接执行。cwrap则是先定义方法。onRuntimeInitialized是初始化模块。
//test.js
方法一:ccall
let addModule = require('./add.js');
    addModule.onRuntimeInitialized = function() {
        console.log(addModule.ccall('add', 'number', ['number','number'], [3,4]));
}
方法二:cwrap
let addModule = require('./add.js');
let add = addModule.cwrap('add', 'number', ['number','number']);
    addModule.onRuntimeInitialized = function() {
        console.log(add(1,2))
}
方法三:直接调用
let addModule = require('./add.js');
    addModule.onRuntimeInitialized = function() {
        console.log(addModule._add(5,6))
}

4.执行test.js文件

直接用node运行

node test.js

四、在html调用c函数

1.html中调用C++生成的js文件

html文件





 
    
    
	

add.c文件

#include 
#include 

#ifdef __cplusplus
extern "C"{
#endif	
    int EMSCRIPTEN_KEEPALIVE add(int a,int b) {
        printf("sum:%i\n",a+b);
        return a+b;
    }
	
#ifdef __cplusplus
}
#endif

将add.c编译成add.js文件

emcc add.c -o add.js -O3 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']"

http-server运行后,就可以在浏览器端看到结果

 

 

1. 使用编译命令就可以将.cpp文件编译为.html文件,并在网页进行运行:

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

2. ①编译完,你可以使用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)。

② 使用.cwrap()的注意事项

  • 这些方法用于编译的C函数,对进行过函数名改编的C++函数不工作。
  • 推荐你导出你要用JavaScript调用的函数。

         A. 导出是在编译阶段做的。比如-s EXPORTED_FUNCTIONS='["_main","_other_function"]' 导出了main()和other_function()。

         B. 导出时给函数名加下划线“_”,见A。

         C. A中把main也导出了,如果你不导出main,mian就会变成无效代码,这个导出列表应该是完整 的可以keep alive的函数列表。

         D. Emscripten会做无效代码清除以减小生成的代码体积,所以请确保导出了所有你想用js调的函数。

         E. 如果编译是优化编译-O2级别及以上,会进行代码改编,包括函数名。但是通过-s EXPORTED_FUNCTIONS导出的函数可以继续使用原来的函数名。

         F. 如果你想导出一个js库函数(比如,src/library*.js这样的),除了用EXPORTED_FUNCTIONS ,还得用DEFAULT_LIBRARY_FUNCS_TO_INCLUDE。 * 使用Module.ccall调用,不要直接用ccall。前者在代码进行优化编译时也工作。

3. 在JS中直接调C中的函数,不必使用ccall()或cwrap()

①使用编译命令:

emcc api_example.c -o api_example.js

②可以用以下代码执行这个库中的函数:

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

em_module._sayHi(); // 方法一:直接调用,函数名前要加_
em_module.ccall("sayHi"); // 方法二:使用ccall或cwrap调用,函数名前不需要加_

你可能感兴趣的:(JS)