封装流程
- 在 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