IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> WebAssembly第四章 C |C++调用JavaScript和HTTP请求文件读写操作示例 关键字:wasm emcc js c++ c -> 正文阅读

[C++知识库]WebAssembly第四章 C |C++调用JavaScript和HTTP请求文件读写操作示例 关键字:wasm emcc js c++ c

系列文章目录

第一章 WebAssembly概念
第二章 Emscripten详解
第三章 JavaScript调用C\C++
第四章 C\C++调用JavaScript



前言

本篇是WebAssembly系列文章的第四章,我会在本文介绍在几个常用场景下C++源文件内运用js代码段、调用JavaScript函数所需要用到的操作步骤,编译命令,和一些具体操作(比如:http请求fetch,文件读写)的示例代码。


我的环境

组件版本
CentOS7
Docker20.10.7
emscripten/emsdk3.1.14
nginx1.18.0
chrome102.0.5005.115

C\C++运行JavaScript代码

Emscripten 提供了从 C/C++ 调用 JavaScript 的两种主要方法:使用 emscripten_run_script() 运行脚本或编写“内联 JavaScript”。

emscripten_run_script()

最直接但速度稍慢的方法是使用emscripten_run_script()。。这有效地运行 C/C++ 中使用 指定的 JavaScript 代码。例如,要使用文本“hi”调用浏览器的函数,可以调用以下 JavaScript:eval()alert()

emscripten_run_script("alert('hi')");

EM_* 宏

从C调用JavaScript的更快方法是使用EM_JS()或EM_ASM()(以及相关的宏)编写“内联JavaScript”。

EM_ASM

内联JavaScript 的便捷语法。

这允许您在C代码中“内联”声明JavaScript,然后在浏览器中运行编译的代码时执行。例如,如果以下 C 代码是使用 Emscripten 编译并在浏览器中运行的,它将显示两个警报:

EM_ASM(alert('hai'); alert('bai'));

参数可以在JavaScript代码块内传递,它们作为变量到达,等等。这些参数可以是类型或 。
$0$1 int32_t double

EM_ASM({
  console.log('I received: ' + [$0, $1]);
}, 100, 35.5);

以 Null 结尾的 C 字符串也可以传递到块中,但要对它们进行操作,需要将它们从堆中复制出来以转换为高级 JavaScript 字符串。EM_ASM

EM_ASM(console.log('hello ' + UTF8ToString($0)), "world!");

以同样的方式,指向任何类型(包括)的指针都可以在代码中传递,其中它们显示为整数,就像上面的指针一样。可以通过直接读取堆来管理数据的访问。void *EM_ASMchar *

int arr[2] = { 30, 45 };
EM_ASM({
  console.log('Data: ' + HEAP32[$0>>2] + ', ' + HEAP32[($0+4)>>2]);
}, arr);

EM_ASM_INT、EM_ASM_DOUBLE

此宏以及EM_ASM_DOUBLE和EM_ASM_PTR的行为类似于EM_ASM,但除此之外,它们还会向 C 代码返回一个值。输出值通过语句传回:return

int x = EM_ASM_INT({
  return $0 + 42;
}, 100);

int y = EM_ASM_INT(return HEAP8.length);

EM_ASM_PTR

类似于EM_ASM_INT,但对于指针大小的返回值。使用此生成时,将生成 i64 返回值,否则将生成 i32 返回值。-sMEMORY64

字符串可以从 JavaScript 返回到 C,但需要小心内存管理。

char *str = (char*)EM_ASM_PTR({
  var jsString = 'Hello with some exotic Unicode characters: T?ss? on yksi lumiukko: ?, ole hyv?.';
  var lengthBytes = lengthBytesUTF8(jsString)+1;
  // 'jsString.length' would return the length of the string as UTF-16
  // units, but Emscripten C strings operate as UTF-8.
  var stringOnWasmHeap = _malloc(lengthBytes);
  stringToUTF8(jsString, stringOnWasmHeap, lengthBytes);
  return stringOnWasmHeap;
});
printf("UTF8 string says: %s\n", str);
free(str); // Each call to _malloc() must be paired with free(), or heap memory will leak!

C/C++调用JS函数

通过将JavaScript定义的函数mergeInto到LibraryManager.library

我们在mergeInto函数的第二个参数中,将需要注入的函数定义为对象的方法。mergeInto将该对象合并到LibraryManager.library中,LibraryManager.library是JavaScript注入C环境的库

JavaScript代码

cat test_js_library.js

mergeInto(LibraryManager.library,{
    custom_add: function(x,y){
        return x+y;
    }
});

C++代码

#include <stdio.h>
#include <emscripten/emscripten.h>
extern "C"{
    //声明在外部模块中定义的custom_add函数
        extern int custom_add(int x,int y);
        }
int main(int argc, char ** argv) {
    printf("Hello World\n");
    printf("ready to call js func>>\n");
    printf("call js func return %d\n", custom_add(2, 3));
    return 0;
}

编译命令

在编译时添加参数 --js-library 表示将js函数注入C,后接js文件地址。

docker run --rm -v $(pwd):/src -u $(id -u):$(id -g)   emscripten/emsdk emcc --std=c++17 hello3.cpp -s WASM=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']" --shell-file templates/shell_minimal.html -o sayhello3.js --js-library test_js_library.js

运行结果

在这里插入图片描述

发起HTTP请求

Emscripten Fetch API允许本机代码通过XHR(HTTP GET,PUT,POST)从远程服务器传输文件,并将下载的文件本地保存在浏览器的IndexedDB存储中,以便在后续页面访问时可以在本地重新访问它们。Fetch API 可从多个线程调用,并且网络请求可以根据需要同步或异步运行。

#include <stdio.h>
#include <iostream>
#include <emscripten/emscripten.h>
#include <emscripten/fetch.h>
using namespace std;

void downloadSucceeded(emscripten_fetch_t *fetch) {
  printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);
  emscripten_fetch_close(fetch);
}

void downloadFailed(emscripten_fetch_t *fetch) {
  printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status);
  emscripten_fetch_close(fetch); // Also free data on failure.
}

void downloadProgress(emscripten_fetch_t *fetch) {
  if (fetch->totalBytes) {
    printf("Downloading %s.. %.2f%% complete.\n", fetch->url, fetch->dataOffset * 100.0 / fetch->totalBytes);
  } else {
    printf("Downloading %s.. %lld bytes complete.\n", fetch->url, fetch->dataOffset + fetch->numBytes);
  }
}

void sync_idbfs() {
        EM_ASM(
                FS.syncfs(function (err) {});
        );
}



int main(int argc, char ** argv) {
  printf("我是printf: Hello World\n");
  cout<<"我是C++的cout "<<"你好,世界"<<endl;

  emscripten_fetch_attr_t attr;
  emscripten_fetch_attr_init(&attr);
  strcpy(attr.requestMethod, "GET");
  attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY | EMSCRIPTEN_FETCH_PERSIST_FILE;
  attr.onsuccess = downloadSucceeded;
  attr.onerror = downloadFailed;
  attr.onprogress = downloadProgress;
  emscripten_fetch_t *fetch = emscripten_fetch(&attr, "https://cccc.com/emcc/helloworld3.html");
    if (fetch->status == 200) {
      printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url);
      // The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1];
    } else {
      printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status);
    }

return 0;
}

emscripten_fetch_attr_t.attributes

EMSCRIPTEN_FETCH_LOAD_TO_MEMORY 结果载入内存,方便程序读取使用结果,如果是大文件的话建议取消此设置
EMSCRIPTEN_FETCH_PERSIST_FILE 持久化结果
EMSCRIPTEN_FETCH_SYNCHRONOUS 同步执行

编译命令

docker run --rm -v $(pwd):/src -u $(id -u):$(id -g)   emscripten/emsdk emcc --std=c++11 test_fetch.cpp -s WASM=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']" -s -sFETCH -lidbfs.js --shell-file templates/shell_minimal.html -o helloworld4.html

结果

在这里插入图片描述

读写本地文件

Emscripten 中的文件操作由 FS 库提供。它在内部用于Emscripten的所有libc和libcxx文件I / O。
当前支持的文件系统:
MEMFS :默认的文件系统,只存在于内存中。
-lnodefs.js node环境
-lidbfs.js 游览器环境
-lworkerfs.js
-lproxyfs.js

因为这几种文件系统的特性,这里我们只介绍indexedDB。

C++代码

#include <stdio.h>
#include <iostream>
#include <emscripten/emscripten.h>
#include <emscripten/fetch.h>
using namespace std;

void sync_idbfs() {
        EM_ASM(
                FS.syncfs(function (err) {});
        );
}
int main(int argc, char ** argv) {
  printf("我是printf: Hello World\n");
  cout<<"我是C++的cout "<<"你好,世界"<<endl;
        EM_ASM(
                FS.mkdir('/data');
                FS.mount(IDBFS, {}, '/data');
                FS.syncfs(true, function (err) {
                        assert(!err);
                        ccall('test1', 'v');
                });
        );
return 0;
}

#ifdef __cplusplus
extern "C" {
#endif


void EMSCRIPTEN_KEEPALIVE test1()
{
        FILE* fp = fopen("/data/idbfs_data.txt", "r+t");
        if (fp == NULL) fp = fopen("/data/idbfs_data.txt", "w+t");
        int count = 0;
        char *pcstr = new char[2048];
        pcstr = "";
        if (fp) {
                fscanf(fp, "%s", pcstr);
                cout<<"old context:"<<pcstr<<endl;
                count++;
                fseek(fp, 0, SEEK_SET);

                char *pcstr2 = new char[2048];
                pcstr2 = "你好web";
                fprintf(fp, "%s", pcstr2);
                fclose(fp);
                printf("new context:%s\n", pcstr2);

                sync_idbfs();
        }
        else {
                printf("fopen failed.\n");
        }
        cout<<"file write done"<<endl;
}

#ifdef __cplusplus
}
#endif

编译命令

docker run --rm -v $(pwd):/src -u $(id -u):$(id -g)   emscripten/emsdk emcc --std=c++11 sayhello.cpp -s WASM=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']" -s -sFETCH -s -lidbfs.js --shell-file templates/shell_minimal.html -o helloworld3.js

运行结果

在这里插入图片描述

总结

新文件系统:WasmFS(进行中…)
WasmFS是一个高性能,完全多线程,基于WebAssembly的Emscripten文件系统层,它将取代现有的JavaScript版本。
基于 JavaScript 的文件系统最初是在支持 pthreads 之前编写的,当时用 JS 编写代码更为理想。因此,它在pthreads 构建中具有开销,因为我们必须代理到完成所有文件系统操作的主线程。相反,WasmFS被编译为Wasm,并具有完全的多线程支持。它还旨在更加模块化和可扩展。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 18:34:21  更:2022-07-20 18:35:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/13 9:34:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码