封装流程
- 在 C++ 中声明
- 创建 JQFunctionTemplate 对象
- 设定函数指针,变量字段
- 通过 GetFunction 生成相应的 JS 函数
在 JS 中使用
将 C++对象 ObjectCreator 和“成员变量”设定在 InstanceTemplate 中
- 将“成员方法”设定在 PrototypeTemplate 中
在 C++ 中声明
#include "jsmodules/JSCModuleExtension.h"#include "jquick_config.h"#include "JQuickContext.h"#include <chrono>#include <thread>#include "jqutil/JQFunctionTemplate.h"#include "jqutil/JQTypes.h"// 类型 & 全局变量声明class TstClass;using TstClassRef = JQRef<TstClass>;static pthread_t _loop_pid = 0;static JQuick::Mutex g_mutex;static std::vector<TstClassRef> g_tstRefs;enum KeyAction { UP = 0, DOWN = 1, REPEAT = 2 };void _processKeyEvent(std::string keyCode, int32_t action, int64_t timestamp);// 独立线程 loop 函数,模拟按键读取分发void* _loop_func(void* data){while(1) {std::this_thread::sleep_for(std::chrono::milliseconds(1000));int64_t timestamp = std::chrono::seconds(std::time(NULL)).count();_processKeyEvent("power_key", KeyAction::DOWN, timestamp);std::this_thread::sleep_for(std::chrono::milliseconds(1000));_processKeyEvent("power_key", KeyAction::UP, timestamp);}return NULL;}// TstClass 声明class TstClass: public JQBaseObject {public:TstClass(const std::string &name): _name(name) {}// JS 对象已销毁,此时解除全局对 C++ 对象的引用virtual void OnGCCollect(){printf("call OnGCCollect\n");JQuick::Mutex::Autolock l(g_mutex);auto iter = std::find(g_tstRefs.begin(), g_tstRefs.end(), this);if (iter != g_tstRefs.end()) {printf("call OnGCCollect erase itself %p\n", this);g_tstRefs.erase(iter);}}virtual void OnGCMark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func){ printf("OnGCMark should to mark some JSValues\n"); }void helloFunc(JQFunctionCallbackInfo& info){printf("called in helloFunc\n");const std::string msg = "hello return message " + _name;info.GetReturnValue().Set(msg);}void nameGetter(JQFunctionCallbackInfo& info){info.GetReturnValue().Set(_name);}void nameSetter(JQFunctionCallbackInfo& info){JQString name(info.GetContext(), info[0]);_name = name.get();}void asyncFunc(JQAsyncCallbackInfo &info) {printf("called into asyncFunc");info.postCallbackJSON("{\"hello\": \"back\"}");}public:JQSignal<std::string, int32_t, int64_t> keySignal;JQProperty<int32_t> intProp;private:std::string _name;};// 事件分发实现,直接向 keySignal 发送信号即可抛到 JS 线程void _processKeyEvent(std::string keyCode, int32_t action, int64_t timestamp){JQuick::Mutex::Autolock l(g_mutex);for (auto iter: g_tstRefs) {iter->keySignal.emit(keyCode, action, timestamp);}}void helloFuncNonMember(JQFunctionCallbackInfo& info){printf("called in helloFunc non-member\n");TstClass *cppObj = info.UnwrapThis<TstClass>();cppObj->keySignal.emit("mock_key", KeyAction::REPEAT, 9999999999999);info.GetReturnValue().Set("return from helloFunc non-member");}void asyncFuncNonMember(JQAsyncCallbackInfo &info){printf("called into asyncFunc non-member");info.postCallbackJSON("{\"hello\": \"back from non-member\"}");}
JSAPI 导出声明
static int module_init(JSContext *ctx, JSModuleDef *m) {
JSValue module = JS_NewObject(ctx);
JQFunctionTemplateRef tpl = JQFunctionTemplate::New(ctx, "TstClass");
tpl->setFunc([](JQFunctionCallbackInfo &info) { printf("new TstClass called\n"); });
// 设定 C++ 对象工厂函数
tpl->InstanceTemplate()->setObjectCreator([]() {
if (!_loop_pid) {
// 创建监听/dev/input的事件线程循环
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 8192);
pthread_create(&_loop_pid, &attr, &_loop_func, NULL);
pthread_attr_destroy(&attr);
}
TstClass *cppObj = new TstClass("myTstClass");
{
JQuick::Mutex::Autolock l(g_mutex);
// 同时全局引用该 C++ 对象,用于后续 keySignal 分发使用
g_tstRefs.push_back(cppObj);
}
return cppObj;
});
// 将“成员方法”设定在 PrototypeTemplate 中
tpl->SetProtoMethod("helloFunc", &TstClass::helloFunc);
// 成员变量的 setter getter 函数
tpl->SetProtoAccessor("name", &TstClass::nameGetter, &TstClass::nameSetter);
// 普通 C 函数
tpl->PrototypeTemplate()->Set("helloFuncNonMember", helloFuncNonMember);
// 将“成员变量”设定在 InstanceTemplate 中
tpl->InstanceTemplate()->SetBool("boolField", false);
tpl->InstanceTemplate()->Set("intField", 100);
tpl->InstanceTemplate()->Set("floatField", 100.123);
tpl->InstanceTemplate()->Set("strField", "string value");
// 将 Signal 设定在 InstanceTemplate 中
tpl->InstanceTemplate()->Set("keySignal", &TstClass::keySignal);
// 将 JQProperty 设定在 InstanceTemplate 中
tpl->InstanceTemplate()->Set("intProp", &TstClass::intProp);
// 异步调用机制,独立线程调度
// 设置线程池名字
tpl->setAsyncThreadPoolName('MyThreadPoolName');
// 异步接口,成员函数
tpl->SetProtoMethodAsync("asyncFunc", &TstClass::asyncFunc);
// 异步接口,普通 C 函数
tpl->SetProtoMethodAsync("asyncFuncNonMember", asyncFuncNonMember);
// 生成 JS 函数,并导入 JS 空间
JSValue TstClassCls = tpl->GetFunction();
JS_SetPropertyStr(ctx, module, "TstClass", TstClassCls);
JS_SetModuleExport(ctx, m, "default", module);
return 0;
}
DEF_MODULE(tst, module_init)
回调参数说明
JQFunctionCallbackInfo 说明:
- Length() 获取参数长度, [] operator 获取参数,超出范围则返回 JS_UNDEFINED
- Holder(),持有 C++ 对象的 JSValue
- This(),JS 上下文的 this 变量
- GetContext(),当前 JSContext 环境
- UnwrapHolder(),获取 Holder() 持有的 C++ 对象
- UnwrapThis(),获取 This() 持有的 C++ 对象
如上字段在 SetProtoMethod() 等回调中可用
- NewTarget(),表示 new 时的具体 ctor,与js语法 new.target 一致
- IsConstructCall(),是否为 new 方式调用,即判断 NewTarget 是否为 JS_UNDEFINED
如上两字段在 setFunc 的回调中有效,由 JS new 语法触发
JQAsyncCallbackInfo 说明:
- params 获取入参,是 JS 端第一个参数,JSON 序列化字符串
postCallbackJSON,向 JS 端返回参数,注意这里字符串必须是严格 JSON 格式
JS代码使用
在 JS 中使用
异步方法入参说明:params:可序列化的参数对象,用于序列化后传递给 JSAPI C++ 层接口
- callback: 异步回调函数,当 JSAPI C++ 异步接口完成逻辑并触发 postCallbackJSON 时被调度
- callback 接收一个参数 res,为 JSAPI C++ 层返回的序列化结果 ```javascript import test_qt from “test_qt”
export default {
…
methods: {
onTestAsync() {
this.obj = new test_qt.TstClass()
this.obj.asyncFunc({
intVal: 100,
strVal: ‘str value’
}, (res) => {
console.log(got async callback ${JSON.stringify(res)})
})
this.obj.asyncFuncNonMember({
intVal: 100,
strVal: ‘str value’
}, (res) => {
console.log(got async callback ${JSON.stringify(res)})
})
},
onTest() {
let obj = new test_qt.TstClass()
console.log(typeof obj)
console.log(Object.keys(obj))
let token = obj.keySignal.on((...args) => {
console.log(`keySignal evt ${args}`)
})
setTimeout(() => {
obj.keySignal.off(token)
}, 3000)
let token1 = obj.intProp.on((val) => {
console.log(`got intProp changed ${val}, expect ${obj.intProp.value}`)
obj.intProp.off(token1)
})
console.log(obj.helloFunc())
obj.name = 'myTstClassChanged'
obj.intProp.value = 200
console.log(`name change to ${obj.name}`)
console.log(obj.helloFuncNonMember())
},
}
… }
onTest 输出
```cpp
TstClass ctor 0x7fa209458450
new TstClass called
JSCONSOLE: object __LOG
JSCONSOLE: keySignal,intProp,boolField,intField,floatField,strField,strProp,boolProp __LOG
called in helloFunc
JSCONSOLE: hello return message myTstClass __LOG
JSCONSOLE: got intProp changed 200, expect 200 __LOG
JSCONSOLE: name change to myTstClassChanged __LOG
called in helloFunc non-member
keySignal keyCode mock_key, action 2
JSCONSOLE: keySignal evt mock_key,2,9999999999999 __LOG
JSCONSOLE: return from helloFunc non-member __LOG
keySignal keyCode power_key, action 1
JSCONSOLE: keySignal evt power_key,1,1637416302 __LOG
keySignal keyCode power_key, action 0
JSCONSOLE: keySignal evt power_key,0,1637416302 __LOG
call OnGCCollect
call OnGCCollect erase itself 0x7fa209458450
TstClass dtor 0x7fa209458450
效果

