Newbee trouble getting EMSCRIPTEN_BINDINGS to work

474 views
Skip to first unread message

Hanns Holger Rutz

unread,
Dec 27, 2020, 7:06:18 PM12/27/20
to emscripte...@googlegroups.com
Hi there,

I need some help getting one of the basic doc examples to work:

https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#classes

In other words, compile a basic example for Embind that instantiates a
C++ class from JavaScript and makes some calls on the instance methods.

This is the class with added imports as shown in the docs:

```cpp
// file: classes.cpp
#include <string>
#include <emscripten.h>
#include <emscripten/bind.h>

using namespace emscripten;

class MyClass {
public:
MyClass(int x, std::string y)
: x(x)
, y(y)
{}

void incrementX() {
++x;
}

int getX() const { return x; }
void setX(int x_) { x = x_; }

static std::string getStringFromInstance(const MyClass& instance) {
return instance.y;
}

private:
int x;
std::string y;
};

// Binding code
EMSCRIPTEN_BINDINGS(my_class_example) {
class_<MyClass>("MyClass")
.constructor<int, std::string>()
.function("incrementX", &MyClass::incrementX)
.property("x", &MyClass::getX, &MyClass::setX)
.class_function("getStringFromInstance",
&MyClass::getStringFromInstance)
;
}
```

And here is a JS snippet I will add as post-js:

```javascript
// file: classes_post.js
var instance = new Module.MyClass(10, "hello");
instance.incrementX();
instance.x; // 11
instance.x = 20; // 20
Module.MyClass.getStringFromInstance(instance); // "hello"
instance.delete();
```

My attempt to build:

em++ -O1 --bind -o classes.html --post-js classes_post.cpp classes.cpp

I serve the page with python simple-http-server, and when I open it, I
just get

But when I open the generated HTML, I only get:

```
classes.js:3553 Uncaught TypeError: Module.MyClass is not a constructor
at classes.js:3553
```

So I must be missing a crucial bit?

The problem is extra complicated for me because I'm not a C++
programmer, nor very good at JavaScript, so excuse me if I'm missing
something fundamental; I'm trying to port a C++ application to
WebAssembly, and this will be an important thing for me to solve.

The next step will be for me that I need to call instance methods from
JavaScript on an instance created from within C++ itself. How would I
pass an instance to JavaScript?

My naive attempt:

```cpp
int main() {
auto c = MyClass(10, "hello"); // I want to use _this_ instance

EM_ASM({
var instance = $0; // apparently that doesn't work
instance.incrementX(); // instance.incrementX is not a function
console.log('The value of x is now: ' + instance.x);
}, &c);

return 0;
}
```

If I log `$0`, that's an integer number, so I presume that is the heap
memory location of `&c`, and I need a way to map that to the binding API
in JavaScript.


Thanks!

Best,

.h.h.

OpenPGP_signature

Hanns Holger Rutz

unread,
Dec 28, 2020, 8:57:21 AM12/28/20
to emscripte...@googlegroups.com
Hi there,

I solve the first problem, and found a work-around the second

On 28/12/2020 01:06, Hanns Holger Rutz wrote:
[...]
> And here is a JS snippet I will add as post-js:

It seems that `--post-js` not necessarily runs after the module is
initialized. So that code must be wrapped in `addOnPostRun`, like so:

```javascript
addOnPostRun(function() {
console.log("here: 1");
var instance = new Module.MyClass(10, "hello");
instance.incrementX();
var x1 = instance.x; // 11
console.log("x is now: " + x1);
instance.x = 20; // 20
var s = Module.MyClass.getStringFromInstance(instance); // "hello"
console.log("string is now: " + s);
instance.delete();
console.log("here: 2");
});
```

(see https://github.com/emscripten-core/emscripten/issues/13116)

[...]
> The next step will be for me that I need to call instance methods from
> JavaScript on an instance created from within C++ itself. How would I
> pass an instance to JavaScript?

I haven't solved passing an actual instance, but in my case I will only
ever have _one_ instance of the class. In this case, one can use a
singleton-pattern as described here:

https://groups.google.com/g/emscripten-discuss/c/MimQol7peuQ

Ex:

```cpp
#include <emscripten/bind.h>
#include <emscripten.h>

class MyClass {
public:
MyClass() {}

void doSomething() {
printf("Doing something\n");
}
};

static MyClass* singleton_val;

MyClass* singleton() {
if (singleton_val == NULL) {
singleton_val = new MyClass;
}
return singleton_val;
}

EMSCRIPTEN_BINDINGS(my_module) {
emscripten::class_<MyClass>("MyClass")
.function("doSomething", &MyClass::doSomething);
function("singleton", &singleton, emscripten::allow_raw_pointers());
}

int main() {
EM_ASM({
Module.singleton().doSomething();
});
return 0;
}
```


Best, .h.h.

OpenPGP_signature
Reply all
Reply to author
Forward
0 new messages