当前位置: 首页 > 科技观察

Node.jsAddons零基础开发:参数和返回值处理

时间:2023-03-18 14:58:00 科技观察

本文介绍在使用Node为Node.js开发C-basedAddons时,如何接收和处理Node.js层、Node-API传递的参数-API如何转换参数类型和C类型,使用CMake.js构建源码。实现两个整数相加下面是一个用C语言编写的两个整数相加函数的简单例子。intadd(inta,intb){intsum=a+b;returnsum;}创建项目n-api-calculator,文件结构如下:├──app.js├──CMakeLists.txt└──src└──calculator.c编码实现这次我们主要实现计算器.c文件,首先导入下面两个头文件。#include#include定义一个通用的参数校验宏。Node-API提供的一些API会返回状态供我们判断操作是否成功。代码中会展示类似的通用判断逻辑。它出现了很多次。这里首先将status的判断封装成一个宏,如下示例代码所示,第一个参数env为上下文信息,第二个参数call为传入的回调函数;#defineNAPI_STATUS_CALL(env,call)\do{\napi_statusstatus=(call);\if(status!=napi_ok){\constnapi_extended_error_info*error_info=NULL;\napi_get_last_error_info((env),&error_info);\boolis_pending;\napi_isp_ending(ption(&is_pending);\if(!is_pending){\constchar*message=(error_info->error_message==NULL)\?“空错误信息”\:error_info->error_message;\napi_throw_error((env),NULL,消息);\返回空值;\}\}\}while(0)defineaddFnfunctionnapi_valueaddFn(napi_envenv,napi_callback_infoinfo){...}getNode.js层传递的参数使用Node-API提供的napi_get_cb_info方法获取napi_callback_info上下文信息。这块是Node.js层调用函数时传入的参数信息。下面是napi_get_cb_info方法的定义:napi_statusnapi_get_cb_info(napi_envenv,napi_callback_infocbinfo,//传递给回调函数的信息,这块是Node.js层传递的值size_t*argc,//指定长度提供给argv的数组,并接收参数lengthnapi_value*argv,//存放参数的地方,只复制指定argc个数的参数,如果小于argc指定的个数,则剩余参数指定为Node-API提供的值undefinednapi_value*thisArg,//接收JavaScript参数thisvoid**data//接收回调数据指针)定义参数个数argc为2,并定义数组argv用于存储参数同理,关键是napi_get_cb_info方法。在C语言中,可以通过&符号获取变量对应的内存地址,所以变量argc会随着实际参数个数的变化而变化。napi_valueaddFn(napi_envenv,napi_callback_infoinfo){size_targc=2;napi_valueargv[2];NAPI_STATUS_CALL(env,napi_get_cb_info(env,info,&argc,argv,NULL,NULL));}传递给Node.js层的参数校验处理有时候我们需要对参数得到的参数做一些校验。如果它们不符合我们的期望,我们希望抛出一些异常。Node-API还为我们提供了一些用于错误消息的API。例如napi_throw_type_error抛出TypeError类型错误,n_api_napi_throw_range_error抛出RangeError类型错误,更多信息见n_api_exceptions。napi_valueaddFn(napi_envenv,napi_callback_infoinfo){...if(argc<2){napi_throw_error(env,"BadParameter","需要两个参数");返回空值;}}另一个错误处理是获取参数Type,做类型校验,可以通过napi_typeof函数获取一个参数的类型。napi_valueaddFn(napi_envenv,napi_callback_infoinfo){...napi_valuetypeargv1Type,argv2Type;NAPI_STATUS_CALL(env,napi_typeof(env,argv[0],&argv1Type));NAPI_STATUS_CALL(env,napi_typeof(env,argv[1]),&argv2Type);if(argv1Type!=napi_number||argv2Type!=napi_number){napi_throw_type_error(env,"ParameterTypeError","参数必须是数字类型");返回空值;}}Node-API与C类型交换Node-API类型的参数不能直接传递给C函数,这里需要一层转换。比如在Node.js中我们要表示一个整数,使用Number类型,那么如果传递给C函数,可以使用Node-API提供的函数napi_get_value_int32()函数转换为C中的int类型语言。其他类型同理,参考Node-API转C类型函数。napi_valueaddFn(napi_envenv,napi_callback_infoinfo){...inta,b;NAPI_STATUS_CALL(env,napi_get_value_int32(env,argv[0],&a));NAPI_STATUS_CALL(env,napi_get_value_int32(env,argv[1],&b));}add()这个函数是我们用标准的C类型定义的。这是一个很简单的例子,但是原理是一样的。现在传入我们转换后的参数a和b就可以正常运行了,但是add函数的返回值是C类型的值,所以需要从C类型转换成Node-API支持的类型。如下例所示,使用Node-API提供的napi_create_int32()函数将C类型转换为Node-API类型。其他类似的类型也是如此。参考C类型转Node-API函数。intadd(inta,intb){intsum=a+b;返回总和;}napi_valueaddFn(napi_envenv,napi_callback_infoinfo){...napi_valuesum;NAPI_STATUS_CALL(env,napi_create_int32(env,add(a,b),&sum));returnsum;}ModuleRegistration模块注册在第一章已经讲解过了,同样的,我们这次注册模块的名字是calculator。napi_valueinit(napi_envenv,napi_valueexports){napi_property_descriptorproperties[]={{"add",NULL,addFn,NULL,NULL,NULL,napi_default,NULL}};NAPI_STATUS_CALL(env,napi_define_properties(env,exports,1,属性));返回出口;}NAPI_MODULE(计算器,初始化);除了用node-gyp来构建CMake.js(上一篇文章用过这个方法),使用CMake.js也是一个不错的选择,它是基于CMake的构建系统,它不需要你有一个Python环境已安装。在开始之前,使用NPM安装npminstall-gcmake-js。项目根目录创建一个CMakeLists.txt文件,写入如下内容:CMAKE_MINIMUM_REQUIRED(VERSION3.2FATAL_ERROR)#项目名称(将是插件名称)project(calculator)#如果有多个文件用空格分隔#file(GLOBCOMMONM_FILES"src/calculator.csrc/other.c")file(GLOBCOMMONM_FILES"src/calculator.c")#如果是跨平台,请参考下面的方法#IF(WIN32)#file(GLOBOS_FILES"src/win.c")#ELSE(WIN32)#file(GLOBOS_FILES"src/linux.c")#ENDIF(WIN32)#set(SOURCE_FILES${COMMONM_FILES}${OS_FILES})set(SOURCE_FILES${COMMONM_FILES})#从src/`add_library(${PROJECT_NAME}SHARED${SOURCE_FILES})#中构建一个以项目命名的共享库#给我们的库文件一个没有任何“lib”前缀的.node扩展名set_target_properties(${PROJECT_NAME}PROPERTIESPREFIX""SUFFIX".node")#Essentialincludefilestobuildanodeaddon,#你应该在每个基于CMake.js的项目中添加这一行target_include_directories(${PROJECT_NAME}PRIVATE${CMAKE_JS_INC})#Essentiallibraryfilestolinktoanodeaddon#你应该在每个基于CMake.js的项目中添加这一行target_link_libraries(${PROJECT_NAME}${CMAKE_JS_LIB})运行cmake-jsbuild,可能生成一个./build/Release/calculator.node文件应用测试创建app.js可以测试下。const{add}=require('bindings')('calculator');//console.log(add(1));//错误:需要两个参数//console.log(add('1',2));//TypeError:参数必须是类型numberconsole.log(add(1,2));//3参考https://nodejs.org/api/n-api.htmlhttps://www.linuxjournal.com/article/6700https://discourse.urho3d.io/t/how-do-you-include-source-在子目录/871/2