16

I am learning emscripten, and I can't even get the most basic string manipulation working, when passing strings between C++ and JS.

For example, I would like to write a string length function. In C++:

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

Called from javascript as:

var len = _stringLen("hi.");

This yields 0 for me. How do I make this work as expected? Which string type should I use here? char const*? std::wstring? std::string? None seem to work; I always get pretty random values.

This is only the beginning... How do I then return a string from C++ like this?

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

And in JS:

var str = _stringTest();

Again, I cannot find a way to make this work; I always get garbage in JS.

So my question is clearly: How do I marshal string types between JS and C++ via Emscripten?

1
  • 1
    Related findings about the other direction of the problem (although this question is 7 years old): This problem asks about passing a string from JavaScript to C++. To pass a stiring FROM C++ to Javascript, one need UTF8ToString. Commented Nov 6, 2022 at 19:29

5 Answers 5

14

extern "C" doesn't recognize std::string.

You may want to try this:
Test.cpp

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

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

Use the following command to compile the cpp code :

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

Sample test code :
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>
Sign up to request clarification or add additional context in comments.

1 Comment

This doesn't answer the complete question which was how to pass strings both to and from the C++ code.
9

If you use extern "C" with a function, you cannot use C++ types in it's signature.

So if you want to use std::string, then you can use "Embind" or "WebIDL Binder". Refer here

I prefer embind, so this is a sample code for your problem.

P.S I am not sure how to pass variables by reference here, so doing it by value.

// 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);
}

Now in JS all you need to do is:

var myStr = "TestString";
Module.MyStrLen(myStr);

Make sure you pass the flag

--bind

when calling emcc.

There is another approach where you can do a Malloc on the C++ heap from JS and then do manipulations, but the above approach should be easier.

1 Comment

"If you use extern "C" with a function, you cannot use C++ types in it's signature." I see both answers claim this, but I don't think it's true. AFAIK this is just a linkage specification to prevent name mangling. I can compile extern "C" void foo(std::string s) { std::cout << "hello, world" << std::endl; } just fine
6

Other answers have not addressed how to return strings from C++. Here is a way to pass a string from c++ to javascript by passing a string by reference.

The emscripten documentation states the following about the types that can be passed to C/C++ functions using cwrap/ccall (emscripten docs):

The types are "number" (for a JavaScript number corresponding to a C integer, float, or general pointer), "string" (for a JavaScript string that corresponds to a C char* that represents a string) or "array" (for a JavaScript array or typed array that corresponds to a C array; for typed arrays, it must be a Uint8Array or Int8Array).

As the other answers have pointed out, you need to write the C function using a C string as the argument because that is the emscripten API (not because of extern "C").

If you want to return a string, you may think that you can just pass a C string (effectively by reference because it is pointer) and modify that string:

// 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

However, this does not work because a copy is created when passed to the function. So, you need to explicitly allocate the memory and pass a pointer instead. Emscripten provides the allocateUTF8 and UTF8ToString functions for this purpose:

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

Because we are transforming the string to a character pointer, we could have also called the function directly without using cwrap (emscripten docs): Module._stringTest(ptr). It requires some additional steps but now you have passed a string from C to javascript.

For this example to work, you may need to compile using the following flags: -sEXPORTED_FUNCTIONS="['_stringTest','_malloc','_free']" and -sEXPORTED_RUNTIME_METHODS="['cwrap','allocateUTF8','UTF8ToString']".

There are more general ways to allocate memory for arrays of other types (https://stackoverflow.com/a/23917034/12510953).

Comments

3

A few thoughts:

  1. The only way I have called a method is by using crwap or ccall?
    var length = Module.ccall('stringLen', ['string'], 'number');
  2. Are you including stringLen and stringTest in your EXPORTED_FUNCTIONS parameter?
    emcc hello_world.cpp ... -s EXPORTED_FUNCTIONS=['_stringLen','_stringTest']

Take a look here for more details:
http://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html

Or my hello_world tutorial:
http://www.brightdigit.com/hello-emscripten/

Hopefully that helps.

Comments

0

This solution worked for me with an out-of-block method, '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));
    }
}

build:

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.