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

如何使用QuickJS将C/C++变量、结构体暴露为JS全局变量并同步

单个int变量暴露实现

核心用JS属性的getter/setter机制,每次读写都直接操作C侧原生变量内存,避免值拷贝导致的不同步问题:

  • 首先定义对应属性的读写回调
// C侧全局偏好变量
int myPref = 0;

// jsPref的读回调
static JSValue js_get_myPref(JSContext *ctx, JSValueConst this_val, int magic) {
    return JS_NewInt32(ctx, myPref);
}

// jsPref的写回调
static JSValue js_set_myPref(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) {
    int32_t v;
    // 类型校验失败直接抛JS异常
    if (JS_ToInt32(ctx, &v, val)) {
        return JS_EXCEPTION;
    }
    myPref = v;
    return JS_UNDEFINED;
}
  • 把属性注册到JS全局对象
// 上下文初始化完成后执行一次即可
JSValue global_obj = JS_GetGlobalObject(ctx);
JS_DefinePropertyGetSet(ctx, global_obj, JS_NewAtom(ctx, "jsPref"),
    JS_NewCFunctionMagic(ctx, js_get_myPref, "get jsPref", 0, JS_CFUNC_generic_magic, 0),
    JS_NewCFunctionMagic(ctx, js_set_myPref, "set jsPref", 1, JS_CFUNC_generic_magic, 0),
    JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE);
JS_FreeValue(ctx, global_obj);

此时在JS中执行jsPref = 1会直接修改C侧myPref的值,读jsPref也会获取C侧变量的最新值。

结构体变量暴露实现

思路是自定义JS类,类实例内部存储C侧结构体的指针,每个结构体成员对应绑定类的getter/setter属性:

  • 先定义结构体和对应的JS类
// C侧结构体定义
typedef struct {
    int a;
    int b;
} MyPrefStruct;
// 全局配置实例
MyPrefStruct myPref = {0, 0};

// 类ID和析构函数定义
static JSClassID js_mypref_class_id;
static void js_mypref_finalizer(JSRuntime *rt, JSValue val) {
    // 如果是动态分配的结构体,这里加释放逻辑,全局实例可留空
    // MyPrefStruct *p = JS_GetOpaque(val, js_mypref_class_id);
    // free(p);
}
static const JSClassDef js_mypref_class_def = {
    .class_name = "MyPref",
    .finalizer = js_mypref_finalizer,
};
  • 定义结构体每个成员的读写回调
// 成员a的读回调
static JSValue js_mypref_get_a(JSContext *ctx, JSValueConst this_val, int magic) {
    MyPrefStruct *p = JS_GetOpaque(this_val, js_mypref_class_id);
    if (!p) return JS_EXCEPTION;
    return JS_NewInt32(ctx, p->a);
}
// 成员a的写回调
static JSValue js_mypref_set_a(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) {
    MyPrefStruct *p = JS_GetOpaque(this_val, js_mypref_class_id);
    if (!p) return JS_EXCEPTION;
    int32_t v;
    if (JS_ToInt32(ctx, &v, val)) return JS_EXCEPTION;
    p->a = v;
    return JS_UNDEFINED;
}

// 成员b的读回调
static JSValue js_mypref_get_b(JSContext *ctx, JSValueConst this_val, int magic) {
    MyPrefStruct *p = JS_GetOpaque(this_val, js_mypref_class_id);
    if (!p) return JS_EXCEPTION;
    return JS_NewInt32(ctx, p->b);
}
// 成员b的写回调
static JSValue js_mypref_set_b(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) {
    MyPrefStruct *p = JS_GetOpaque(this_val, js_mypref_class_id);
    if (!p) return JS_EXCEPTION;
    int32_t v;
    if (JS_ToInt32(ctx, &v, val)) return JS_EXCEPTION;
    p->b = v;
    return JS_UNDEFINED;
}
  • 绑定属性并注册全局实例
// 属性表定义
static const JSPropertyDef js_mypref_props[] = {
    {
        .name = "a",
        .getter = JS_NewCFunctionMagic(js_mypref_get_a, "get a", 0, JS_CFUNC_generic_magic, 0),
        .setter = JS_NewCFunctionMagic(js_mypref_set_a, "set a", 1, JS_CFUNC_generic_magic, 0),
        .flags = JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE,
    },
    {
        .name = "b",
        .getter = JS_NewCFunctionMagic(js_mypref_get_b, "get b", 0, JS_CFUNC_generic_magic, 0),
        .setter = JS_NewCFunctionMagic(js_mypref_set_b, "set b", 1, JS_CFUNC_generic_magic, 0),
        .flags = JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE,
    },
    {NULL} // 结束标记
};

// 运行时初始化时注册类,只执行一次
JS_NewClassID(&js_mypref_class_id);
JS_NewClass(rt, js_mypref_class_id, &js_mypref_class_def);

// 绑定属性到类原型
JSValue proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, proto, js_mypref_props, sizeof(js_mypref_props)/sizeof(js_mypref_props[0]) - 1);
JS_SetClassProto(ctx, js_mypref_class_id, proto);

// 创建绑定C侧结构体的实例,注册到全局对象
JSValue global_obj = JS_GetGlobalObject(ctx);
JSValue js_pref_inst = JS_NewObjectClass(ctx, js_mypref_class_id);
JS_SetOpaque(js_pref_inst, &myPref); // 存储C侧结构体指针到JS对象
JS_SetPropertyStr(ctx, global_obj, "jsPref", js_pref_inst);
JS_FreeValue(ctx, global_obj);

此时在JS中执行jsPref.a = 1;jsPref.b = 2会直接同步修改C侧myPref结构体的对应成员。如果有其他类型的结构体成员,仅需修改对应getter/setter里的类型转换逻辑即可,C++环境下把上述代码放在extern "C"块中即可直接编译使用。

内容的提问来源于stack exchange,提问作者moi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 15:45:05