0. 前提知识点

  1. 导出C中的函数给JS调用:主要是EMSCRIPTEN_KEEPALIVE这个Emscripten环境特有的宏。
#include <stdio.h>#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
#endifEM_PORT_API (int) sum(int *ptr, int count) {int total = 0;for(int i = 0; i < count; i++) {total += ptr[i];}return total;
}
// js
Module._sum(ptr, 10);

  1. 使用Emscripten编译C代码到wasm。Emscripten的环境比较难配置,主要是网络问题,但好在有docker环境可以直接使用trzeci/emscripten。推荐写个build.sh文件方便修改编译脚本
# build.sh
docker run --rm -v $(pwd):$(pwd) -u $(id -u):$(id -g) trzeci/emscripten emcc helloworld.c -o helloworld.js

  1. 使用编译后的wasm。编译成功后有会.wasm文件和其对应的.js胶水文件,其目录:
.
├── build.sh
├── index.html
├── test.cc
├── test.js
└── test.wasm

index.html写测试代码

<script>var Module = {};Module.onRuntimeInitialized = () => {// 在这个回调中调用wasm中的方法};
</script>
<!-- 胶水层代码要放在后台 -->
<script src="test.js"></script>

  1. WASM Table: 存储函数

这是一个包装了WebAssemble Table 的Javascript包装对象,具有类数组结构,存储了多个函数引用。在Javascript或者WebAssemble中创建Table 对象可以同时被Javascript或WebAssemble 访问和更改。

1. 在C中调用JS函数之addFunction

Emscripten提供了多种在C环境调用JavaScript的方法,包括:

  1. EM_JS/EM_ASM宏内联JavaScript代码
  2. emscripten_run_script函数
  3. JavaScript函数注入(更准确的描述为:“Implement C API in JavaScript”,既在JavaScript中实现C函数API)
  4. 使用addFunction将函数指针传到C代码中调用

前3种方法点击链接就可以查看详细的使用说明

下面着重描述下第4种方法,主要结合Calling JavaScript functions as function pointers from C实践一下

You can use addFunction to return an integer value that represents a function pointer. Passing that integer to C code then lets it call that value as a function pointer, and the JavaScript function you sent to addFunction will be called.
你可以使用addFunction这个函数的返回值(数字)来代表这个函数的指针。然后将该指针(数字)传递给C代码,然后让其将该值作为函数指针进行调用,发送给addFunction的JavaScript函数将被调用。

由上面的说明可以推测出Module有一个addFunction的方法,返回值是一个数字类型。

在尝试调用的时候,发现提示说要在编译的时候导出这个函数

docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) emscripten/emsdk emcc test.cc -o test.js # 要在这里加上-s EXTRA_EXPORTED_RUNTIME_METHODS="['addFunction']"

再次调用时又发现要设置wasm table成为可以grow

此时要在编译脚本中再加上一行

-s ALLOW_TABLE_GROWTH

You should build with -s ALLOW_TABLE_GROWTH to allow new functions to be added to the table. Otherwise by default the table has a fixed size.

加上编译后,再次运行,发现叕报错了,缺少函数签名

查看文档

When using addFunction on LLVM wasm backend, you need to provide an additional second argument, a Wasm function signature string. Each character within a signature string represents a type. The first character represents the return type of a function, and remaining characters are for parameter types. - 'v': void type - 'i': 32-bit integer type - 'j': 64-bit integer type (currently does not exist in JavaScript) - 'f': 32-bit float type - 'd': 64-bit float type

原来是addFunction的第二个参数需要标明函数的返回值类型,及参数类型,再次修改

终于成功了,此时已得到了函数的指针,将其传入到C代码中就可以调用了。

下面看下C代码的实现:

// 声明函数签名,在JS中调用addFunction时,第二个函数的签名要与此声明保持一致
typedef void testExternalJSMethod(int p);// 导出一个接收函数
EM_PORT_API (void) pass_fn_ptr(int ptr) {((testExternalJSMethod*)ptr)(1);
}
// js
Module.onRuntimeInitialized = () => {function jsFunction(i) {console.log('定义在js中的function');console.log('从c中传来的参数: ', i);}// 这里说明下,C语言是先声明函数返回的类型,所以这里要先写返回值的类型,再写其他参数的类型var fPtr = Module.addFunction(jsFunction, 'vi');Module._pass_fn_ptr(fPtr);
};

最后看下输出结果:

2. addFunction的优点

  1. 相对于第3种方式来说比较灵活,第3种在js中函数的实现的参与到编译的过程中,而真实应用时给c调用的函数往往混合在业务代码中,或者是用ts去实现的,这样第3种方式就会很麻烦;
  2. EM_ASM emscripten_run_script这种方式直接将js代码内联到C中,没有什么维护性;
#include <emscripten.h>int main() {EM_ASM(console.log('你好,Emscripten!'));return 0;
}

X. 参考文档

  1. C/C++面向WebAssembly编程
  2. Calling JavaScript functions as function pointers from C

调用c++_WebAssembly: 在C代码中调用JS的函数相关推荐

  1. c语言调用c 方法,C语言代码中调用C++代码的方法示例

    由于历史原因,以及不同开发人员的技术偏好,C语言和C++语言都有一些独有的非常有价值的项目,因而两种语言的互操作,充分利用前人造的轮子是一件非常有价值的事情. C++代码调用C代码很简单,只要分别在包 ...

  2. c语言代码中调用系统命令行.sh shell脚本,linux shell system传参

    C语言代码中调用命令行: 1. 使用system(" 命令行 ");    --  执行完命令行后,会返回原先C代码的位置,继续执行. 2. 如果命令行中需要传参,使用 sprin ...

  3. 【Unity3D】Android Studio 工程中使用 Java 代码调用 Unity 的 C# 脚本 ( Java 中调用 UnityPlayer#UnitySendMessage 方法 )

    文章目录 一. Java 调用 C# 依赖库准备 1.依赖库位置 2.unityLibrary 依赖库位置 二. Java 调用 C# 的 UnityPlayer#UnitySendMessage 方 ...

  4. 在python代码中调用vba宏的四种方法

    在python代码中调用vba宏 工作以python为主体,但是遇到了一些word操作的需求(详见上一篇),这个需求用word自带的功能会很容易实现,于是就想着能不能用python调用宏来处理. 网上 ...

  5. Golang cgo:如何在Go代码中调用C语言代码?

    如何在Go代码中调用C语言代码? Go语言是通过自带的一个叫CGO的工具来支持C语言函数调用,同时我们可以用Go语言导出C动态库接口给其它语言使用. 方式一.直接在 Go 代码中写入 C 代码 检查是 ...

  6. java 0xf0_java 中类似js encodeURIComponent 函数的实现案例

    我就废话不多说了,大家还是直接看代码吧~ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import ...

  7. C# 代码中调用 Javascript 代码段以提高应用程序的配置灵活性(使用 Javascript .NET 与 Jint)...

    一般来说,我们需要在开发应用软件的配置文件中,添加一些参数,用于后续用户根据实际情况,自行调整. 配置参数,可以放在配置文件中.环境变量中.或数据库表中(如果使用了数据库的话).通常,配置数据,以 k ...

  8. 汇编语言调用c语言ads,ADS1.2 在汇编代码中调用C函数

    EDA365欢迎您登录! 您需要 登录 才可以下载或查看,没有帐号?注册 x , U) b) }+ U8 \" d/ v( \$ ~  T对于ARM体系来说,不同语言撰写的函数之间相互调用( ...

  9. Pytest学习-如何在用例代码中调用fixtrue时传入参数

    前言 在使用Pytest的时候,为了提高代码的复用性,我们一般会把一些常用操作,比如把登录方法写在 conftest.py 中,然后在不同测试用例中,调用这个登录方法. 但是在测试用例中,我们可能需要 ...

最新文章

  1. webpy + nginx + fastcgi 构建python应用
  2. 计算机的桌面教案,《认识计算机桌面》教案-20210608141312.pdf-原创力文档
  3. mysql表数据以本地文件方式导入Hive
  4. Hive分析窗口函数(五) GROUPING SETS,GROUPING__ID,CUBE,ROLLUP
  5. Linux多线程实践(4) --线程特定数据
  6. 川大计算机复试公平吗,看清华、川大这波操作,你还会担心网络复试会不公平吗?...
  7. 如果站做的比较大,那么关键词和内页的分布就要比别人高一个档次
  8. SQLite3之事务机制详解
  9. web前端开发工程师面试题大全
  10. 设计之美 --大道至简
  11. OpenCV—播放AVI视频
  12. python 在线客服_如何利用Python实现简单全双工在线客服系统!这个有点东西!...
  13. 寻仙服务器维护到几点,寻仙10月14日上午服务器例行维护公告
  14. QStringList去除重复项
  15. Windows安装lua,并使用SciTE进行编辑
  16. 关于排水管道沉积模拟建模的想法
  17. 【数学建模】数学建模中的常用工具推荐
  18. SonarQube 9.x集成阿里p3c代码规范检测java代码;
  19. 多麦克风做拾音的波束_录制人声时用电容式麦克风还是动圈式麦克风?
  20. iOS APP设计规范大全

热门文章

  1. mysql dump 增量_mysql mysqldump数据备份和增量备份
  2. opengl游戏引擎源码_UE4渲染引擎模块简介(1)
  3. 虚拟机不显示桌面_Windows10系统,你不知道的10个使用技巧请收藏
  4. vc60如何输入c语言,vc60中如何编译运行及调试c语言程序.pdf
  5. java map赋值_java 中的map怎么没有办法赋值?
  6. c语言错误 xef代表什么,单片机C语言代码手册 含100多个经典C程序
  7. 和ur的区别_UR机械臂simscape正逆解仿真
  8. 怎样把php文件改成固定大小,php修改上传文件大小限制的方法
  9. vim格式粘贴错乱的解决办法
  10. windows共享使用linux生成的密钥