You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 03:05:38