如何通过Emscripten将WebAssembly C++对象传入JavaScript回调
问题描述
使用Emscripten将C代码编译为WebAssembly,通过Embind实现C对象与JavaScript的绑定。当C对象作为Wasm调用的返回值或直接在JS中创建时,绑定工作正常;但在Wasm代码调用JavaScript回调并传入C结构体的场景中,JS回调收到的不是可调用方法的对象,而是一个数字(推测为对象内存地址),无法调用对象的GetA()、GetB()方法。
原因分析
你用extern "C"声明的testCallback是C风格的函数接口,Embind不会对这类接口的参数自动进行C++对象到JS绑定对象的包装转换,因此传入JS的是对象在Wasm内存中的原始地址值,而非Embind封装后的实例。
解决方案
有两种可行的解决方式:
方式一:改用Embind绑定回调函数
放弃extern "C"的声明,改用Emscripten提供的绑定方式定义回调类型,让Embind自动处理对象的包装:
修改后的C++代码
#include "emscripten/bind.h" #include <iostream> #include <functional> using emscripten::class_; using emscripten::function; class TestClass { public: int a; int b; int GetA() { return a; } int GetB() { return b; } }; // 定义回调类型 using TestCallback = std::function<void(TestClass)>; // 声明JS侧要实现的回调获取函数 extern "C" { extern TestCallback getTestCallback(); } void testFunction() { TestClass testClass; testClass.a = 5; testClass.b = 10; std::cout << "Calling js callback" << std::endl; auto callback = getTestCallback(); callback(testClass); std::cout << "Js callback called!" << std::endl; } EMSCRIPTEN_BINDINGS(test_project) { class_<TestClass>("TestClass") .constructor() .function("GetB", &TestClass::GetB) .function("GetA", &TestClass::GetA) .property("a", &TestClass::a) .property("b", &TestClass::b); // 可选:绑定成员变量方便直接访问 // 绑定回调类型及相关函数 function<TestCallback()>("getTestCallback"); function("testFunction", &testFunction); };
修改后的JS代码
// 提供回调函数给C++侧调用 Module.getTestCallback = function() { return function(testClass) { console.log(testClass); // 现在是Embind封装的对象实例 console.log(testClass.GetA()); // 输出5 console.log(testClass.a); // 如果绑定了property,可直接访问成员变量 }; };
方式二:手动包装内存地址(兼容extern "C"场景)
如果必须保留extern "C"的回调声明,可以在JS中用Embind提供的wrap方法,将内存地址转换为对应的对象实例:
修改后的JS代码
mergeInto(LibraryManager.library, { testCallback: function(testClassPtr) { // 用TestClass.wrap将内存地址包装成Embind对象 const testClass = Module.TestClass.wrap(testClassPtr); console.log(testClass); console.log(testClass.GetA()); // 输出5 console.log(testClass.GetB()); // 输出10 } });
注意:这种方式需要确保传入的地址指向的对象在JS回调执行期间仍然有效,避免内存释放导致的非法访问。
内容的提问来源于stack exchange,提问作者Nikolai Nikolov
相关产品推荐
相关产品推荐

