封装流程

  1. 在 C++ 中声明
    1. 创建 JQFunctionTemplate 对象
    2. 设定函数指针,变量字段
    3. 通过 GetFunction 生成相应的 JS 函数
  2. 在 JS 中使用

    1. new 来创建 JS 对象
    2. 通过 JS 对象的属性调用 C++ 相应成员函数

      封装例子

  3. 将 C++对象 ObjectCreator 和“成员变量”设定在 InstanceTemplate 中

  4. 将“成员方法”设定在 PrototypeTemplate 中

在 C++ 中声明

  1. #include "jsmodules/JSCModuleExtension.h"
  2. #include "jquick_config.h"
  3. #include "JQuickContext.h"
  4. #include <chrono>
  5. #include <thread>
  6. #include "jqutil/JQFunctionTemplate.h"
  7. #include "jqutil/JQTypes.h"
  8. // 类型 & 全局变量声明
  9. class TstClass;
  10. using TstClassRef = JQRef<TstClass>;
  11. static pthread_t _loop_pid = 0;
  12. static JQuick::Mutex g_mutex;
  13. static std::vector<TstClassRef> g_tstRefs;
  14. enum KeyAction { UP = 0, DOWN = 1, REPEAT = 2 };
  15. void _processKeyEvent(std::string keyCode, int32_t action, int64_t timestamp);
  16. // 独立线程 loop 函数,模拟按键读取分发
  17. void* _loop_func(void* data)
  18. {
  19. while(1) {
  20. std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  21. int64_t timestamp = std::chrono::seconds(std::time(NULL)).count();
  22. _processKeyEvent("power_key", KeyAction::DOWN, timestamp);
  23. std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  24. _processKeyEvent("power_key", KeyAction::UP, timestamp);
  25. }
  26. return NULL;
  27. }
  28. // TstClass 声明
  29. class TstClass: public JQBaseObject {
  30. public:
  31. TstClass(const std::string &name): _name(name) {}
  32. // JS 对象已销毁,此时解除全局对 C++ 对象的引用
  33. virtual void OnGCCollect()
  34. {
  35. printf("call OnGCCollect\n");
  36. JQuick::Mutex::Autolock l(g_mutex);
  37. auto iter = std::find(g_tstRefs.begin(), g_tstRefs.end(), this);
  38. if (iter != g_tstRefs.end()) {
  39. printf("call OnGCCollect erase itself %p\n", this);
  40. g_tstRefs.erase(iter);
  41. }
  42. }
  43. virtual void OnGCMark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
  44. { printf("OnGCMark should to mark some JSValues\n"); }
  45. void helloFunc(JQFunctionCallbackInfo& info)
  46. {
  47. printf("called in helloFunc\n");
  48. const std::string msg = "hello return message " + _name;
  49. info.GetReturnValue().Set(msg);
  50. }
  51. void nameGetter(JQFunctionCallbackInfo& info)
  52. {
  53. info.GetReturnValue().Set(_name);
  54. }
  55. void nameSetter(JQFunctionCallbackInfo& info)
  56. {
  57. JQString name(info.GetContext(), info[0]);
  58. _name = name.get();
  59. }
  60. void asyncFunc(JQAsyncCallbackInfo &info) {
  61. printf("called into asyncFunc");
  62. info.postCallbackJSON("{\"hello\": \"back\"}");
  63. }
  64. public:
  65. JQSignal<std::string, int32_t, int64_t> keySignal;
  66. JQProperty<int32_t> intProp;
  67. private:
  68. std::string _name;
  69. };
  70. // 事件分发实现,直接向 keySignal 发送信号即可抛到 JS 线程
  71. void _processKeyEvent(std::string keyCode, int32_t action, int64_t timestamp)
  72. {
  73. JQuick::Mutex::Autolock l(g_mutex);
  74. for (auto iter: g_tstRefs) {
  75. iter->keySignal.emit(keyCode, action, timestamp);
  76. }
  77. }
  78. void helloFuncNonMember(JQFunctionCallbackInfo& info)
  79. {
  80. printf("called in helloFunc non-member\n");
  81. TstClass *cppObj = info.UnwrapThis<TstClass>();
  82. cppObj->keySignal.emit("mock_key", KeyAction::REPEAT, 9999999999999);
  83. info.GetReturnValue().Set("return from helloFunc non-member");
  84. }
  85. void asyncFuncNonMember(JQAsyncCallbackInfo &info)
  86. {
  87. printf("called into asyncFunc non-member");
  88. info.postCallbackJSON("{\"hello\": \"back from non-member\"}");
  89. }

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

onAsync 输出
image.png

效果

image.png