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

问题描述 投票:0回答:5

我正在学习 emscripten,在 C++ 和 JS 之间传递字符串时,我什至无法进行最基本的字符串操作。

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

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

从 javascript 调用为:

var len = _stringLen("hi.");

这对我来说是

0
。我如何使这项工作按预期进行?我应该在这里使用哪种字符串类型?
char const*
std::wstring
std::string
?似乎没有一个有效;我总是得到相当随机的值。

这只是开始...然后我该如何返回这样的 C++ 字符串?

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

JS 中:

var str = _stringTest();

再次,我无法找到一种方法来完成这项工作;我在 JS 中总是得到垃圾。

所以我的问题很明确:如何通过 Emscripten 在 JS 和 C++ 之间编组字符串类型?

javascript c++ emscripten
5个回答
14
投票

extern "C" 无法识别 std::string。

您可能想尝试一下:
测试.cpp

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

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

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

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

示例测试代码:
测试.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>

8
投票

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

所以如果你想使用std::string,那么你可以使用“Embind”或“WebIDL Binder”。参考这里

我更喜欢嵌入,所以这是您问题的示例代码。

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 时。

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


6
投票

其他答案没有解决如何从 C++ 返回字符串。这是一种通过引用传递字符串的方式将字符串从 C++ 传递到 javascript 的方法。

emscripten 文档声明了以下关于可以使用

cwrap
/
ccall
传递给 C/C++ 函数的类型(emscripten 文档):

类型为“number”(对应于 C 整数、浮点或通用指针的 JavaScript 数字)、“string”(对应于表示字符串的 C char* 的 JavaScript 字符串)或“数组”(对于 JavaScript 数组或对应于 C 数组的类型化数组;对于类型化数组,它必须是 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 为此提供了

allocateUTF8
UTF8ToString
函数:

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

因为我们正在将字符串转换为字符指针,所以我们也可以直接调用该函数,而不使用

cwrap
emscripten docs):
Module._stringTest(ptr)
。它需要一些额外的步骤,但现在您已将字符串从 C 传递到 javascript。

要使此示例正常工作,您可能需要使用以下标志进行编译:

-sEXPORTED_FUNCTIONS="['_stringTest','_malloc','_free']"
-sEXPORTED_RUNTIME_METHODS="['cwrap','allocateUTF8','UTF8ToString']"

有更通用的方法为其他类型的数组分配内存(https://stackoverflow.com/a/23917034/12510953)。


3
投票

一些想法:

  1. 我调用方法的唯一方法是使用
    crwap
    ccall

    var length = Module.ccall('stringLen', ['string'], 'number');
  2. 您的
    stringLen
    参数中是否包含
    stringTest
    EXPORTED_FUNCTIONS

    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/

希望有帮助。


0
投票

这个解决方案对我来说是一种块外方法“extern”。

#include <iostream>
#include <math.h>
#include <string.h>

int _strlen(std::string a) {
    return a.length();
}
extern "C" {

    int stringLen(char* p)
    {
        return _strlen(std::string(p));
    }
}

构建:

emcc rfc.cpp -o ../dist/rfc.html -sEXPORTED_FUNCTIONS=_stringLen -sEXPORTED_RUNTIME_METHODS=ccall,cwrap

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script async type="text/javascript" src="rfc.js"></script>
    <script type="text/javascript">
        window.onload = () => {

            var strlen = Module.cwrap('stringLen', 'number', ['string'])

            console.log(strlen('hola jc!!'))
        }
    </script>
</body>
</html>
© www.soinside.com 2019 - 2024. All rights reserved.