Hi all,
I'm having some challenges getting callbacks to JS from C++ using WebIDL where the callback interface is declared in C++.
I'm using emscripten 1.38.6.
The challenge seems to be that the pointer to the JS class has no recollection that it is of the JS class and the virtual C++ implementation is called.
I have tried to follow the format in Box2d, but with little luck.
The WebIDL for the test is simple:
interface CallbackListener {
};
[JSImplementation="CallbackListener"]
interface JSCallbackListener {
void JSCallbackListener();
void sessionConnectResult(long nResult, DOMString strSessionId);
};
interface Client {
void Client([Ref, Const] DOMString strName);
void startSession();
void registerCallbackListener(JSCallbackListener listener);
void unregisterCallbackListener(JSCallbackListener listener);
DOMString strName();
};
Here is the CallbackListener.hpp
#pragma once
#include <cstdint>
#include <experimental/optional>
#include <string>
#include <emscripten.h>
#define NM_NOT_USED(x) ((void)(x))
class CallbackListener {
public:
virtual ~CallbackListener() {}
virtual void sessionConnectResult(int32_t nResult, const std::experimental::optional<std::string> & strSessionId) {
EM_ASM({
console.log('in C++ sessionConnectResult with ' + $0 + ' and ' + UTF8ToString($1) + '!');
}, nResult, strSessionId->c_str());
NM_NOT_USED(nResult);
NM_NOT_USED(strSessionId);
};
};
The EM_ASM is just for debugging.
In the Client class in C++ the registerCallbackListener takes a CallbackListener * and saves it in an ivar. When startSession is called, it calls a protected notifySessionConnectResult method. In that method, if the ivar is set, sessionConnectResult is invoked. Here is the definition of that method:
void Client::notifySessionConnectResult(int32_t nResult, const std::experimental::optional<std::string> & strSessionId) {
if (m_listener) {
EM_ASM({
console.log(' about to call sessionConnectResult on ' + $0);
var l = wrapPointer($0, JSCallbackListener);
l.sessionConnectResult($1, UTF8ToString($2));
console.log(' after call sessionConnectResult on ' + $0);
}, m_listener, nResult, strSessionId ? strSessionId->c_str() : "");
m_listener->sessionConnectResult(nResult, strSessionId);
}
}
The line l.sessionConnectResult(...) inside the EM_ASM block does invoke the JSCallbackListener instance's implementation of sessionConnectResult, but the m_listener->sessionConnectResult(...) invokes the CallbackListener method. I expected that call to also invoke the JSCallbackListener implementation. Could someone point me to something I have missed?
Perhaps something needs to be added to the wrapper?
I have a zip of the project with an example index.html file to demonstrate, but I seem to be having trouble attaching files, so here is a dropbox link:
Any idea would be appreciated. If my expectations are incorrect that the m_listener->sessionConnectResult(...) will never invoke the JSCallbackListener method, please let me know. If I've simply missed something in the wrapper or WebIDL that would fix it that would be excellent.
Thanks
Kevin