如何写一个简单的node.js C 扩展

node 是由 c 编写的,核心的 node 模块也都是由 c 代码来实现,所以同样 node 也开放了让使用者编写 c 扩展来实现一些操作的窗口。

如果大家对于 require 函数的描述还有印象的话,就会记得如果不写文件后缀,它是有一个特定的匹配规则的:

LOAD_AS_FILE(X)1. If X is a file, load X as its file extension format. STOP2. If X.js is a file, load X.js as javascript text. STOP3. If X.json is a file, parse X.json to a javascript object. STOP4. If X.node is a file, load X.node as binary addon. STOP

可以看到,最后会匹配一个 .node,而后边的描述也表示该后缀的文件为一个二进制的资源。


而这个 .node 文件一般就会是我们所编译好的 c 扩展了。

为什么要写 c 扩展

可以简单理解为,如果想基于 node 写一些代码,做一些事情,那么有这么几种选择:

1. 写一段 JS 代码,然后 require 执行

2. 写一段 c 代码,编译后 require 执行

3. 打开 node 源码,把你想要的代码写进去,然后重新编译

日常的开发其实只用第一项就够了,我们用自己熟悉的语言,写一段熟悉的代码,然后发布在 NPM 之类的平台上,其他有相同需求的人就可以下载我们上传的包,然后在TA的项目中使用。


但有的时候可能纯粹写 JS 满足不了我们的需求,也许是工期赶不上,也许是执行效率不让人满意,也有可能是语言限制。


所以我们会采用直接编写一些 c 代码,来创建一个 c 扩展让 node 来加载并执行。


况且如果已经有了 c 版本的轮子,我们通过扩展的方式来调用执行而不是自己从头实现一套,也是避免重复造轮子的方法。

一个简单的例子,如果大家接触过 webpack 并且用过 sass 的话,那么在安装的过程中很可能会遇到各种各样的报错问题,也许会看到 gyp 的关键字,其实原因就是 sass 内部有使用一些 c 扩展来辅助完成一些操作,而 gyp 就是用来编译 c 扩展的一种工具。

当然,上边也提到了还有第三种操作方法,我们可以直接魔改 node 源码,但是如果你只是想要写一些原生 JS 实现起来没有那么美好的模块,那么是没有必要去魔改源码的,毕竟改完了以后还要编译,如果其他人需要用你的逻辑,还需要安装你所编译好的特殊版本。


这样的操作时很不易于传播的,大家不会想使用 sass 就需要安装一个 sass 版本的 node 吧。


就像为了看星战还要专门下载一个优酷- -。

简单总结一下,写 c 的扩展大概有这么几个好处:

1. 可以复用 node 的模块管理机制

2. 有比 JS 更高效的执行效率

3. 有更多的 c 版本的轮子可以拿来用


怎么去写一个简单的扩展

node 从问世到现在已经走过了 11 年,通过早期的资料、博客等各种信息渠道可以看到之前开发一个 c 扩展并不是很容易,但经过了这么些年迭代,各种大佬们的努力,我们再去编写一个 c 扩展已经是比较轻松的事情了。


这里直入正题,放出今天比较关键的一个工具:node-addon-api module


以及这里是官方提供的各种简单 demo 来让大家熟悉这是一个什么样的工具:node-addon-examples。

需要注意的一点是, demo 目录下会分为三个子目录,在 readme 中也有写,分别是三种不同的 c 扩展的写法(基于不同的工具)。


我们本次介绍的是在 node-addon-api 目录下的,算是三种里边最为易用的一种了。

首先是我们比较熟悉的 package.json 文件,我们需要依赖两个组件来完成开发,分别是 bindings 和 node-addon-api。

然后我们还需要简单了解一下 gyp 的用法,因为编译一个 c 扩展需要用到它。


就像 helloworld 示例中的 binding.gyp 文件示例:

{  "targets": [    {      // 导出的文件名      "target_name": "hello",      // 编译标识的定义 禁用异常机制(注意感叹号表示排除过滤)      "cflags!": [ "-fno-exceptions" ],      // c  编译标识的定义 禁用异常机制(注意感叹号表示排除过滤,也就是 c  编译器会去除该标识)      "cflags_cc!": [ "-fno-exceptions" ],      // 源码入口文件      "sources": [ "hello.cc" ],      // 源码包含的目录      "include_dirs": [        // 这里表示一段 shell 的运行,用来获取 node-addon-api 的一些参数,有兴趣的老铁可以自行 node -p "require('node-addon-api').include" 来看效果        "

gyp 的语法挺多的,这次并不是单独针对 gyp 的一次记录,所以就不过多的介绍。

从最简单的数字相加来实现

然后我们来实现一个简单的创建一个函数,让两个参数相加,并返回结果。

源码位置:https://github.com/Jiasm/node...

我们需要这样的一个 binding.gyp 文件:

{  "targets": [    {      "target_name": "add",      "cflags!": [ "-fno-exceptions" ],      "cflags_cc!": [ "-fno-exceptions" ],      "sources": [ "add.cc" ],      "include_dirs": [        "

然后我们在项目根目录创建 package.json 文件,并安装 bindings 和 node-addon-api 两个依赖。

接下来就是去编写我们的 c 代码了:

#include 
// 定义 Add 函数Napi::Value Add(const Napi::CallbackInfo& info) {  Napi::Env env = info.Env();// 接收第一个参数  double arg0 = info[0].As<:number>().DoubleValue();  // 接收第二个参数  double arg1 = info[1].As<:number>().DoubleValue();  // 将两个参数相加并返回  Napi::Number num = Napi::Number::New(env, arg0   arg1);return num;}
// 入口函数,用于注册我们的函数、对象等等Napi::object Init(Napi::Env env, Napi::object exports) {  // 将一个名为 add 的函数挂载到 exports 上  exports.Set(Napi::String::New(env, "add"), Napi::Function::New(env, Add));  return exports;}
// 固定的宏使用NODE_API_MODULE(addon, Init)

在 c 代码完成以后就是需要用到 node-gyp 的时候了,建议全局安装 node-gyp,避免一个项目中出现多个 node_modules 目录的时候使用 npx 会出现一些不可预料的问题:

> npm i -g node-gyp# 生成构建文件> node-gyp configure# 构建> node-gyp build

这时候你会发现项目目录下已经生成了一个名为 add.node 的文件,就是我们在 binding.gyp 里边的 target_name 所设置的值了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/339237.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在线画 有穷状态自动机 的软件_怎么画思维导图?不用下载软件,在线就能操作...

怎么画思维导图&#xff1f;在工作中&#xff0c;除了流程图&#xff0c;脑图也是很重要的一个存在&#xff1a;流程图帮助我们快速完成任务&#xff0c;而脑图告诉我们任务本质。画思维导图是一个积累的过程&#xff0c;急不来&#xff0c;对于新手来说还是有一定难度的。由于…

Spring Boot Actuator:在其顶部具有MVC层的自定义端点

Spring Boot Actuator端点允许您监视应用程序并与之交互。 Spring Boot包含许多内置端点&#xff0c;您也可以添加自己的端点。 添加自定义端点就像创建一个从org.springframework.boot.actuate.endpoint.AbstractEndpoint扩展的类一样容易。 但是Spring Boot Actuator也提供了…

422器件与lvds接收器的区别_SPI、I2C、UART三种串行总线的原理、区别

SPI、I2C、串口、我相信如果你是从事的是嵌入式开发&#xff0c;一定会用到这三种通信协议&#xff0c;串口的话因为和波特率有关&#xff0c;所以一般的CPU或者MCU只会配有两个或者三个串口&#xff0c;而数据的传输&#xff0c;的话SPI和I2C用得会比较多区别&#xff1a;1、U…

C 的 6 种内存顺序,你都知道吗?

原子操作的内存顺序有六个内存顺序选项可应用于对原子类型的操作&#xff1a;1. memory_order_relaxed2. memory_order_consume3. memory_order_acquire4. memory_order_release5. memory_order_acq_rel6. memory_order_seq_cst。除非你为特定的操作指定一个顺序选项&#xff0…

易语言 网页用什么编码_通常提到的编码器是干什么用的

编码器&#xff08;encoder&#xff09;是将信号&#xff08;如比特流&#xff09;或数据进行编制、转换为可用以通讯、传输和存储的信号形式的设备。编码器把角位移或直线位移转换成电信号&#xff0c;前者成为码盘&#xff0c;后者称码尺&#xff0e;按照读出方式编码器可以分…

如何优雅地实现 C 编译期静态反射

部门请来了软件专家袁英杰咨询师指导我们软件开发&#xff0c;从中我也学到了很多姿势&#xff0c;在此记录下来宝贵的经验。苹果的 mbp 品控真是差劲&#xff0c;写这个东西把 LShift 键 按坏了&#xff0c;真是难受。反射能做什么最近和大师聊软件设计&#xff0c;其中一个点…

香草 jboss 工具_如何为JBoss Developer Studio 8设置BPM和规则工具

香草 jboss 工具最新的JBoss Developer Studio&#xff08;JBDS&#xff09;的发布带来了有关如何开始使用尚未安装的各种JBoss Integration和BPM产品工具集的问题。 在本系列文章中&#xff0c;我们将为您概述如何安装每套工具并说明它们支持哪些产品。 这将有助于您在着手进…

局域网steam联机_适合和基友联机一起玩的单机游戏(1)

GTA5还有什么比在GTA中&#xff0c;和几个好基友一起&#xff0c;组建帮派&#xff0c;联机打砸抢&#xff0c;组队完成任务&#xff0c;和其他帮派火并更有意思的呢&#xff1f;游戏丰富的内容&#xff0c;各式各样的玩法&#xff0c;广袤的可探索空间&#xff0c;不愧是史上最…

C/C assert()函数用法总结与注意事项

1. 简介assert宏的原型定义在中&#xff0c;其作用是如果它的条件返回错误&#xff0c;则终止程序执行。原型定义&#xff1a;#include void assert( int expression );assert的作用是先计算表达式 expression &#xff0c;如果其值为假&#xff08;即为0&#xff09;&#xff…

ppt flash倒计时器_PPT三大神器之iSlide插件

本文约1200字&#xff0c;阅读预计需要4分钟。为了提升PPT制作效率&#xff0c;我们有必要使用一些插件来提升工作效率&#xff0c;而PPT有三大插件神器&#xff0c;分别是iSlide、PA口袋动画&#xff0c;Onekey Tool&#xff08;俗称OK插件&#xff09;&#xff0c;今天我们就…

C 语言中std::array的神奇用法总结

std::array是在C 11标准中增加的STL容器&#xff0c;它的设计目的是提供与原生数组类似的功能与性能。也正因此&#xff0c;使得std::array有很多与其他容器不同的特殊之处&#xff0c;比如&#xff1a;std::array的元素是直接存放在实例内部&#xff0c;而不是在堆上分配空间&…

java线程池并发_线程池之外:Java并发并不像您想象的那样糟糕

java线程池并发Apache Hadoop&#xff0c;Apache Spark&#xff0c;Akka&#xff0c;Java 8流和Quasar&#xff1a; 针对Java开发人员的经典用例以及最新的并发方法 关于并发性更新概念的讨论很多&#xff0c;但是许多开发人员还没有机会将他们的想法缠住。 在本文中&#xff…

网络营销理论模型_网络营销:课堂笔记(第四章下)

网络营销产品策略(续上篇)本章知识清单三、网络品牌如何打造&#xff1f;什么是品牌目前为止&#xff0c;对品牌的含义一直没有一个统一的、权威的解释。如果从品牌的构成要素和基本功能方面来界定品牌的话&#xff0c;最具有代表性和最经典的表述当属美国市场营销协会的定义。…

ios多线程Android,iOS 关于多线程

一.进程和线程1.什么是进程进程是指在系统中正在运行的一个应用程序每个进程之间是独立的&#xff0c;每个进程均运行在其专用且受保护的内存空间内比如&#xff1a;同时打开QQ&#xff0c;Xcode&#xff0c;系统就会分别启动2个进程通过”活动监视器”可以查看Mac系统中所开启…

websockets_使用用户名/密码和Servlet安全性保护WebSockets

websocketsRFC 6455提供了WebSockets安全注意事项的完整列表。 其中一些是在协议本身中烘焙的&#xff0c;其他一些则需要更多有关如何在特定服务器上实现它们的解释。 让我们来谈谈协议本身内置的一些安全性&#xff1a; HTTP请求中的Origin头仅包含标识发起该请求的主体&…

android横向排列 间隙,Android开发消除横向排列的多个Button之间的空隙

一.问题重述摘要里描述的可能不太清楚&#xff0c;问题如下图&#xff1a;如何消除Button1和Button2之间的空隙&#xff0c;以及Button与左右边界之间的空隙&#xff1f;二.问题根源这里出现的空隙其实是Button的背景图片中的透明部分&#xff0c;如下图&#xff1a;(两个按钮被…

电脑的发展史_互联网发展史 硅谷传奇之 IBM

2节 硅谷传奇之 IBM为什么要讲IBM呢&#xff1f;互联网是因计算机而诞生的&#xff0c;互联网的发展史与电脑的发展史有很多是重叠的&#xff0c;而IBM是上世纪60年代八大电脑公司之首。在互联网席卷全球之前&#xff0c;在硅谷是以无线电、军事技术、硅晶体管而闻名的。这些东…

android汉字田字格,画一个简单的田字格

image.png上代码package com.nick.customview;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RectF;import android.support.annotation.Nullable;import android.sup…

xp定时关机软件_好用又免费的电脑定时工具,不用得后悔

现在利用电脑办公的人有多少&#xff0c;举个手示意下&#xff01;&#xff01;&#xff01;给电脑设置定时关机&#xff0c;可以方便我们不在电脑前完成关机操作。那么&#xff0c;如何设置定时关机呢&#xff1f;如果要取消&#xff0c;定时关机又如何取消&#xff1f;有的人…

apache camel_Apache Camel请向我解释这些端点选项的含义

apache camel在即将发布的Apache Camel 2.15中&#xff0c;我们使Camel更智能。 现在&#xff0c;它可以充当老师&#xff0c;并向您说明其配置方式以及这些选项的含义。 Camel可以做的第一课是告诉您如何配置所有端点以及这些选项的含义。 接下来我们要学习的课程是让Camel解…