JS调用C++模块中的函数

场景一:JS侧调用C++侧函数,并传递参数

JS侧调用C++侧函数

import testNapi from 'libentry.so'

const TAG = "beixiang";

@Entry
@Component
struct Index {
  build() {
    Row() {
      Column() {
        Text('调用testNapi.add')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            const result = testNapi.add(3,4);
            console.log(`${TAG} 调用testNapi.add的结果为${result}`);
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

C++侧方法实现

static napi_value Add(napi_env env, napi_callback_info info)
{
    // 获取 2 个参数,napi_value是对 JS 类型的封装
    size_t argc = 2;
    napi_value argv[2] = {nullptr};
    
    // 调用napi_get_cb_info方法,从 info 中读取传递进来的参数放入argv里
    napi_get_cb_info(env, info, &argc, argv , nullptr, nullptr);

    // 获取参数并校验类型
    napi_valuetype valuetype0;
    napi_typeof(env, argv[0], &valuetype0);
    napi_valuetype valuetype1;
    napi_typeof(env, argv[1], &valuetype1);
    
    // 调用napi_get_value_double把 napi_value 类型转换成 C++ 的 double 类型
    double value0;
    napi_get_value_double(env, argv[0], &value0);
    double value1;
    napi_get_value_double(env, argv[1], &value1);
    
    // 调用napi_create_double方法把 C++类型转换成 napi_value 类型
    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);
    
    // 返回 napi_value 类型
    return sum;
}
  • napi_get_cb_info (napi_env env, napi_callback_info cbinfo, size_t *argc, napi_value *argv, napi_value *this_arg, void **data)

    • env:调用 API 的环境
    • cbinfo:传递给回调函数的回调信息
    • argc:指定提供的 argv 数组的大小并接收参数的实际计数
    • argv:将表示参数的 napi_value 复制到的缓冲区
    • this_arg:接收调用的 JavaScript this 参数
    • data:接收回调的数据指针

    例如Add方法的代码,napi_get_cb_info(env, info, &argc, argv , nullptr, nullptr);,从 info 中读取传递进来的&argc个参数放入argv。

  • napi_get_value_double(napi_env env, napi_value value, double *result)

    把 napi_value 类型转换成 C++ 的 double 类型,供C++侧使用

  • napi_create_double(napi_env env, double value, napi_value *result)

    把 C++ double类型转换成 napi_value 类型,供JS侧使用

  • napi_create_function(napi_env env, const char *utf8name, size_t length, napi_callback cb, void *data, napi_value *result)

不同于以上定义函数,并在Init() 方法内声明 napi_property_descriptor 结构体导出函数的方式,使用 napi_create_function允许将C++侧函数创建为可供JS侧调用的函数对象,然后使用napi_set_named_property将创建的函数对象导出,以便可以从JS侧访问该函数,如下代码:

// xxx.cpp    
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_value fn;
    // 根据C++侧函数Add创建函数fn
    napi_create_function(env, nullptr, 0, Add, nullptr, &fn);
    // 将创建的函数fn导出,函数名为newAdd
    napi_set_named_property(env, exports, "newAdd", fn);
    
    napi_property_descriptor desc[] = {
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END
// index.d.ts
export const newAdd: (a: number, b: number) => number;

场景二:JS侧不传参给回调函数,C++侧接收JS侧回调函数并执行

JS侧调用C++侧函数

import testNapi from 'libentry.so'
const TAG = "beixiang";

@Entry
@Component
struct Index {
  build() {
    Row() {
      Column() {
        Text('调用无参回调函数')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            // 先注册无参回调函数
            testNapi.registerCallback(() => {
              const a = 2;
              const b = 3;
              return a + b;
            })
            // 调用无参回调函数
            const result = testNapi.handleCallbackWithoutParams();
            console.log(`${TAG} 调用无参回调函数的结果为${result}`);
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

C++侧注册回调函数

// js函数回调
static napi_ref callback = nullptr;
/**
 * 注册回调函数
 * @param env
 * @param info
 * @return 
 */
static napi_value RegisterCallback(napi_env env, napi_callback_info info) 
{
    size_t argc = 1;
    napi_value argv[1] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    
    napi_create_reference(env, argv[0], 1, &callback);
    
    return nullptr;
}

C++侧执行注册回调函数

/**
 * 执行回调函数,不带参数
 * @param env
 * @param info
 * @return 
 */
static napi_value HandleCallbackWithoutParams(napi_env env, napi_callback_info info)
{
    
    napi_value global;
    napi_get_global(env, &global);
    
    napi_value cb = nullptr;
    napi_get_reference_value(env, callback, &cb);

    napi_value result;
    napi_status status = napi_call_function(env, global, cb, 0 , nullptr, &result);

    if (status != napi_ok) return nullptr;
    
    return result;
}
  • napi_create_reference(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref *result)

    此 API 为传入的对象创建一个具有指定引用计数的新引用,例如注册回调函数RegisterCallback中的napi_create_reference(env, argv[0], 1, &callback),将JS侧传入的对象argv[0](对JS来说,函数也是对象)保存在callback中,供C++侧方法调用;

  • napi_get_reference_value(napi_env env, napi_ref ref, napi_value *result)

    将创建的引用ref保存到result中

  • napi_call_function(napi_env env, napi_value recv, napi_value func, size_t argc, const napi_value *argv, napi_value *result)

    • env:调用 API 的环境
    • recv:this 对象传递给被调用的函数,一般是当前环境的global对象,通过napi_get_global来获取。
    • func:表示要调用的 JavaScript 函数
    • argc:argv 数组中元素的计数。
    • argv:表示作为参数传递给函数的 JavaScript 值的 napi_values 数组
    • result:napi_value 表示返回的 JavaScript 对象

    在env环境下,在global对象中调用函数func,该函数参数数组为argv,有argc个参数,函数执行结果保存在result。此API允许从C++侧调用 JavaScript 函数对象,例如本文的napi_call_function(env, global, cb, 0 , nullptr, &result);

场景三:JS侧传参给回调函数,C++侧接收JS侧回调函数并执行

JS侧调用C++侧函数

import testNapi from 'libentry.so'
const TAG = "beixiang";

@Entry
@Component
struct Index {
  build() {
    Row() {
      Column() {
        Text('调用有参回调函数')
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            // 先注册无参回调函数
            testNapi.registerCallback((a: number, b: number) => {
              return a + b;
            })
            // 调用无参回调函数
            const result = testNapi.handleCallbackWithParams();
            console.log(`${TAG} 调用有参回调函数的结果为${result}`);
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

C++侧注册回调函数

/**
 * 注册回调函数
 * @param env
 * @param info
 * @return 
 */
static napi_value RegisterCallback(napi_env env, napi_callback_info info) 
{
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    napi_create_reference(env, args[0], 1, &callback);
    
    return nullptr;
}

C++侧执行注册回调函数

/**
 * 执行回调函数,带参数
 * @param env
 * @param info
 * @return 
 */
static napi_value HandleCallbackWithParams(napi_env env, napi_callback_info info)
{
    napi_value argv[2] = {nullptr};
    
    napi_valuetype valuetype0;
    napi_typeof(env, argv[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, argv[1], &valuetype1);
    
    double value1 = 2;
    double value2 = 3;

    // 创建两个double,给callback调用
    napi_create_double(env, value1, &argv[0]);
    napi_create_double(env, value2, &argv[1]);
    
    
    napi_value global;

    napi_get_global(env, &global);
    
    napi_value cb = nullptr;
    napi_get_reference_value(env, callback, &cb);
    
    napi_valuetype type;
    napi_typeof(env, cb, &type);

    napi_value result;
    // 调用回调函数
    napi_status status = napi_call_function(env, global, cb, 2, argv, &result);
    
    if (status != napi_ok) return nullptr;
    
    return result;
}

值得注意的是,本文JS侧传递给C++的回调函数是匿名函数,C++侧先将JS回调函数先在C++侧注册,即使用napi_create_reference将JS函数创建为ref,ref最终会作为napi_call_function的第三个参数,可以放心并没有在global对象里面直接去取函数引用。

另一种实现方式是JS侧传递给C++的回调函数是非匿名函数,使用napi_get_named_property在global对象中直接获取函数引用:

JS侧定义非匿名的回调函数:

function add(a: number, b:number) {
  return a + b;
}

C++侧从global对象中取出add函数,在napi_call_function引入:

napi_value global, add, arg;
napi_get_global(env, &global);
// 在global对象中取出名为"add"的对象/函数名,保存在add中。
napi_get_named_property(env, global, "add", &add);
...
// 调用add函数
napi_call_function(env, global, add, 2 , argv, &result);

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Node.js 可以通过 C++ 模块来扩展其功能,实现 C++ 模块函数调用需要使用 Node.js 提供的 `N-API` 接口。下面是一个简单的例子,演示了如何在 C++ 模块实现一个函数,并在 Node.js 调用函数并传递参数。 C++ 模块代码: ```cpp #include <node_api.h> napi_value add(napi_env env, napi_callback_info info) { size_t argc = 2; napi_value args[2]; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); int32_t a, b; napi_get_value_int32(env, args[0], &a); napi_get_value_int32(env, args[1], &b); napi_value result; napi_create_int32(env, a + b, &result); return result; } napi_value Init(napi_env env, napi_value exports) { napi_value fn; napi_create_function(env, nullptr, 0, add, nullptr, &fn); napi_set_named_property(env, exports, "add", fn); return exports; } NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) ``` 上述代码定义了一个 `add` 函数,该函数接受两个整数参数,返回它们的和。然后将该函数导出为 Node.js 模块的一个属性。 在 Node.js 调用函数: ```javascript const addon = require('./build/Release/addon'); console.log(addon.add(1, 2)); // 3 ``` 上述代码首先通过 `require` 函数引入了 C++ 模块,然后调用模块的 `add` 函数,并传入两个整数参数。`add` 函数返回它们的和,该和被输出到控制台上。 这是一个简单的例子,演示了如何在 C++ 模块实现函数,并在 Node.js 调用函数并传递参数。具体实现方式可以根据实际情况来进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值