【问题标题】:Providing stdin to an emscripten HTML program?为 emscripten HTML 程序提供标准输入?
【发布时间】:2015-10-02 16:55:05
【问题描述】:

我有一个 C 程序,它通过命令行获取一个参数(一个字符数组/字符串),并且还从标准输入中读取。我已经使用 emscripten 将其编译为 JavaScript。这很成功,我可以像使用 node.js 的普通 C 程序一样运行它:

emcc -O2 translate.c
node translate.js "foo" < bar.txt

如您所见,我将字符串“foo”作为参数提供,并将 bar.txt 的内容作为标准输入。现在我希望这是一个独立的 HTML 文件。

通过将输出更改为 HTML:

emcc -O2 translate.c -o trans.html

我将adding arguments: ['foo'], 的参数提供给var Module 中的定义。这按预期工作,程序正确接收参数。

现在,我如何为这个程序提供标准输入输入?我不需要动态地执行此操作。只需在 HTML 中的某处声明一个带有所需标准输入内容的字符串就可以了。

【问题讨论】:

  • 对 emscripten 不太熟悉,但看起来 --embed-file 可能符合要求。
  • @nrabinowitz 刚刚找到了解决方案。谢谢!
  • @minxomat 您应该将解决方案添加为答案(并将其视为正确),而不是作为问题的编辑
  • @AlvaroMontoro 我确信这实际上不是“正确”的解决方案。总的来说,我对 JS 或 emscripten 不够精通,无法判断这种方法的质量。
  • @mınxomaτ 更有理由将其添加为答案,因此其他人可以与所有其他答案一起对其进行投票...我将删除它,但欢迎您如果需要,请重新发布它作为答案。

标签: javascript emscripten


【解决方案1】:

一种方法是使用 Emscripten 文件系统 API,例如在 Module preRun 函数中调用 FS.init,传递自定义函数以用于标准输入、输出和错误。

var Module = {
  preRun: function() {
    function stdin() {
      // Return ASCII code of character, or null if no input
    }

    function stdout(asciiCode) {
      // Do something with the asciiCode
    }

    function stderr(asciiCode) {
      // Do something with the asciiCode
    }

    FS.init(stdin, stdout, stderr);
  }
};

这些函数是相当低级的:它们每次处理一个字符作为 ASCII 码。如果您有要传入的字符串,则必须自己迭代字符串的字符。我怀疑charCodeAt 会有所帮助。要从 stdout 或 stderr 输出字符串,我怀疑 fromCharCode 会有所帮助。

使用每个示例(没有经过很好的测试!)实现如下。

var input = "This is from the standard input\n";
var i = 0;
var Module = {
  preRun: function() {
    function stdin() {
      if (i < res.length) {
        var code = input.charCodeAt(i);
        ++i;
        return code;
      } else {
        return null;
      }
    }

    var stdoutBuffer = "";
    function stdout(code) {
      if (code === "\n".charCodeAt(0) && stdoutBuffer !== "") {
        console.log(stdoutBuffer);
        stdoutBuffer = "";
      } else {
        stdoutBuffer += String.fromCharCode(code);
      }
    }

    var stderrBuffer = "";
    function stderr(code) {
      if (code === "\n".charCodeAt(0) && stderrBuffer !== "") {
        console.log(stderrBuffer);
        stderrBuffer = "";
      } else {
        stderrBuffer += String.fromCharCode(code);
      }
    }

    FS.init(stdin, stdout, stderr);
  }
};

【讨论】:

  • 太棒了!我无法摆脱ReferenceError: FS is not defined - github.com/kripken/emscripten/issues/… 任何想法?
  • 我确认这适用于 emscripten 本身提供的 test_stdin.c 程序。谢谢!
【解决方案2】:

你可以修改 Window 对象,而不是编辑 Emscripten 的输出

window.prompt = function() {
  return 'This will appear to come from standard input';
};

不太好,但我认为这与其说是编辑 Emscripten 生成的 Javascript 不如说是一种 hack。

【讨论】:

  • 抱歉,没有理由阻止这个默认功能。与此相比,编辑输入处理程序是更安全、更简单的方法。这样我就可以使用stdInput = read('input'); 来传递整个输入。查看here(第 86 行+)我如何调用 emscripten 脚本。 :)
  • @minxonat 我认为这是有原因的:让构建不那么容易受到 Emscripten 生成的输出变化的影响。我怀疑大多数项目不使用 window.prompt,但确实需要处理对其依赖项的更新。这是一个权衡:您失去了按设计使用 window.prompt 的能力,但获得了代码,因为更新 Emscripten / 其配置可能会破坏其输出的自动修补,因此更有可能表现得更好。注意:“有更好的机会”是基于我自己的判断,即修补生成的代码很脆弱:我无法提供任何证据来支持这一点。
  • 我确实相信从标准 JS API 修补函数会更加脆弱。
  • 您的脚本可以将window.prompt 保存在另一个变量中,然后调用它作为您的提示,并让原始的工作用于 wasm 而无需修改它。我同意 Michal 的观点,monkeypatching 比修改 wasm.js 更好,但是可以实现 window.readline 或自定义函数,然后在 wasm.js 中删除或重新排序 wasm 的 prompt 条件分支。如果readline首先在get_char中进行了测试,那么很容易推荐。
【解决方案3】:

根据问题“编辑”,我做了我的功能,谢谢。

只是希望下面的代码可以帮助别人。

  1. 注释运行();在 emscript 结尾

    // in my emscript 
    
    // shouldRunNow refers to calling main(), not run().
    var shouldRunNow = true;
    if (Module['noInitialRun']) {
        shouldRunNow = false;
    }
    //run(); // << here
    // {{POST_RUN_ADDITIONS}}
    
  2. result = areaInput(); // 如题所述

  3. 在您的 html 文件中添加以下代码以激活 emscript 中的 run()

    <script>
    var message;
    var point = -1;
    function getArea(){
        message = document.getElementById('input').value.split('\n');
    }
    function areaInput(){
        if(point >= message.length - 1){
            return null;
        }
        point += 1;
        return message[point];
    }
    function execEmscript(){
        window.console = {
            log: function(str){
                document.getElementById("output").value += "\n" + str;
            }
        }
        getArea();
        run();
    }
    </script>
    
  4. 记住 html 中的 io textareas

    &lt;textarea id="input" cols="80" rows="30"&gt;&lt;/textarea&gt;

    &lt;textarea id="output" cols="80" rows="30"&gt;&lt;/textarea&gt;

  5. 还有一个按钮

    &lt;button onclick="execEmscript();"&gt;run&lt;/button&gt;

【讨论】: