使用 Emscripten 读写字符串到虚拟文件系统

Posted

技术标签:

【中文标题】使用 Emscripten 读写字符串到虚拟文件系统【英文标题】:Read and write string to virtual file system using Emscripten 【发布时间】:2021-11-15 19:11:57 【问题描述】:

我有一个 C++ 程序,它接受两个文件作为输入并生成一个文件作为输出。这些文件包含字符串。我使用带有以下标志的 Emscripten 成功将此程序编译为 webassembly:

emcc program.bc -o program.js -s USE_ZLIB=1 -s EXPORTED_RUNTIME_METHODS="['FS', 'callMain']" -s ALLOW_MEMORY_GROWTH=1 -s ENVIRONMENT='web, worker'

然后我使用以下粘合代码将字符串(作为参数传递)写入 Emscripten 虚拟文件系统中的文件。这些文件然后用于运行program 的主要功能。然后从文件系统中读取输出文件并将其内容记录到控制台:

const Module = require('./program.js')

async function main(input1, input2)
    Module.FS.writeFile('first_input.txt', input1)
    Module.FS.writeFile('second_input.txt', input2)
    Module.callMain(['-r', 'first_input.txt', '-q', 'second_input.txt', '-o', 'test.out', '-t', '1'])
    const output = Module.FS.readFile('test.out',  encoding: 'utf8' )
    console.log(output)


module.exports = main

当尝试调用上面的主函数时,我得到了错误:

Unhandled Rejection (TypeError): Cannot read properties of undefined (reading 'writeFile')

当我console.log(Module.FS)时,确实是未定义。此外,console.log(Module) 返回一个空对象。在program.js 内部,似乎确实通过Module["FS"] = FS 启用了FS 模块。当我为节点环境编译它(使用-s ENVIRONMENT='node')时,它可以顺利运行。

为什么工作环境会失败,我可以做些什么来修复它?

编辑:我尝试将代码包装在 `onRuntimeInitialized' 函数中,但我得到了同样的错误:

const Module = require('./program.js')

async function main(input1, input2)
    Module.onRuntimeInitialized = () => 
      Module.FS.writeFile('first_input.txt', input1)
      Module.FS.writeFile('second_input.txt', input2)
      Module.callMain(['-r', 'first_input.txt', '-q', 'second_input.txt', '-o', 'test.out', '-t', '1'])
      const output = Module.FS.readFile('test.out',  encoding: 'utf8' )
      console.log(output)
    
    
    await Module.onRuntimeInitialized()



module.exports = main

EDIT2:上面的 main 函数实际上是在一个 worker 上运行,而不是在 html 文档中的 script 标签内。我无法直接编辑 HTML 文档,这就是我 require Emscripten 输出 javascript 文件的原因。

【问题讨论】:

浏览器和节点的行为方式不同。你得到未定义的模块,因为它没有完全初始化。您应该将代码包装在 onRuntimeInitialized 中,如本常见问题文章 emscripten.org/docs/getting_started/… 中所述 你还应该为读取文件提供一个ajax(Promise)函数。例如阅读以下代码:github.com/kalwalt/Emscripten-JpegReader/blob/… @kalwalt 您能否详细说明包装代码(最好作为答案)?我检查了链接的文档,但对于如何修改我的代码仍然有些困惑。我尝试将代码包装在上述主函数中(见编辑),但它给了我同样的错误。 不,您的代码不正确。它应该首先被调用。我会尽可能发布详细的答案。我现在没时间,抱歉! @kalwalt 需要注意的一点是,我无法直接编辑 html,因此无法添加 【参考方案1】:

解决办法是:

    使用MODULARIZE=1 标志编译程序
emcc program.bc -o program.js -s USE_ZLIB=1 -s EXPORTED_RUNTIME_METHODS="['FS', 'callMain']" -s ALLOW_MEMORY_GROWTH=1 -s MODULARIZE=1 -s EXPORT_NAME='createModule'
    导入模块,然后等待生成的承诺
import createModule from './program.js'

async function main(input1, input2)
    const Module = await createModule()
    Module.FS.writeFile('first_input.txt', input1)
    Module.FS.writeFile('second_input.txt', input2)
    Module.callMain(['-r', 'first_input.txt', '-q', 'second_input.txt', '-o', 'test.out', '-t', '1'])
    const output = Module.FS.readFile('test.out',  encoding: 'utf8' )
    console.log(output)



export default main

【讨论】:

很高兴你解决了,也许是最简单的一个。

以上是关于使用 Emscripten 读写字符串到虚拟文件系统的主要内容,如果未能解决你的问题,请参考以下文章

Emscripten教程之文件和文件系统

如何使用库导入编译 C 文件到 webassembly 文件(Emscripten)

如何使用 emscripten 将文件从 C 保存到浏览器存储

使用 emscripten 将字符串从 C++ 传递给 JS

是否可以使用 emscripten 使用 Javascript 从 C++ 打印字符串?

emscripten window.onload 等效