Best way to pass the C++ object's "this" pointer to a JavaScript callback method?

22 views
Skip to first unread message

Jeremy Friesner

unread,
Mar 18, 2025, 7:59:41 PMMar 18
to emscripten-discuss
I'm using the "Non-abstract virtual methods" approach (i.e. with optional_override()) to allow my JavaScript code to subclass a C++ class and receive callback methods on it.  This works okay, but currently my JavaScript code needs to maintain a Map from each JavaScript-object to its associated C++-object (as returned by implement()) in order to call methods on the C++ superclass, and that seems like an unnecessary burden to place on the JavaScript programmer.

What I'd like to do instead is pass the C++ "this" pointer to the JavaScript callback method, so that the JavaScript callback method can access it directly without any external bookkeeping.  Something like the code at the bottom of this post.

However, when I try it, I get a static_assert failure when compiling the C++ bindings: 

static assertion failed due to requirement '!std::is_pointer<ISomeInterfaceClass*>::value': Implicitly binding raw pointers is illegal.  Specify allow_raw_pointer<arg<?>>
  117 |     static_assert(!std::is_pointer<T*>::value, "Implicitly binding raw pointers is illegal.  Specify allow_raw_pointer<arg<?>>");


My question is, how can I specify allow_raw_pointer<arg<0>> in a call<> call?  Or is there another elegant/idiomatic way to do what I want to do, that I should be using instead?

Thanks,
Jeremy

-----

class ISomeInterfaceClass
{
public:
   virtual void SomeCallbackMethod() {/* empty */}

   void DoSomethingElse() {...}
};

struct ISomeInterfaceClassWrapper : public wrapper<ISomeInterfaceClass>
{
   EMSCRIPTEN_WRAPPER(ISomeInterfaceClassWrapper);

   virtual void SomeCallbackMethod() {call<void>("SomeCallbackMethod", this);}  // Trying to pass the this-pointer causes a static_assert failure!
};

EMSCRIPTEN_BINDINGS(ISomeInterfaceClassWrapper)
{
   class_<ISomeInterfaceClass>("ISomeInterfaceClass")
      .allow_subclass<ISomeInterfaceClassWrapper>("ISomeInterfaceClassWrapper", constructor<IAutomationUpdateGateway*>())
      .function("SomeCallbackMethod", optional_override([](ISomeInterfaceClass& self) {return self.ISomeInterfaceClass::SomeCallbackMethod();}))
}


// JavaScript code
var myJavaScriptSubclassObject =
{
   SomeCallbackFunction : function(cppThis)
   {  
      console.log("SomeCallbackFunction was called!");
      cppThis.DoSomethingElse();
   }
};

var mySubclassObject = Module.ISomeInterfaceClass.implement(myJavaScriptSubclassObject);
[...]

Reply all
Reply to author
Forward
0 new messages