WebAssembly(wasm)中C与JavaScript的相互调用

WebAssembly(wasm)中C与JavaScript的相互调用本文用两个事例实践,简单介绍wasm中C与JavaScript的相互调用。 随着`wasm`技术出现,c、c++、rust、go等语言也可以运行在浏览器上,而且接近本地程序运行到速度。

Javascript 长久以来是浏览器能运行到唯一编程语言,随着wasm技术出现,c、c++、rust、go等语言也可以运行在浏览器上,而且接近本地程序运行到速度。

本文用两个事例实践,介绍wasmCJavaScript的相互调用。

简介

什么是wasm

wasm是Web的汇编,是为Web浏览器定制的汇编语言。从高级语言编译器角度看,wasm是目标代码。从浏览器觉度来看,wasm最终会被编译成平台相关的机器码。说人话:wasm能被浏览器解释并执行。

高级语言 > IR > 汇编 > 机器码 C C++.jpg

兼容性

不考虑IE情况下,主流浏览器都支持 image.png

事例实践

emscripten/emsdk 使用

需要使用emsdk把,.cc/.cpp代码编译成.wasm后缀与.js后缀的文件。

emsdkv1.38.1版本后编译目标默认为wasm(.wasm.js)

  • .wasm为源文件编译后形成的WebAssembly汇编文件
  • .jsemsdk生成的胶水代码,包含emscripten运行环境和.wasm文件的封装,.html文件导入.js后可自动完成.wasm文件的载入/编译/实例化、运行时初始化等工作。

方法一 emsdk系统环境安装

webassembly.org.cn/getting-sta…

方法二 使用docker容器编译

hub.docker.com/r/emscripte…

JavaScript调用C函数

c代码如下,代码中声明并定义了print_int,print_float,print_double三个方法。

//type_conv.cc
#ifndef EM_PORT_API
# if defined(__EMSCRIPTEN__)
# include <emscripten.h>
# if defined(__cplusplus)
# define EM_PORT_API(rettype) extern "C" rettype EMSCRIPTEN_KEEPALIVE
# else
# define EM_PORT_API(rettype) rettype EMSCRIPTEN_KEEPALIVE
# endif
# else
# if defined(__cplusplus)
# define EM_PORT_API(rettype) extern "C" rettype
# else
# define EM_PORT_API(rettype) rettype
# endif
# endif
#endif
#include<stdio.h>


EM_PORT_API(void) print_int(int a) {
    printf("C{print_int*( a:%d)}\n", a);
}

EM_PORT_API(void) print_float(float a) {
    printf("C{print_int*( a:%f)}\n", a);
}

EM_PORT_API(void) print_double(double a) {
    printf("C{print_int*( a:%lf)}\n", a);
}

__EMSCRIPTEN__ 用于探测是否 emscripten环境;

__cplusplus 用于探测是否是C++环境;

EMSCRIPTEN_KEEPALIVEemscripten特有宏,用于告知编辑器后续函数在优化时是否保留,并且该函数将被导出至JavaScript环境。

使用emsdk.cc文件进行编译

控制台执行以下指令,生成type_conv.js文件。

emcc type_conv.cc -o type_conv.js

手动创建type_conv.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>type conv</title>
    </head>
    <body>
        <script> Module = {} Module.onRuntimeInitialized = function() { Module._print_int(3.4) Module._print_int(4.6) Module._print_int(-3.4) Module._print_int(-4.6) Module._print_float(2000000.03125) Module._print_double(2000000.03125) } </script>
        <script src="type_conv.js"></script>
    </body>
</html>

Module.onRuntimeInitializedemscripten运行时准备就绪时的回调。

此时文件目录如下:

|--"/"
    |--"type_conv.cc" // 手动创建
    |--"type_conv.js" // emcc 工具生成
    |--"type_conv.html" // 手动创建

此时启动静态资源服务并使用浏览器访问type_conv.html,并查看控制台:

image.png

JavaScript调用c的函数成功!

JavaScript函数注入C环境

c代码如下,代码中声明了show_me_the_answer方法,但并没有去定义。

// closure.cc
#ifndef EM_PORT_API
# if defined(__EMSCRIPTEN__)
# include <emscripten.h>
# if defined(__cplusplus)
# define EM_PORT_API(rettype) extern "C" rettype EMSCRIPTEN_KEEPALIVE
# else
# define EM_PORT_API(rettype) rettype EMSCRIPTEN_KEEPALIVE
# endif
# else
# if defined(__cplusplus)
# define EM_PORT_API(rettype) extern "C" rettype
# else
# define EM_PORT_API(rettype) rettype
# endif
# endif
#endif

#include <stdio.h>

EM_PORT_API(int) show_me_the_answer();

EM_PORT_API(void) func() {
    printf("%d\n", show_me_the_answer());
}

创建pkg.js

mergeInto(LibraryManager.library,{
    show_me_the_answer: function(){
        return jsShowMeTheAnswer();
    }
})

LibraryManager.library可以简单理解为JavaScript注入C环境的库。 控制台执行以下指令,生成closure.js文件与closure.wasm文件。

emcc closure.cc -—js-library pkg.js -o closure.js

-—js-library pkg.js表示把pkg.js作为附加库参与链接 手动创建closure.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>hello world</title>
    </head>
    <body>
        <script> function f1(){ var answer = 42; function f2() { return answer; } return f2; } var jsShowMeTheAnswer = f1(); Module = {} Module.onRuntimeInitialized = function() { console.log(`onRuntimeInitialized`) Module._func(); console.log(`onRuntimeInitialized after`) } </script>
        <script src="closure.js"></script>
    </body>
</html>

此时文件目录如下:

|--"/"
    |--"closure.cc" // 手动创建
    |--"pkg.js" // 手动创建
    |--"closure.js" // emcc 工具生成
    |--"closure.wasm" // emcc 工具生成
    |--"closure.html" // 手动创建

此时启动静态资源服务并使用浏览器访问closure.html,并查看控制台:

image.png

至此,实现了在C环境中调用JavaScript方法show_me_the_answer

总结

使用emscripten/emsdk

  • c中定义 EM_PORT_API 宏,用来声明方法,可让JavaScript去调用对应的方法。
  • JavaScript中可以使用mergeInto方法,结合emcc -—js-library 参数,把JavaScript定义的方法注入到c环境库。

参考书籍

《面向WebAssembly编程 应用开发方法与实践》

《WebAssembly 原理与核心技术》

今天的文章WebAssembly(wasm)中C与JavaScript的相互调用分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/20377.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注