来源:自学PHP网 时间:2014-12-14 21:15 作者: 阅读:次
[导读] 这篇文章的主要内容其实简而言之就是——用C/C++来实现 Node.js 的模块,非常的不错,有需要的朋友可以参考下...
N久之前的一个坑——用 Node.js 来重构 NBUT 的 Online Judge,包括评测端也得重构一遍。(至于什么时候完成大家就不要关心了,(/‵Д′)/~ ╧╧ 总之我们现在要做的其实简而言之就是——用C/C++来实现 Node.js 的模块。 准备工作 工欲善其事,必先~~耍流氓~~利其器。 node-gyp 首先你需要一个 node-gyp 模块。 在任意角落,执行: 复制代码 代码如下: $ npm install node-gyp -g 在进行一系列的 blahblah 之后,你就安装好了。 Python 然后你需要有个 python 环境。 自己去官网搞一个来。
嘛嘛,我就偷懒点不细写了,还请自己移步到 node-gyp 去看编译器的需求。并且倒腾好。 入门 我就拿官网的入门 Hello World说事儿了。 Hello World 请准备一个 C++ 文件,比如就叫 ~~sb.cc~~ hello.cc。 然后我们一步步来,先往里面搞出头文件和定义好命名空间: 复制代码 代码如下: #include <node.h> #include <v8.h> using namespace v8; 主要函数 接下去我们写一个函数,其返回值是 Handle<Value>。 复制代码 代码如下: Handle<Value> Hello(const Arguments& args) { //... 嗷嗷待写 } 然后我来粗粗解析一下这些东西: Handle<Value> 做人要有节操,我事先申明我是从这里(@fool)参考的。
JavaScript 类型在 C++ 中均有对应的自定义类型,如 String 、 Integer 、 Object 、 Date 、 Array 等,严格遵守在 JavaScript 中的继承关系。 C++ 中使用这些类型时,必须使用 Handle 托管,以使用 GC 来管理它们的生命周期,而不使用原生栈和堆。 在了解了这件事之后,我们大致能明白上面那段函数的申明的意思就是说,我们写一个 Hello 函数,其返回的是一个不定类型的值。
这个就是传入这个函数的参数了。我们都知道在 Node.js 中,参数个数是乱来的。而这些参数传进去到 C++ 中的时候,就转变成了这个 Arguments 类型的对象了。 具体的用法我们在后面再说,在这里只需要明白这个是个什么东西就好。(为毛要卖关子?因为 Node.js 官方文档中的例子就是分开来讲的,我现在只是讲第一个 Hello World 的例子而已( ´థ౪థ)σ 添砖加瓦 接下去我们就开始添砖加瓦了。就最简单的两句话: 复制代码 代码如下: Handle<Value> Hello(const Arguments& args) { HandleScope scope; return scope.Close(String::New("world")); } 这两句话是什么意思呢?大致的意思就是返回一个 Node.js 中的字符串 "world"。 HandleScope 同参考自这里。
复制代码 代码如下: Handle<Value> Hello(const Arguments& args) { HandleScope scope; return String::New("world"); } 因为当函数返回时,scope 会被析构,其管理的Handle也都将被回收,所以这个 String 就会变得没有意义。 所以呢 V8 就想出了个神奇的点子——HandleScope::Close(Handle<T> Value) 函数!这个函数的用处就是关闭这个 Scope 并且把里面的参数转交给上一个 Scope 管理,也就是进入这个函数前的 Scope。 于是就有了我们之前的代码 scope.Close(String::New("world"));。 String::New 这个 String 类所对应的就是 Node.js 中原生的字符串类。继承自 Value 类。与此类似,还有: •Array 而这个 New 呢?这里可以看的。就是新建一个 String 对象。 至此,这个主要函数我们就解析完毕了。 导出对象 我们来温习一下,如果是在 Node.js 里面写的话,我们怎么导出函数或者对象什么的呢? 复制代码 代码如下: exports.hello = function() {} 那么,在 C++ 中我们该如何做到这一步呢? 初始化函数 首先,我们写个初始化函数: 复制代码 代码如下: void init(Handle<Object> exports) { //... 嗷嗷待写你妹啊!#゚Å゚)⊂彡☆))゚Д゚)・∵ } 这是龟腚!函数名什么的无所谓,但是传入的参数一定是一个 Handle<Object>,代表我们下面将要在这货上导出东西。 然后,我们就在这里面写上导出的东西了: 复制代码 代码如下: void init(Handle<Object> exports) { exports->Set(String::NewSymbol("hello"), FunctionTemplate::New(Hello)->GetFunction()); } 大致的意思就是说,为这个 exports 对象添加一个字段叫 hello,所对应的东西是一个函数,而这个函数就是我们亲爱的 Hello 函数了。 用伪代码写直白点就是: 复制代码 代码如下: void init(Handle<Object> exports) { exports.Set("hello", function hello); } 大功告成! (大功告成你妹啊!闭嘴( ‘д‘⊂彡☆))Д´) 真·导出 这才是最后一步,我们最后要申明,这个就是导出的入口,所以我们在代码的末尾加上这一行: 纳了个尼?!这又是什么东西? 别着急,这个 NODE_MODULE 是一个宏,它的意思呢就是说我们采用 init 这个初始化函数来把要导出的东西导出到 hello 中。那么这个 hello 哪来呢? 它来自文件名!对,没错,它来自文件名。你并不需要事先申明它,你也不必担心不能用,总之你的这个最终编译好的二进制文件名叫什么,这里的 hello 你就填什么,当然要除去后缀名了。 详见官方文档。
复制代码 代码如下: void Initialize (Handle<Object> exports); NODE_MODULE(module_name, Initialize) There is no semi-colon after NODE_MODULE as it's not a function (see node.h). The module_name needs to match the filename of the final binary (minus the .node suffix). 来吧,让我们一起编译吧! 我们再新建一个类似于 Makefile 的归档文件吧——binding.gyp。 并且在里面添加这样的代码: 复制代码 代码如下: { "targets": [ { "target_name": "hello", "sources": [ "hello.cc" ] } ] } 为什么这么写呢?可以参考 node-gyp 的官方文档。 configure 在文件搞好之后,我们要在这个目录下面执行这个命令了: 复制代码 代码如下: $ node-gyp configure 如果一切正常的话,应该会生成一个 build 的目录,然后里面有相关文件,也许是 M$ Visual Studio 的 vcxproj 文件等,也许是 Makefile ,视平台而定。 build Makefile 也生成好之后,我们就开始构造编译了: 等到一切编译完成,才算是真正的大功告成了!不信你去看看 build/Release 目录,下面是不是有一个 hello.node 文件了?没错,这个就是 C++ 等下要给 Node.js 捡的肥皂! 搞基吧!Node ヽ(✿゚▽゚)ノ C++ 我们在刚才那个目录下新建一个文件 jianfeizao.js: 复制代码 代码如下: var addon = require("./build/Release/hello"); console.log(addon.hello()); 看到没!看到没!出来了出来了!Node.js 和 C++ 搞基的结果!这个 addon.hello() 就是我们之前在 C++ 代码中写的 Handle<Value> Hello(const Arguments& args) 了,我们现在就已经把它返回的值给输出了。 洗洗睡吧,下节更深入 时间不早了,今天就写到这里了,至此为止大家都能搞出最基础的 Hello world 的 C++ 扩展了吧。下一次写的应该会更深入一点,至于下一次是什么时候,我也不知道啦其实。 |
自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习
京ICP备14009008号-1@版权所有www.zixuephp.com
网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com