WebAssembly编译之(4)-WASM编译进阶(多文件、多接口)

news2024/9/20 9:38:38

引言
上一节我们讲到如何用Emscripten将一个C编译陈wasm,并导出可供Javascirpt调用的接口,以及C++导出类的函数接口、导出类的封装对象等。然而,编译的方式比较玛法,有没办法能更友好一点实现wasm的编译呢

WASM 相关文档:
WebAssembly编译之(1)-asm.js及WebAssembly原理介绍
WebAssembly编译之(2)-Ubuntu搭建WASM编译环境
WebAssembly编译之(3)-WASM编译实战之C/C++导出asm.js及wasm库

这一节我们继续给大家介绍wasm编译进阶教程,多个文件的编译,甚至多个接口的编译。

多个C/C++接口的导出编译方法

上一节前面我们知道,每次执行emcc指令时,我们都需要在命令行中携带参数EXPORTED_FUNCTIONS=['_funsName1','_funsName2']指定需要导出的函数接口。而然,当需要导出的接口变非常多的时候,这样操作显然不太明智。怎么办呢?

1)使用EMSCRIPTEN_KEEPALIVE宏修饰C的函数

我们可以使用Emscripten给我们提供的宏指令EMSCRIPTEN_KEEPALIVE修饰。使用这个宏之前需要引入#include <emscripten.h>,同时为了避免代码提示找不到include头文件的错误信息,我们可以需要在IDE编辑器中,设置好include目录,include的目录位置一般在你的EMSDK git项目目录emsdk/upstream/emscripten/system/include/emscripten

我们还是先上代码

// HelloToolFuns.c
#include <stdio.h>
#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int myAdd(int a,int b){
    int res = a+b;
    return res;
}

EMSCRIPTEN_KEEPALIVE
int myMutilp(int a, int b){
    int s = a * b;
    return s;
}

EMSCRIPTEN_KEEPALIVE
void sayHello() {
      printf("Hello World!(HelloToolFuns.c)\n");
}

我们可以直接使用一个EMSCRIPTEN_KEEPALIVE修饰在需要导出的函数接口即可。在编译时,不需要指定带导出的接口函数名,只需要直接使用emcc编译即可,如下所示:

注意:cpp的函数导出也同样适用这个方法,只是,任然需要在函数的外层套一个extern "C" { }

emcc HelloToolFuns.c -o ./test-html/HelloToolFuns.js

EMSCRIPTEN_KEEPALIVE本意是保持函数的意思,怎么理解呢?Emscripten在编译时,为了保证wasm足够小,其实还会做一层删除无用函数的操作。如对于内联函数、或是未调用的函数会做DCE清除操作。当我们采用了这个宏,表示这个函数无论如何将做保留操作。

那么在编译后,被保留下来的函数将会挂着到Module.asm下,这样,我们就可以调用了。我们分析一下我们的HelloToolFuns.js代码,确实myAddmyMutilpsayHello这三个函数都不存在任何调用关系,很显然在编译时,必然会被Emscripten优化掉。这也就是我们上一节课开头发现的,为啥直接编译不携带任何参数时,编译出来的wasm在Module.asm下没有任何我们需要的接口函数

2)使用EMSCRIPTEN_BINDINGS实现C++的函数及类的完美导出

前面我们介绍了,使用cpp编译导出wasm可用的函数时,需要添加extern "C",同时,需要将C++的类及方法做一层转化,使C可调用的C++的类。

而在Javascript使用这种导出的wasm类时,用起来总感觉不符合面向对象的编程习惯,看起就是妥妥C的编程习惯。如下代码所示:

var hToolObj = Module.asm.HelloTools_OBJ_New(); // 创建对象
Module.asm.HelloTools_add(hToolObj,10,20) // 访问对象

有没办法能使C++导出的wasm,在javascirpt调用时,可以优雅一点来访问呢?比如这样:

var hToolObj = new Module.asm.HelloTools(); // 创建对象
hToolObj.add(10,20) // 访问对象

这样是不更爽一点呢?这就是我们这小节需要重点介绍的EMSCRIPTEN_BINDINGS宏。

2.1)C++的导出函数

我们还是先看看用怎么通过它实现cpp编写的函数的导出,先上代码:

// HelloToolFuns2.cpp
#include  <stdio.h>
#include <emscripten/bind.h>

int myAdd(int a,int b){
    int res = a+b;
    return res;
}

int myMutilp(int a, int b){
    int s = a * b;
    return s;
}

void sayHello() {
    printf("Hello World!(HelloToolFuns2.cpp - EMSCRIPTEN_BINDINGS)\n");
}

// 这里是导出列表
EMSCRIPTEN_BINDINGS(my_module) {
    
    emscripten::function("myAdd",&myAdd);

    emscripten::function("myMutilp",&myMutilp);

    emscripten::function("sayHello",&sayHello);
}

然后编译cpp

emcc --bind HelloToolFuns2.cpp -o ./test-html/HelloToolFuns2.js

注意,编译时,我们需要添加 --bind参数,否则会报错

通过使用EMSCRIPTEN_BINDINGS导出列表,我们甚至可以不需要对cpp的函数添加extern "C" {} 来包裹,直接写在EMSCRIPTEN_BINDINGS声明的即可;

而使用这种方式的编译出来的函数接口,是直接挂着到Javascript的Module._<函数名>的,如Module._sayHello,而Module.asm下是找不到的。

2.2)C++的导出类

如何使用EMSCRIPTEN_BINDINGS导出类呢,我们还是直接上代码

// HelloTools.cpp
#include <iostream>
#include <emscripten/bind.h>

class HelloTools{
    public:
    HelloTools(int n);
    void print(int a, int b);
    int add(int a, int b);
    int num=0;
    int sum=0;
    static void showHello();
};

HelloTools::HelloTools(int n){
    num = n;
}

void HelloTools::print(int a, int b){
    std::cout<<"a+b="<<a<<"+"<<b<<"="<<a+b<<std::endl;
}

int HelloTools::add(int a, int b){
    int c = 0;
    sum+= a+b;
    return sum;
}

void HelloTools::showHello(){
    std::cout<<"show Hello Emscripten!"<<std::endl;
}

// 导出类
EMSCRIPTEN_BINDINGS(my_class_example) {
    emscripten::class_<HelloTools>("HelloTools")
    .constructor<int>()
    .function("print",&HelloTools::print)
    .function("add",&HelloTools::add)
    .property("num",&HelloTools::num)
    .class_function("showHello",&HelloTools::showHello)
    ;
}

执行编译

emcc --bind HelloTools.cpp -o ./test-html/HelloTools.js

编译出来的类将直接挂着到Module.<类名>,即Module.HelloTools

在浏览器中测试一下

var hToolObj = new Module.HelloTools(10) // 实例化HelloTools
hToolObj.add(10,20) // 返回30
hToolObj.num // 返回10
hToolObj.print(100,200) // 输出 a+b=100+200=300
Module.HelloTools.showHello() //输出 show Hello Emscripten!

在这里插入图片描述
至此,我们实现了通过Emscripten提供的接口,使用EMSCRIPTEN_BINDINGS宏,实现C++函数或类的完美导出,且至此Javascript实现友好的调用。

(多文档导出,待续)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/190197.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【自学Docker】Docker diff命令

Docker diff命令 大纲 docker diff命令教程 docker diff 命令用于比较一个 Docker容器 不同版本提交的文件差异。该命令后面的 CONTAINER 可以是容器Id&#xff0c;或者是容器名。 docker diff命令会列出 3 种容器内文件状态变化&#xff08;A - Add, D - Delete, C - Chang…

Java-基础-3.容器

一&#xff1a;为什么会出现容器&#xff1f; 在之前的学习中&#xff0c;我们学习了变量和常量。都是一个字符或者字符串&#xff0c;数字的情况。但是在实际的生产中&#xff0c;我们一次会接受到很多类型不同&#xff0c;个数不同的数据。所以&#xff0c;为了方便我们后续…

红杉:2022企业数字化年度指南

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2022年12月份热门报告盘点罗振宇2023年跨年演讲PPT原稿吴晓波2022年年终秀演讲PPT原稿2023年&#xff0c;如何科学制定年度规划&#xff1f;《底层逻辑》高清配图华为2021数字…

[基础语法] python语法之列表的基本操作

文章目录列表已发布列表的基本操作增删改查排序列表实例练习列表 已发布 python判断语句python循环语句python之列表list python 的数据格式主要有列表、字典、元组、集合。其中列表的使用最为广泛。 任何一种数据格式的使用都离不开增、删、改、查四个操作。列表除了这四个…

【Mysql第四期 运算符规则计算】

文章目录写在前面1.算数运算符2.比较运算符3.逻辑运算符4.位运算符5.运算符的优先级拓展&#xff1a;使用正则表达式查询写在前面 基本的运算符号在计算机编程领域都是相通的&#xff0c;会有自己的一些特定符号语言&#xff0c;就像是各地的普通话一样&#xff0c;尽管语音描…

剑指 Offer II 004只出现一次的数字

给你一个整数数组 nums &#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 示例 1&#xff1a; 输入&#xff1a;nums [2,2,3,2] 输出&#xff1a;3 示例 2&#xff1a; 输入&#xff1a;nums [0,1,0,…

Linux中Vi编辑器和Vim编辑器

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java案例分…

Docker常用命令总结

基础命令 1.启动docker systemctl start docker 2.关闭docker systemctl stop docker 3.设置docker为自启动 systemctl enable --now docker 4.重启docker systemctl restart docker 3.查看docker版本信息 docker version 4.查看docker详细信息 docker info Clien…

Spring定时器超过30分钟问题

目前需要定时器做一个定时扫描任务的功能&#xff0c;原先都是定时在半个小时&#xff0c;程序跑起来也没事。但是最近公司要求定时时间加长到45分钟&#xff0c;而调整完配置完后发现&#xff0c;程序是在45分钟和整点进行的扫描。 下面是我做的示例时间缩短为45秒 spring。x…

守护进程编程流程及代码实现

概念不做阐述&#xff0c;本文主要内容为守护进程编程部分的知识说明 守护进程的编程流程&#xff1a; 1.fork退出父进程&#xff0c;保证留下的子进程是组员进程 2.利用setsid()创建新会话&#xff0c;把子进程挪到新的会话中 //获取会话是getsid() 3.fork退出父进程&#x…

完成基于Servlet的对user表的增删改查

基于Servlet的增删改查 1.开发环境 IDEAJDK1.8Tomcat8.5Mysql 8.0.12 2.数据库 2.1表创建 2.2表数据 3.JavaWeb代码 3.1目录结构 3.2util包下代码 JdbcUtil完成对数据库的连接和资源释放 JsonResult对返回前端资源的封装 JdbcUtil代码&#xff1a; /* 数据库连接板帮助类 …

Python中的垃圾回收机制

Python的垃圾回收主要以引用计数为主&#xff0c;分代回收为辅。引用计数在Python中&#xff0c;使用了引用计数这一技术实现内存管理。一个对象被创建完成后就有一个变量指向这个对象&#xff0c;那么就这个对象的引用计数为1&#xff0c;以后如果有其他变量指向这个对象&…

不吹牛,完爆ant design的定位组件,floating-ui来也

前言 因为要写react定位组件&#xff08;这不是标题党&#xff0c;就是完爆ant design的定位组件&#xff0c;你应该看到一半就会同意我的观点&#xff09;&#xff0c;如下图&#xff1a; 红框部分是用绝对定位放在按钮上面的&#xff0c;你们B端用的主流组件库都是这样实现的…

Python自动化小技巧14——自动批量发送邮件(带各种附件)

案例背景 我的博客下面评论都是各种要数据的......一个一个发其实很浪费时间的&#xff0c;每次输入评论者的邮箱&#xff0c;然后打开数据所在的文件夹&#xff0c;上传&#xff0c;填写标题正文&#xff0c;发送....... 一模一样的流程&#xff0c;所以这种重复性的劳动肯定…

Linux下 git 上传与删除 的基本指令

git的概述克隆仓库使用 git 上传文件删除 git 中的文件git的概述 Git 是一个免费并开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 在使用 git 应确保Linux系统中已安装有git 命令&#xff1a;git --version 作用&#xff1a;查看 git 是否…

Spring Boot 单元测试

文章目录1. 单元测试是什么2. 单元测试的优点3. 进行 Spring Boot 单元测试3.1 确认项目中已经内置了测试框架3.2 生成单元测试的类3.3 添加 SpringBootTest 注解3.4 添加单元测试的业务代码3.5 注解 Transactional4. 断言1. 单元测试是什么 单元测试&#xff0c;是指对软件中…

微信小程序022同学会学生会活动经费系统

同学会小程序采用B/S结构、java开发语言、以及Mysql数据库等技术。系统主要分为管理员端和用户端两部分&#xff0c;管理员管理主要功能包括&#xff1a;首页、个人中心、用户管理、共享账本管理、我的账本管理、经费信息管理、经费支出管理、活动信息、管理员管理、留言板管理…

分享怎么做公众号预约_美容院预约小程序开发制作功能介绍

小程序的功能首先是为美人有约客户提供更快选购、预约服务的线上工具&#xff0c;解决顾客对商品详情、线上购买、线上预约查看等各种服务需求。一、美容美发预约下单小程序主要功能有&#xff1a;首页&#xff1a;搜索、banner、金刚区、瓷片区、项目列表预约&#xff1a;单次…

Java技术栈,从入门到放弃,废了废了

Java技术路线应用框架后端Spring家族SpringIoCAOPSpring MVCSpring Boot自动配置、开箱即用整合Web整合数据库&#xff08;事务问题&#xff09;整合权限ShiroSpring Security整合中间件缓存MQRPC框架NIO框架服务器软件应用服务器TomcatJettyUndertowWeb服务器Nginx中间件缓存R…

Deathstalker的核心武器——Janicab新变种

01 概述 DeathStalker是一个专门针对金融机构和律师事务所进行攻击的组织&#xff0c;而Janicab是其所使用的比较古老的武器。 Janicab 首次在2013年被发现&#xff0c;它是能够运行在MacOS和Windows操作系统上的恶意软件。其中&#xff0c;Windows版本基于VBscript的植入作为…