Call C++ function from Javascript - I need example that Actually works

1,666 views
Skip to first unread message

Gary Stuart

unread,
Jul 2, 2021, 2:25:40 PM7/2/21
to emscripten-discuss
Hi,

I am trying to use emscripten on Windows 10.
I have an existing Javascript client application that works.
I want to connect in c++ code, and be able to call those c++ functions when needed.
I have spent 1.5 days and can't even make a simple example work.

I am using emcc version: 2.0.25 (Online the latest version seems to be 2.0.21???)
I am using Chrome

My example:
function factorial()
{
    console.log("MT:F")
    Module.onRuntimeInitialized = _ => {
        console.log("A")
        const  factorialCpp = Module.cwrap('factorial', 'number', ['number']);
        console.log(factorialCpp)
        var result = factorialCpp(10);
        console.log(result)
    };   
    console.log("MT:F-E")
}

When I call this function, I get only the "MT:F" and "MT:F-E", and no errors in browser console.

My compile line:
emcc -O3 -s WASM=1 -s EXPORTED_RUNTIME_METHODS='["cwrap"]' -s EXPORTED_FUNCTIONS="['_factorial']" factorial.cpp factorial-service.cpp -o factorial.js

If optimization is -O0, it complains about the cwrap export

I have also tried:
function factorial2()
{
    Module['onRuntimeInitialized'] = onRuntimeInitializedFactorial;
    const factorialCpp = Module.cwrap('factorial', 'number', ['number']);

    function onRuntimeInitializedFactorial()
    {
        console.log("MT:F")
            console.log("A")
            var result = factorialCpp(10);
            console.log(result)
        console.log("MT:F-E")
    }
}

and:
function factorial3()
{
    var factorialCpp = Module.cwrap("factorial", "number", ["number"]);
    var result = factorialCpp(10);
}

Both produce an error: Uncaught TypeError: Module.cwrap is not a function

I have found over 20 examples, that say just do this or that, but nothing works.
Maybe my environment has a config issue?

I would appreciate any any guidance on this.
Thanks,
Gary




Alon Zakai

unread,
Jul 2, 2021, 4:05:18 PM7/2/21
to emscripte...@googlegroups.com
To use cwrap(), you must export it. See


You can also run an example from the tutorial, which I verified now,


It does have one typo which I'll fix now, the require should be of "api_example.js".

--
You received this message because you are subscribed to the Google Groups "emscripten-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to emscripten-disc...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/emscripten-discuss/56840ae5-46b1-458d-b30f-27ee5433fd39n%40googlegroups.com.

Sam Clegg

unread,
Jul 2, 2021, 4:53:15 PM7/2/21
to emscripte...@googlegroups.com
Also, I believe you need to setup `Module['onRuntimeInitialized']` early on, like at the top level of your script, preferably even before the module JS file is imported.    IIUC, you need to make sure you register this callback before the module is actually initialized, otherwise it might never be called.   

Gary Stuart

unread,
Jul 2, 2021, 5:21:28 PM7/2/21
to emscripten-discuss
Okay, so I took an example:

#include <math.h>
extern "C" {
   int int_sqrt(int x) {
      return sqrt(x);
   }
}

and put it in the file hello_function.cpp 

I ran their command, in an emcmdprompt, and it produced errors:
emcc hello_function.cpp -o function.html -s EXPORTED_FUNCTIONS='["_int_sqrt"]' -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'

I found that swapping the double and single quotes solved that problem:
emcc hello_function.cpp -o function.html -s EXPORTED_FUNCTIONS="['_int_sqrt']" -s EXPORTED_RUNTIME_METHODS="['ccall','cwrap']"

I put this line in the html file:
    <script type="text/javascript" src="/cpp/sqrt/function.js"></script>

When I load the html file I get lots of errors:
Uncaught (in promise) TypeError: Cannot read property 'apply' of undefined
    at Module._emscripten_stack_init (function.js:1832)
    at stackCheckInit (function.js:2104)
    at run (function.js:2116)
    at runCaller (function.js:2095)
    at removeRunDependency (function.js:1451)
    at receiveInstance (factorial.js:1)
    at receiveInstantiationResult (factorial.js:1)

Module._emscripten_stack_init @ function.js:1832
stackCheckInit @ function.js:2104
run @ function.js:2116
runCaller @ function.js:2095
removeRunDependency @ function.js:1451
receiveInstance @ factorial.js:1
receiveInstantiationResult @ factorial.js:1
function.js:1477 Uncaught (in promise) RuntimeError: abort(Assertion failed: undefined) at Error
    at jsStackTrace (function.js:1755)
    at stackTrace (function.js:1772)
    at abort (function.js:1471)
    at assert (function.js:678)
    at removeRunDependency (function.js:1438)
    at receiveInstance (function.js:1630)
    at receiveInstantiationResult (function.js:1647)
    at abort (function.js:1477)
    at assert (function.js:678)
    at removeRunDependency (function.js:1438)
    at receiveInstance (function.js:1630)
    at receiveInstantiationResult (function.js:1647)


As to         setup `Module['onRuntimeInitialized']` early on
I'm not sure of the proper syntax. I tried 

    <script>
        Module['onRuntimeInitialized'] = onRuntimeInitializedFactorial;
    </script>

and I get the error:
Uncaught ReferenceError: Module is not defined

I feel like I'm missing something really basic that the rest of you just take for granted....

I look forward to more clarification

Gary Stuart

unread,
Jul 2, 2021, 9:53:07 PM7/2/21
to emscripten-discuss
So I figured out that Module was not defined.

And after a lot of failed examples, found this code that works...
<script type="module">
        import Module from '../cpp/sqrt2/example.js'
        Module().then(function(mymod) {
            console.log("Module Loaded")
            const int_sqrt = mymod.cwrap('int_sqrt', 'number', ['number']);
            console.log(int_sqrt(64));
        }); 
</script>

when compiled with:
emcc example.cpp -o example.js -s EXPORTED_FUNCTIONS="['_int_sqrt']" -s EXPORTED_RUNTIME_METHODS="['ccall', 'cwrap']" -s EXPORT_ES6=1 -s MODULARIZE=1

Now I just need to figure out how to split it up.
I want the call to int_sqrt to be callable from a javascript function

Thanks

Floh

unread,
Jul 6, 2021, 6:34:13 AM7/6/21
to emscripten-discuss
I wrote a "most minimal" C / JS interop demo a little while ago (only difference for C++ is that you need to wrap the exported functions in extern "C"):


The ccall helper function is only ever needed if you want marshalling from JS to C strings like here:


Cheers!

Sam Clegg

unread,
Jul 21, 2021, 12:26:35 PM7/21/21
to emscripte...@googlegroups.com
Regarding the shell quoting issue with EXPORTED_RUNTIME_METHODS/EXPORTED_FUNCTIONS, these days you can avoid all the quotes and just use simple comma separated lists:

e.g:  
-sEXPORTED_FUNCTIONS=_int_sqrt 
-sEXPORTED_RUNTIME_METHODS=ccall,cwrap 

Reply all
Reply to author
Forward
0 new messages