如何通过emscripten在C++和javascript之间传递字符串

15

我正在学习emscripten,但是甚至不能在C++和JS之间传递字符串时实现最基本的字符串操作。

例如,我想编写一个字符串长度函数。在C++中:

extern "C" int stringLen(std::string p)
{
    return p.length();
}

从JavaScript中调用的方式为:

var len = _stringLen("hi.");

对我来说,这产生了0。我该如何使其按预期工作?在这里应该使用哪种字符串类型?char const*std::wstringstd::string?似乎没有一种有效;我总是得到相当随机的值。

这只是个开始...我怎样才能从C++中返回一个字符串呢?

extern "C" char *stringTest()
{
    return "...";
}

在 JavaScript 中:

var str = _stringTest();

再次强调,我无法找到一种方法使其工作;在JS中我总是得到垃圾数据。

因此,我的问题很明显:如何通过Emscripten在JS和C++之间传递字符串类型?


关于问题的另一个方向的相关发现(尽管这个问题已经有7年了): 这个问题是关于如何从JavaScript传递一个字符串到C++。 要从C++传递一个字符串到JavaScript,需要使用UTF8ToString - nitroglycerine
4个回答

13

extern "C"不支持std::string。

您可以尝试以下方法:
Test.cpp

#include <emscripten.h>
#include <string.h>

extern "C" int stringLen(char* p)
        {
            return strlen(p);
        }

使用以下命令编译Cpp代码:

emcc Test.cpp -s EXPORTED_FUNCTIONS="['_stringLen']

示例测试代码:
Test.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Hello World !</title>
        <script src="a.out.js"></script>
        <script>
             var strLenFunction =  Module.cwrap('stringLen', 'number', ['string']);
             var len1 = strLenFunction("hi.");  // alerts 3
             alert(len1);
             var len2 = strLenFunction("Hello World"); // alerts 11
             alert(len2);
        </script>
    </head>
</html>

7
这并没有完全回答问题,问题是如何将字符串传递到C++代码并从中返回。 - Stuart M

8

如果您在函数中使用extern "C",则不能在其签名中使用C++类型。

因此,如果您想使用std::string,则可以使用“Embind”或“WebIDL Binder”。请参见这里

我更喜欢embind,以下是解决问题的示例代码。

P.S 我不确定如何在这里通过引用传递变量,所以通过值来传递。

// This is your routine C++ code
size_t MyStrLen(std::string inStr) {
    return inStr.length();
}

// This is the extra code you need to write to expose your function to JS
EMSCRIPTEN_BINDINGS(my_module) {
    function("MyStrLen", &MyStrLen);
}

现在在JS中,你只需要这样做:
var myStr = "TestString";
Module.MyStrLen(myStr);

确保在调用emcc时传递标志

--bind

。有另一种方法,您可以从JS对C ++堆执行Malloc,然后进行操作,但上述方法应该更容易。


如果您在函数中使用extern "C",则不能在其签名中使用C++类型。我看到两个答案都声称如此,但我认为这不是真的。据我所知,这只是一个链接规范,用于防止名称混淆。我可以编译extern "C" void foo(std::string s) { std::cout << "hello, world" << std::endl; } - Dodgie

3
其他答案没有解决如何从C++返回字符串的问题。以下是一种通过引用传递字符串从C++到JavaScript的方法。
Emscripten文档对使用cwrap/ccall(emscripten docs)可以传递给C/C++函数的类型有以下说明:
引用: 这些类型包括“number”(对应于C整数、浮点数或通用指针的JavaScript数字)、“string”(对应于表示字符串的C char*的JavaScript字符串)或“array”(对应于C数组的JavaScript数组或类型化数组;对于类型化数组,它必须是Uint8Array或Int8Array)。
正如其他答案所指出的那样,你需要编写一个使用C字符串作为参数的C函数,因为这是emscripten API(而不是因为extern "C")。
如果你想返回一个字符串,你可能会认为你只需传递一个C字符串(实际上是通过引用传递,因为它是指针),然后修改该字符串:
// C function
extern "C" {
  void stringTest(char* output) {
    output[0] = 'H';
    output[1] = 'i';
  }
}

// Call to C function in javascript that does not modify output
let stringTestFunction =  Module.cwrap('stringTest', null, ['string']);
let output = "12"; // Allocate enough memory
stringTestFunction(output);
console.log(output); // 12

然而,这种方法行不通,因为在传递给函数时会创建一个副本。所以,你需要显式地分配内存并传递一个指针。Emscripten提供了allocateUTF8UTF8ToString函数来实现这个目的。
let stringTestFunction =  Module.cwrap('stringTest', null, ['number']); // the argument is 'number' because we will pass a pointer 
let output = "12";
let ptr = Module.allocateUTF8(output); // allocate memory available to the emscripten runtime and create a pointer
stringTestFunction(ptr);
output = Module.UTF8ToString(ptr); // read from the allocated memory to the javascript string
Module._free(ptr); // release the allocated memory
console.log(output); // Hi

因为我们将字符串转换为字符指针,所以我们也可以直接调用函数而不使用(emscripten docs):Module._stringTest(ptr)。这需要一些额外的步骤,但现在你已经成功将一个字符串从C传递到了JavaScript。
为了使这个示例工作,您可能需要使用以下标志进行编译:-sEXPORTED_FUNCTIONS="['_stringTest','_malloc','_free']"-sEXPORTED_RUNTIME_METHODS="['cwrap','allocateUTF8','UTF8ToString']"
还有更通用的方法来为其他类型的数组分配内存(https://dev59.com/fGMm5IYBdhLWcg3wFL7s#23917034)。

3

一些想法:

  1. 我唯一调用方法的方式是使用 crwap 或者 ccall
    var length = Module.ccall('stringLen', ['string'], 'number');
  2. 你是否在 EXPORTED_FUNCTIONS 参数中包含了 stringLenstringTest
    emcc hello_world.cpp ... -s EXPORTED_FUNCTIONS=['_stringLen','_stringTest']

查看这里以获取更多细节:
http://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html

或者看一下我的 hello_world 教程:
http://www.brightdigit.com/hello-emscripten/

希望对你有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接