JavaScript如何调用C++模块中的函数

 更新时间:2024年01月24日 15:50:06   作者:Hens_Hello_Mr  
这篇文章主要给大家介绍了关于JavaScript如何调用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);

总结 

到此这篇关于JavaScript如何调用C++模块中函数的文章就介绍到这了,更多相关JS调用C++模块的函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • js判断手机是否安装并打开app,未安装则安装app【兼容Android、ios,亲测可用】

    js判断手机是否安装并打开app,未安装则安装app【兼容Android、ios,亲测可用】

    这篇文章主要介绍了js判断手机是否安装并打开app,未安装则安装app,通过调用浏览器判断app,兼容Android、ios等系统,,需要的朋友可以参考下
    2023-05-05
  • JS制作简单的三级联动

    JS制作简单的三级联动

    本文给大家分享的是使用javascript实现的一个简单的三级联动菜单,非常简单实用,有需要的小伙伴过来参考下吧。
    2015-03-03
  • JavaScript 常用函数库详解

    JavaScript 常用函数库详解

    在WEB开发中,javascript提供了许多函数供开发人员使用,这些函数在Ajax流行前足够了,但要构建一个交互性强些的应用恐怕就麻烦了。
    2009-10-10
  • JavaScript中清空数组的几种方法

    JavaScript中清空数组的几种方法

    本文主要介绍了JavaScript中清空数组的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • 通过实例解析js可枚举属性与不可枚举属性

    通过实例解析js可枚举属性与不可枚举属性

    这篇文章主要介绍了通过实例解析js可枚举属性与不可枚举属性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • 使用requestAnimationFrame实现js动画性能好

    使用requestAnimationFrame实现js动画性能好

    requestAnimationFrame优于setTimeout/setInterval的地方在于它是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销,这篇文章给大家详细介绍使用requestAnimationFrame实现js动画
    2015-08-08
  • JS正则表达式封装与使用操作示例

    JS正则表达式封装与使用操作示例

    这篇文章主要介绍了JS正则表达式封装与使用操作,涉及javascript使用正则表达式针对邮箱、手机号、身份证、用户名、中文等简单验证操作技巧,需要的朋友可以参考下
    2019-05-05
  • 动态添加js事件实现代码

    动态添加js事件实现代码

    动态添加js事件,主要是不用具体指定位置的事件,这种动态添加事件的方法比较方便,并可以扩展等。
    2009-03-03
  • 用js解决数字不能换行问题

    用js解决数字不能换行问题

    当一串数字的时候,很多浏览器不能自动换行,所以可以借助js来实现。
    2010-08-08
  • JavaScript中$.ajax()最新用法举例详解

    JavaScript中$.ajax()最新用法举例详解

    这篇文章主要介绍了JavaScript中$.ajax()最新用法的相关资料,包括基础语法、现代回调方式、异步处理、高级场景示例、错误处理、安全注意事项、替代方法以及调试技巧,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-04-04

最新评论