手机版

用C/C编写node.js原生模块的方法教程

时间:2021-09-04 来源:互联网 编辑:宝哥软件园 浏览:

一直想知道如何用C/C编写nodejs原生模块,网上找到的博客大多停留在如何搭建环境,然后完成一个Hello World。甚至没有更多的参考资料。所以我整理了一下,分享在这里。

至于准备环境,我在网上抓了很多就不细说了。

主要指两个地方:

Nodejs官方文档v8文档,第一个是nodejs官方文档,介绍了几个很好的参考例子。

第二个是v8引擎文档,C的,C模块主要是这个文档写的。

好了,先从几个例子开始,逐步学习如何用c语言编写nodejs模块。

你好世界

你不能逃避习俗。你应该先写《你好世界》。毕竟程序员知道的第一个程序就是Hello World。

# include node . hvoid hello(const v 8:3360 functioncallbackinfov 83360: value args){ v 8:3360 isolate * isolate=args。GetIsolate自动消息=v 8:3360 string : newfroutf8(隔离,‘你好,世界!’);啊。GetReturnValue()。Set(消息);} void Initialize(v 8:3360 localv 8:3360 object导出){ NODE_SET_METHOD(导出,‘hello’,hello);} node _ module (module _ name,initialize)好了,这是最简单的HelloWorld,我们把这个文件命名为addon.cc,我们用node-gyp编译它,然后直接用require在我们的js文件中引入这个模块,然后我们就可以调用它了。

const myAddon=require('。/build/Release/addon’);console . log(MyAddon . hello());如无意外,终端会打印Hello World!

让我们简单看一下代码。第一行# includnode.h是将node.h的头文件引入C的代码,头文件可以理解为接口,其中我们只定义接口方法,不实现,然后通过其他文件实现,C的链接器负责将这两者链接在一起。

然后定义了一个没有返回值的方法hello()。方法参数通过const v 8: functioncallbackinfov 8:3360 value args传递。请注意,这里我们添加了前缀注释v833603360,或者我们可以在文件的开头直接使用v8。这样,您就不必每次都使用这个注释。

v 8: isolate * isolate=args。GetIsolate这里,我们访问函数中javascript的范围。

自动消息=v 8:3360 string : newfroutf8(隔离,‘你好,世界!’);我们创建了一个字符串类型的变量,并分配了Hello World!并将其绑定到范围。

我们通过参数得到函数的返回值。GetReturnValue()。

Initialize()方法用于初始化模块方法,并将该方法绑定到要导出的模块的方法名。

最后,NODE_MODULE导出这个模块。

上面的例子很简单,如果是js代码:

使用“严格”;let hello=函数hello() { let message='Hello World!';返回消息;};module . exports={ hello : hello };好了,第一个地狱世界结束了。网上很多关于nodejs C模块的博客文章到此结束。看完之后,我看起来又蠢又逼。这是什么?我想写另一个传递参数和简单操作参数的方法。

总和(a,b)

好吧。然后我们将编写另一个sum(a,b)函数,传递两个数值参数a,b,求这两个参数的和并返回。

js中的代码如下所示:

让sum=函数(a,b){ if(type of a==' number ' type of b==' number '){ return a b;}else{抛出新错误('错误的参数类型');}}那么,C应该怎么写:

void sum(const FunctionCallbackInfoValue args){ Isolate * Isolate=args。GetIsolateif(!args[0]-is number()){ isolate-throw exception(v 8:3360 exception 3360: type error(v 8:3360 string :3360 newfroutf8(isolate),' args[0]必须是数字'));返回;} if(!args[1]-is number()){ isolate-throw exception(v 8:3360 exception 3360: type error(v 8:3360 string :3360 newfroutf8(isolate),' args[1]必须是数字'));返回;} args。GetReturnValue()。set(args[0]-NumberValue()(args[1]-NumberValue());}首先判断两个参数是否为Number类型,如果不是,直接抛出异常。如果是,将返回值设置为两个参数的总和。

这里,我们不直接使用A和B作为参数列表中的参数,而是直接使用args对象。这类似于js,第一个参数是args[0],第二个参数是args[1]。

调用IsNumber()确定它是否为数字类型。如果不是,则引发类型错误类型错误异常。如果类型正常,使用args[0]-NumberValue()获取参数的数值,然后将其相加并赋给返回值。

也许你会问,args[0]这是什么?它的IsNumber()方法是怎么来的?我在哪里可以找到文件?

这其实就是v8引擎的内部类型,基本对应js的内置对象。您可以查阅v8类型描述文档。

上图比较熟悉,和js的类型系统很像。

数组、日期、函数、字符串等。js的都是从Object继承的,而在v8引擎中,Object和Primitive都是从Value类型继承的。

这里的IsNumber()方法是一个值类型的方法。那么除了这个方法还有什么呢?

上图我只剪了一小部分,都可以直接查阅。看,判断是不是数字类型的Number()方法,日期类型的IsDate()方法,数组的IsArray()方法等等,方法多种多样。

V8的界面也非常完善,即使是不精通C语言的开发者,也可以根据猫和老虎实现一个简单的模块。

Args[0]-NumberValue()返回一个双精度值。是的,这里是C中的双数型,可以直接加减。还有BooleanValue()方法等。用于获取不同类型的值。

在第二个例子中,我们简单地实现了sum()方法,传递两个参数并求和。但是这里只涉及整数值,如果还有其他类型的数值呢?比如数组。

sumOfArray(数组)

接下来,升级方法,传递一个数组,然后对数组中的所有值求和。Js的话:

让sumOfArray=function(array){ if(!数组。ISARRAY(数组)){抛出新错误('错误的参数,必须是数组类型');}让sum=0;for(let item of array){ sum=item;}返回总和;}逻辑很简单,就是遍历一次传递的数组,然后累加所有项。c是一样的:

void sumOfArray(const FunctionCallbackInfoValue args){ Isolate * Isolate=args。GetIsolateif(!args[0]-IsArray()){ isolate-throw exception(v 8:3360 exception 3360: type error(v 8:3360 string : newfroutf8(isolate,' args[0]必须是数组'))));返回;} LocalObject收到_ V8 _ obj=args[0]-TooObject();local array key=received _ V8 _ obj-getowntpropertynames();int Length=key-Length();双和=0;for(int I=0;ilengthI){ sum=received _ V8 _ obj-Get(key-Get(I))-number value();} args。GetReturnValue()。set(sum);}先判断是否是数组,没问题。

然后我们定义一个Object类型的received_v8_obj属性,并将其赋值为args[0]-tooobject()。在这里调用ToObject()方法将其转换为对象。

然后调用这个对象的GetOwnPropertyNames()方法获取所有的键,然后根据这些键获取对象的值进行累加。

为什么不直接把它转换成数组并遍历它呢?

众所周知,js中的数组不是真正的数组,本质上是一个对象。它的内部由键值对存储。因此,这里也是如此。值类型不提供直接转换为数组的ToArray()方法,而是将其转换为对象,并以对象的形式进行操作。

那么对象的操作是什么呢?看文件。

但是你会发现v8确实有一个Array类,它继承了Object类。那么数组的方法是什么呢?

看看文件就知道了,小惜:

因此,对数组的所有操作都将转换为对象操作。

createObj()

说到对象,我们来写一个创建对象的方法。传递两个参数,一个名字和一个年龄,创建一个代表一个人的名字和年龄的对象。

void create obj(const FunctionCallbackInfoValue args){ Isolate * Isolate=args。GetIsolatelocal object obj=object : new(隔离);obj-Set(string : newfroutf8(isolate,' name '),args[0]-ToString());obj-Set(string : newfroutf8(隔离,' age '),args[1]-to number());啊。GetReturnValue()。设置(obj);}这个方法,参考文献,基本没什么好说的。

通过Object:New(隔离)创建一个对象,然后设置两个属性名称、年龄、年龄,依次给这两个属性分配参数,然后返回这个对象。

如果您用js编写:

let createObj=函数(名称、年龄){ let obj={ };obj.name=name年龄=年龄;返回对象;};回收

上面提到的都没有提到js中一个重要的东西,回调函数。如果参数中有回调函数,应该如何执行?

让我们举一个简单的例子。

让cb=函数(a,b,fn){ if(typeof a!=='number' || typeof b!=='Number'){引发新错误('错误的参数类型,仅数字类型');} if(fn的类型!==' Function '){ throwneerror('参数fn的类型错误,只能是Function类型');} fn(a,b);};这个例子很简单。我们传递两个数值型参数A和B以及一个回调函数fn,然后调用以A和B作为fn参数的fn回调函数。这里,我们把A和B的操作转移到回调函数。在回调函数中,我们可以根据您的喜好进行求和或求积。

在这个例子中,还没有涉及到的是如何调用回调函数。

代码优先:

void CB(const FunctionCallbackInfoValue args){ Isolate * Isolate=args。GetIsolateif(!args[0]-is Number()){ isolate-throw exception(v 8:3360 exception 3360: type error(v 8:3360 string :3360 newfroutf8(isolate),' args[0]必须是数字'));} if(!args[1]-is Number()){ isolate-throw exception(v 8:3360 exception 3360: type error(v 8:3360 string :3360 newfroutf8(isolate),' args[1]必须是数字'));} if(!args[2]-is Function()){ isolate-throw exception(v 8:3360 exception 3360: type error(v 8:3360 string :3360 newfroutf8(isolate,' args[2]必须是函数')));} local function jsfn=local function :3360 cast(args[2]);local value argv[2]={ Number:New(隔离,args[0]-NumberValue()),number : new(隔离,args[1]-number value())};LocalValue c=jsfn-Call(Null(隔离),2,argv);啊。GetReturnValue()。set(c);}跳过以上三种判断参数类型。

我们定义了一个函数类型属性jsfn,并强制参数[2]为Function,并将其分配给jsfn。

然后用两个值定义一个参数argv,这两个值是args [0]和args [1]的数字值。

然后通过jsfn-Call调用回调函数(Null(isolate),2,argv)。

Argv是一个数组,我们在定义中指定的个数是2。

Call()方法是为函数类型的值调用的方法。

局部值| Call (handlevaluerecv,int argc,handlevalueargv [])引用了文档,可以看到Call()方法传递了三个参数,第一个参数是执行上下文,在执行代码时用来绑定这个,第二个参数是参数个数,第三个参数列表是数组形式。

以上例子只是冰山一角,甚至不是冰山一角。为了理解nodejs使用C/C编写原生模块,如果要编写一个可用且高性能的C模块,那么程序员必须精通C/C,并且还要精通js底层,包括v8和libuv等。

摘要

以上就是本文的全部内容。希望本文的内容能给你的学习或工作带来一些帮助。有问题可以留言交流。谢谢你的支持。

版权声明:用C/C编写node.js原生模块的方法教程是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。