emscripten-core / emscripten-core/emscripten
Embind: extend vs implement - double delete()
- Dominant language
- C++
- Stars
- 27.6k
- Forks
- 3.6k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 105
Description
Coming from the C++ world, I'm struggling to understand which of the two options is correct when I'm trying to bind the pure virtual C++ class and provide the implementation on the JS side.
Suppose you have an interface like this:
```cpp
// main.cpp
#include
#include
class IAction {
public:
virtual ~IAction() {
std::cout << "IAction::~IAction()" << std::endl;
}
virtual void PrintSelf() const = 0;
};
class IActionWrapper : public emscripten::wrapper {
public:
EMSCRIPTEN_WRAPPER(IActionWrapper);
~IActionWrapper() override = default;
void PrintSelf() const override {
return call("PrintSelf");
}
};
EMSCRIPTEN_BINDINGS(Factory) {
using namespace emscripten;
class_("IAction")
.function("PrintSelf", &IAction::PrintSelf, pure_virtual())
.allow_subclass("IActionWrapper");
}
```
And these are the two possible JS implementations that follow the embind docs example, one using `extend` and the other one `implement`:
```js
const ActionExtend = Module.IAction.extend("IAction", {
__construct: function(name: string) {
this.__parent.__construct.call(this);
this.name = name;
},
__destruct: function() {
this.__parent.__destruct.call(this);
},
PrintSelf() {
console.log("Action:", this.name);
}
})
const ActionImplement = (name: string) => {
return Module.IAction.implement({
name: name,
PrintSelf() {
console.log("Action:", this.name);
}
});
}
const A1 = new ActionExtend("A1");
A1.PrintSelf();
console.log("Action is deleted: ", A1.isDeleted())
A1.delete();
console.log("Action is deleted: ", A1.isDeleted())
const A2 = ActionImplement("A2");
A2.PrintSelf();
console.log("Action is deleted: ", A2.isDeleted())
A2.delete();
console.log("Action is deleted: ", A2.isDeleted())
```
This is the log that I see in the console:
```txt
Action: A1
Action is deleted: false
IAction::~IAction()
Action is deleted: false
Action: A2
Action is deleted: false
IAction::~IAction()
Action is deleted: true
```
Based on the log it seems like the `implement` version is the correct one. The `extend` even allows the double call of `.delete()` without any errors for the 2nd call, although the `IAction`'s destructor is only triggered once.
```js
const A3 = new ActionExtend("A3");
A3.PrintSelf();
console.log("Action is deleted: ", A3.isDeleted())
A3.delete();
console.log("Action is deleted: ", A3.isDeleted())
A3.delete(); // Double delete - still valid, no dtor called
```
Is this all correct and works as expected?
**Version of emscripten/emsdk:**
Emscripten 4.0.15
Contributor guide
Assessment
This issue has not been assessed yet.