2019-08-06 21:43:51 itas109 阅读数 90
  • Nodejs极速入门

    由King老师精心打造的Nodejs极速入门课程,从Nodejs介绍到环境下载安装,到Express框架的基础使用,用短的时间帮你入门Nodejs! 山哥出品,必属精品!

    31086 人正在学习 去看看 何山

nodejs在Linux下c++插件运行


如需转载请标明出处:http://blog.csdn.net/itas109
QQ技术交流群:129518033

环境:

Linux : ubuntu 16.04

nodejs : 10.15.2

node-gyp : 5.0.3

GCC : 5.4.0

Python : 2.7

1. 安装nodejs

略过

参考:nodejs在Linux下的安装

2.安装node-gyp

参考https://www.npmjs.com/package/node-gyp

Linux下:

前提:

  • python (推荐 v2.7, v3.x.x 暂未支持)
  • make
  • C/C++编译工具,例如GCC
# 安装
npm install -g node-gyp

建立软连接(这里假设nodejs安装路径为/app/software/)

sudo ln -s /app/software/nodejs/bin/node-gyp /usr/local/bin/ 

查看node-gyp的版本

node-gyp -v

3.编写测试代码

3.1 编写hello.cc

#include <node.h>
#include <v8.h>

using namespace v8;

void Method(const v8::FunctionCallbackInfo<Value>& args) 
{
  Isolate* isolate = Isolate::GetCurrent();
  HandleScope scope(isolate);
  args.GetReturnValue().Set(String::NewFromUtf8(isolate, "world"));
}

void Init(Handle<Object> exports) 
{
  Isolate* isolate = Isolate::GetCurrent();
  exports->Set(String::NewFromUtf8(isolate, "hello"),
      FunctionTemplate::New(isolate, Method)->GetFunction());

}

NODE_MODULE(hello, Init)

注意,所有的 Node.js 插件必须导出一个如下模式的初始化函数:

void Initialize(Local<Object> exports);
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

NODE_MODULE 后面没有分号,因为它不是一个函数(详见 node.h)。

module_name 必须匹配最终的二进制文件名(不包括 .node 后缀)。

hello.cc 示例中,初始化函数是 Init,插件模块名是 addon。

3.2 编写构建文件binding.gyp

{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ]
    }
  ]
}

3.3 编译.node模块

node-gyp configure --debug build

–debug参数表示生成debug文件

3.4 编写hello.js

//hello.js

var addon = require('./build/Debug/hello.node');

console.log(addon.hello()); // 'world'

require的路径需要和3.3中编译出来的路径一致

3.5 运行

node hello.js

3.6 结果

$ node hello.js 
world

觉得文章对你有帮助,可以扫描二维码捐赠给博主,谢谢!
在这里插入图片描述
如需转载请标明出处:http://blog.csdn.net/itas109
QQ技术交流群:129518033


License

License under CC BY-NC-ND 4.0: 署名-非商业使用-禁止演绎


Reference:
1.http://nodejs.cn/api/addons.html
2.https://www.jianshu.com/p/8a9f4304557c
3.https://www.npmjs.com/package/node-gyp

2018-09-13 11:58:21 abcd1f2 阅读数 478
  • Nodejs极速入门

    由King老师精心打造的Nodejs极速入门课程,从Nodejs介绍到环境下载安装,到Express框架的基础使用,用短的时间帮你入门Nodejs! 山哥出品,必属精品!

    31086 人正在学习 去看看 何山

  NodeJS C++插件是一种动态链接库,采用C/C++语言编写,可以通过require()将插件加载进NodeJS中进行使用。利用V8提供的API,可以实现JavaScript和C++的互相调用,打通JavaScript和C++之间的接口。在做一些高性能或者底层模块的时候,需要用到一些C++库,NodeJS C++插件可以帮助我们封装这些C++库的接口,使得JavaScript具备调用C++库的能力。本文将记录利用基础的V8 API编写NodeJS C++插件的过程,实现C++和JavaScript之间的参数传递、函数调用以及回调、异常处理以及对象函数传递等功能。记录过程中也会对部分概念和API进行阐述。

本文所使用的代码示例可以从该仓库中找到–【cpp-addons】

  备注: 本文旨在探究NodeJS C++ Addons的原生写法,了解部分底层知识,所使用的NodeJS版本为8.11.1,由于V8原生的API会发生变动,不同版本的NodeJS的支持情况可能不同。因此不保证代码能兼容所有版本的NodeJS。

一、基本概念

  • 1.1、hello world示例
      首先通过一个简单的HelloWorld示例来了解编写C++插件的基本写法和一些API的基本概念。在示例中,C++模块向JavaScript暴露了一个hello接口,在JavaScript中调用该接口后会得到返回值hello world。
#include <node.h>

namespace HelloWorldDemo {
  using v8::FunctionCallbackInfo;
  using v8::Isolate;
  using v8::Local;
  using v8::Object;
  using v8::String;
  using v8::Value;

  void hello (const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    /* 通过 FunctionCallbackInfo<Value>& args 可以设置返回值 */
    args.GetReturnValue().Set(String::NewFromUtf8(isolate, "hello world."));
  }

  void init (Local<Object> exports) {
    /* 设置模块的导出方法 hello */
    /* 等价于 js 模块中的 module.exports.hello = hello */
    NODE_SET_METHOD(exports, "hello", hello);
  }

  NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

  JavaScript调用C++模块的方法时,会传递一个V8对象,类型为FunctionCallbackInfo。通过这个V8对象,JavaScript可以向C++接口传递参数,C++函数也可以通过这个对象来向JavaScript回传信息,即设置返回值。在C++接口中,通过参数const FunctionCallbackInfo& args可以拿到一个Isolate对象,Isolate代表一个V8虚拟机实例。通过args.GetIsolate()可以获取到运行JavaScript调用者的V8虚拟机实例。这个V8实例包含了内存堆,在C++接口中创建V8提供的JavaScript对象类型实例的时候会使用到。例如前面的hello world例子中,在创建一个JS字符串的时候需要传递isolate对象,表示在该V8虚拟机上创建了一个JS字符串对象,之后该字符串便可以被V8虚拟机上运行的JS调用者所使用。

  Local是一个模板句柄类,Local代表指向某种类型的句柄。例如模块的exports属性是一个JavaScript对象,句柄类型为Local。传递给init函数的参数其实是指向相应对象的句柄。

  NODE_MODULE是一个宏,设置模块初始化函数为init。init函数中执行模块的初始化,当模块第一次被加载进NodeJS应用中的时候就会执行init函数,init函数中可以设置exports属性将C++接口暴露出去给JavaScript使用。NODE_SET_METHOD用于设置属性或方法,第二个参数为属性名,第三个参数为方法对应的属性值。如果需要给exports对象设置多个属性或方法,可以调用多次NODE_SET_METHOD。exports对象上设置的属性方法将会作为接口暴露给外部使用。

  编写NodeJS C++插件必须遵循以下这种模式:必须有一个初始化函数对模块进行初始化(设置方法属性等),然后加上NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)设置模块名和初始化函数。初始化函数可以有两种写法,第一种写法常用于设置模块的exports对象上的某个属性或方法,第二种写法可用于直接重写整个exports对象。

// 写法1
void Initialize_1(Local<Object> exports) {
  // 进行初始化...
  // example
  // 等价于js模块中的 module.exports.hello = hello
  NODE_SET_METHOD(exports, "hello", hello);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize_1)

// 写法2
void Initialize_2(Local<Object> exports, Local<Object> module) {
  // 进行初始化...
  // example
  // 等价于js模块中的 module.exports = hello
  NODE_SET_METHOD(module, "exports", hello);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize_2)


  • 1.2、构建方法

  编写完C++代码后需要将其编译构建成node文件才能够被NodeJS使用。利用node-gyp可以很方便地进行构建。首先在C++代码文件的根目录下创建一个binding.gyp文件,在文件中写入类似下面的JSON格式的内容。如果有多个插件,可以在targets数组上继续添加。数组的元素为一个对象,对象的target_name属性指明构建后的插件名称,sources属性则是C++源码路径。sources属性是一个数组,如果插件对应的源码文件有多个,只需在数组中列举出对应的多个路径即可。例如下面的示例,C++源码hello.cc经过构建之后,会生成名为addon.node的插件。

{
  "targets": [{
    "target_name": "addon",
    "sources": ["hello.cc"]
  }]
}

  写好binding.gyp以后,只需要执行以下2个命令即可完成构建,两句命令可以分开执行,也可以合并成一句。configure指令执行后会在当前目录下生成build文件夹,并在build文件夹中生成相应平台的构建文件(例如Linux下的Makefile)。build执行后则是编译源码生成拓展名为node的NodeJS插件,插件会输出到./build/Release/或./build/Debug/文件夹下。

node-gyp configure
node-gyp build

合并成一句

node-gyp configure build

  构建完成后,在JS中直接通过require便可引用到该插件,然后便可在JS中直接调用插件上设置的接口。.node文件实际上是一个动态链接库,使用require时可以省略掉拓展名.node,NodeJS会自动去加载这个动态链接库。不过这里有个要点需要注意,使用require引用模块的时候,会优先寻找拓展名为.js的模块,所以,如果相同路径下同时存在.node和.js文件,require引用到的其实是.js文件。例如:如果./build/Release/文件夹下同时存在addon.js和addon.node,这时下面这行代码执行后将加载到addon.js。

const addon = require('./build/Release/addon')
1
  • 1.3、运行结果
通过下面的binding.gyp将前面的hello world示例代码进行编译构建,生成hello_world.node文件。

{
  "targets": [{
    "target_name": "hello_world",
    "sources": ["hello_world.cc"]
  }]
}

接下来用以下代码对C++插件进行使用。可以看到成功实现了在JavaScript中调用C++接口,接口执行后返回字符串hello world。

// hello-world demo
const HelloWorld = require('./build/Release/hello_world')
console.log('[HelloWorldDemo] ' + HelloWorld.hello())

hello world示例

二、函数传参与回调

  在调用C++接口的时候,经常需要向接口传递参数。前面提到,JavaScript调用C++接口的时候会向其传递一个类型为FunctionCallbackInfo对象,通过这个对象可以实现向C++接口传递参数,参数可以是普通的JS对象,也可以是函数。传递函数参数时,还可以实现回调,即JavaScript在调用C++接口时向其传递回调函数,C++接口执行后回调JavaScript。

  接下来通过实现一个累加函数探索函数传参和回调的实现。累加函数accumulate的作用是对参数进行累加求和,然后返回求和结果。例如调用accumulate(1, 2, 3)将得到1+2+3的结果,即6。为了同时展示传参和回调,accumulate函数的求和结果不能像前面hello world例子中一样直接通过设置返回值进行返回,而是要通过回调函数进行返回。假设调用accumulate函数时通过最后一个参数传递回调函数,则调用时的写法将变为accumulate(1, 2, 3, callback)。下面是C++模块的代码。

#include <node.h>

namespace FunctionArgumentsAndCallbackDemo {
  using v8::Function;
  using v8::FunctionCallbackInfo;
  using v8::Isolate;
  using v8::Local;
  using v8::Number;
  using v8::Object;
  using v8::Value;
  using v8::Null;

  void accumulate (const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    /* 提取通过参数传递的回调函数 */
    Local<Function> callback = Local<Function>::Cast(args[args.Length() - 1]);

    /* 遍历参数进行求和 */
    double sum = 0.0;
    for (int i = 0; i < args.Length() - 1; ++i) {
      sum += args[i]->NumberValue();
    }

    /* 将求和结果转成一个js Number, 通过回调函数进行返回 */
    Local<Number> num = Number::New(isolate, sum);
    Local<Value> argv[1] = { num };
    callback->Call(Null(isolate), 1, argv);
  }

  void init (Local<Object> exports) {
    NODE_SET_METHOD(exports, "accumulate", accumulate);
  }

  NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

  上述代码中通过args可以获得从JavaScript传递过来的参数,这些参数都是JavaScript对象实例,使用时需要根据情况调用相关方法,例如求和的参数是多个数字,在JavaScript中数字都是Number对象实例,需要调用NumberValue()方法后才能获取到对应的double类型数值。通过Local::Cast()获取到回调函数的句柄,获取句柄后可以通过Call()方法对回调函数进行调用,调用时需要传递参数个数和参数实例(参数实例需要以数组形式进行传递)。通过这种方式实现的函数回调为同步回调,不是异步回调,异步回调的实现需要用到其他技巧,不在本文的讨论范围之内。

  给binding.gyp中的targets数组添加以下元素,然后继续使用node-gyp configure build进行编译构建生成.node文件。

{
  "target_name": "accumulate",
  "sources": ["accumulate.cc"]
}

  通过以下JavaScript代码即可使用C++插件,并验证接口是否调用成功。代码输出如果所示,accumulate接口成功对参数进行求和并通过回调函数返回了求和结果。

// function-arguments-and-callbacks demo
const Accumulate = require('./build/Release/accumulate')
Accumulate.accumulate(1, 3, 4, 7, (sum) => {
  console.log('[FunctionArgumentsAndCallbacksDemo] 1 + 3 + 4 + 7 = ' + sum)
})

函数传参与回调示例

三、异常处理

  C++插件提供的接口函数如果在运行后遇到异常,JavaScript调用者是否可以知道异常并进行处理呢?答案是可以的,V8提供的API使得C++可以直接向JavaScript抛出异常。前面提到,Isolate对象代表一个V8虚拟机实例。我们可以通过这个实例直接向该V8虚拟机抛出异常,该虚拟机实例上运行的JavaScript代码只要对异常进行捕获就可以知道异常的发生并进行相应的处理了。

  上面的累加求和例子中,没有考虑传递的参数类型就直接进行求和,在某些情况下可能发生异常。接下来,对上面的例子进行改进,增加异常处理机制,探索C++插件如何向JavaScript报告异常。代码如下所示。

#include <node.h>

namespace FunctionArgumentsAndCallbackDemo {
  using v8::Function;
  using v8::FunctionCallbackInfo;
  using v8::Isolate;
  using v8::Local;
  using v8::Number;
  using v8::Object;
  using v8::Value;
  using v8::Null;
  using v8::Exception;
  using v8::String;

  void accumulate (const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    /* 参数不合理异常 */
    if (args.Length() < 1) {
      isolate->ThrowException(Exception::TypeError(
        String::NewFromUtf8(isolate, "Arguments Number Error.")
      ));
      return;
    }

    /* 没有回调函数 */
    if (!args[args.Length() - 1]->IsFunction()) {
      isolate->ThrowException(Exception::TypeError(
        String::NewFromUtf8(isolate, "No Callback Error.")
      ));
      return;
    }

    /* 提取通过参数传递的回调函数 */
    Local<Function> callback = Local<Function>::Cast(args[args.Length() - 1]);

    /* 遍历参数进行求和 */
    double sum = 0.0;
    for (int i = 0; i < args.Length() - 1; ++i) {
      /* 如果参数不是数字,向js抛出异常 */
      if (!args[i]->IsNumber()) {
        isolate->ThrowException(Exception::TypeError(
          String::NewFromUtf8(isolate, "Arguments Type Error.")
        ));
        return;
      } else {
        sum += args[i]->NumberValue();
      }
    }

    /* 将求和结果转成一个js Number, 通过回调函数进行返回 */
    Local<Number> num = Number::New(isolate, sum);
    Local<Value> argv[1] = { num };
    callback->Call(Null(isolate), 1, argv);
  }

  void init (Local<Object> exports) {
    NODE_SET_METHOD(exports, "accumulate", accumulate);
  }

  NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

  通过Isolate对象的ThrowException方法,可以直接向JavaScript抛出异常。在JavaScript中,通过try/catch机制便可以捕获和处理异常。下面是代码示例,调用C++接口的时候故意引发异常,捕获到异常后将异常信息进行输出。如果图所示,可以成功实现C++模块向JavaScript抛出异常以及JavaScript捕获处理异常。

// exception demo
try {
  Accumulate.accumulate()
} catch (err) {
  console.log('[ExceptionDemo] ' + err)
}

try {
  Accumulate.accumulate(1, 2, 3)
} catch (err) {
  console.log('[ExceptionDemo] ' + err)
}

try {
  Accumulate.accumulate(1, 2, 'a', (sum) => {
    console.log(sum)
  })
} catch (err) {
  console.log('[ExceptionDemo] ' + err)
}

四、返回对象和函数

  前面的例子中,JavaScript可以通过args向C++传递JS对象和函数给C++模块使用,如果反过来让C++传递对象和函数给JavaScript使用,是否可以实现呢?答案是可以的。前面的例子展示了C++模块可以通过args.GetReturnValue().Set(xxx)来向JavaScript返回内容,这里返回的内容除了向前面例子中的普通String、Number等对象之外,也可以返回Object或者Function。C++模块中,可以将需要的信息封装成Object然后返回给JavaScript使用,可以将部分C++函数包装成Function给JavaScript调用。

  接下来通过一个简单例子来探索传递对象和函数的做法。在这个例子中,C++模块向JavaScript提供了getPerson()和getFunction()两个方法,getPerson()方法会返回一个Object给JavaScript,在JavaScript中调用此方法获得对象后可以直接使用该对象,访问对象属性获取信息(信息包括firstname和lastname)。getFunction()方法会将一个C++函数sayHiTo()包装成一个JS函数,返回一个Function给JavaScript,在JavaScript中调用此方法获取函数对象后,可以直接对返回的函数进行调用。sayHiTo()是一个C++函数,接收参数为一个Object,返回一句打招呼的信息(”Hi, xxxx”)。

#include <node.h>

namespace ReturnObjectAndFuntionDemo {
  using v8::Function;
  using v8::FunctionTemplate;
  using v8::FunctionCallbackInfo;
  using v8::Isolate;
  using v8::Object;
  using v8::String;
  using v8::Value;
  using v8::Local;

  void getPerson (const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    /* 创建对象 */
    Local<Object> obj = Object::New(isolate);
    /* 设置对象属性,Set(key, value) */
    obj->Set(
      String::NewFromUtf8(isolate, "firstname"),
      String::NewFromUtf8(isolate, "Java")
    );
    obj->Set(
      String::NewFromUtf8(isolate, "lastname"),
      String::NewFromUtf8(isolate, "Script")
    );
    /* 将对象返回给JavaScript */
    args.GetReturnValue().Set(obj);
  }

  void sayHiTo (const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    /* 提取通过参数传递的JS对象 */
    Local<Object> person = Local<Object>::Cast(args[0]);
    /* 提取对象属性值并拼接字符串 */
    Local<String> fullname = String::Concat(
      person->Get(String::NewFromUtf8(isolate, "firstname"))->ToString(),
      person->Get(String::NewFromUtf8(isolate, "lastname"))->ToString()
    );
    /* 将结果返回给JavaScript */
    args.GetReturnValue().Set(String::Concat(
      String::NewFromUtf8(isolate, "Hi, "),
      fullname
    ));
  }

  void getFunction (const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    /* 利用函数模板构造一个JavaScript函数 */
    Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, sayHiTo);
    Local<Function> fn = tpl->GetFunction();
    fn->SetName(String::NewFromUtf8(isolate, "sayHiTo"));
    /* 将函数返回给JavaScript */
    args.GetReturnValue().Set(fn);
  }

  void init (Local<Object> exports) {
    NODE_SET_METHOD(exports, "getPerson", getPerson);
    NODE_SET_METHOD(exports, "getFunction", getFunction);
  }

  NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

  返回对象的做法比较简单,直接创建一个Object,然后通过Set()方法设置属性和属性值,把需要返回的信息进行包装,最后通过args.GetReturnValue().Set()方法便可以直接将包装好的对象返回给JavaScript。

  返回函数的做法也是类似,创建一个Function,然后通过args.GetReturnValue().Set()方法将函数返回给JavaScript。创建函数的时候,需要用到FunctionTemplate,首先根据要包装的C++函数(sayHiTo)创建函数模板,然后调用模板的GetFunction()方法便可获得一个函数句柄,设置完函数名后便可将其返回给JavaScript使用。这里需要注意,sayHiTo是一个C++函数,而且没有通过module.exports属性来提供给外部使用,而是通过返回函数的方式来给外部使用。

  sayHiTo()是一个C++函数,通过函数返回给JavaScript之后将被JavaScript调用,所以它在调用时也会收到参数const FunctionCallbackInfo& args,跟之前通过module.exports暴露给JavaScript调用的方法一样,可以通过参数args来完成跟JavaScript的交互(参数传递、返回等)。

完成以上代码后,给binding.gyp的targets数组增加以下元素,然后进行编译构建生成.node文件。

{
  "target_name": "person",
  "sources": ["person.cc"]
}

完成编译构建之后就可以使用以下JavaScript代码进行实验,输出结果如图所示。

// return-object demo
const Person = require('./build/Release/person')
let someone = Person.getPerson()
console.log('[ReturnObjectDemo] ' + someone.firstname + someone.lastname)

// return-function demo
let sayHiTo = Person.getFunction()
console.log('[ReturnFunctionDemo] ' + sayHiTo(someone))

五、小结

  通过几个例子,大概了解如何编写NodeJS C++插件,也学会了如何处理一些常用场景,例如函数传参、回调、异常处理等。总体感觉编写C++插件实现C++和JavaScript的互相调用并不困难。主要的难点还是对V8的一些概念以及它所提供的API不熟悉,V8提供的很多API可以让我们方便地实现C++和JavaScript的互相调用,但由于对V8 API不够熟悉,编写过程中还是需要不断地查阅NodeJS和V8的官方文档。

2016-08-03 14:20:25 qq_35728402 阅读数 1789
  • Nodejs极速入门

    由King老师精心打造的Nodejs极速入门课程,从Nodejs介绍到环境下载安装,到Express框架的基础使用,用短的时间帮你入门Nodejs! 山哥出品,必属精品!

    31086 人正在学习 去看看 何山
// cnc_resetconnect.cc
#include <node.h>
#include <node_buffer.h>


#include <iostream>
#include <windows.h>


namespace demo {


using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
using v8::Number;
using v8::Uint32;
using v8::Integer;
using v8::Exception;
using v8::Array;


/* allocate library handle 3 */
//FWLIBAPI short WINAPI cnc_allclibhndl3(const char *, unsigned short, long, unsigned short *);
void Cnc_allclibhndl3(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();


  // Check the number of arguments passed.
  if (args.Length() != 4 ) {
    // Throw an Error that is passed back to JavaScript
    isolate->ThrowException(Exception::TypeError(
        String::NewFromUtf8(isolate, "Wrong number of arguments")));
    return;
  }


  HINSTANCE hDLL;
  hDLL=LoadLibrary("Fwlib32.dll");//加载动态链接库Fwlib32.dll文件;
  if (!hDLL) {
 // Throw an Error that is passed back to JavaScript
 DWORD dRe = GetLastError();
 char sz[4];
 sprintf(sz, "cat not get lib, error is %d", dRe);
 isolate->ThrowException(Exception::TypeError(
 String::NewFromUtf8(isolate, sz)));
 return;
  }


typedef  short(WINAPI *pMax)(const char *, unsigned short, long, unsigned short *);//函数指针
  pMax pCnc_resetconnect=NULL;
  pCnc_resetconnect=(pMax)GetProcAddress(hDLL,"cnc_allclibhndl3");
  if ( !pCnc_resetconnect ) {
    // Throw an Error that is passed back to JavaScript
    isolate->ThrowException(Exception::TypeError(
        String::NewFromUtf8(isolate, "can not get function: cnc_allclibhndl3 from Fwlib32.dll.")));
    return;
  }
 
  String::Utf8Value utf(args[0]->ToString());
  char* bufferdata = *utf;


  int a = args[1]->Uint32Value();
  int b = args[2]->Uint32Value();
  int c = args[3]->Uint32Value();


//   char buf[256];
//   sprintf(buf, "%s : %d : %d : %d", bufferdata, a, b, c);
//   MessageBox(NULL, buf, "", MB_OK);


  unsigned short iThr = c;


  short value = pCnc_resetconnect(bufferdata, a, b, &iThr);


  Local<Integer> num = Uint32::New(isolate, value);
  Local<Integer> handle = Uint32::New(isolate, iThr);


  Local<Object> obj = Object::New(isolate);
  obj->Set(String::NewFromUtf8(isolate, "re"), num);
  obj->Set(String::NewFromUtf8(isolate, "handle"), handle);

  args.GetReturnValue().Set(obj);

}


/* reset all socket connection */
//FWLIBAPI short WINAPI cnc_resetconnect(unsigned short);
void Cnc_resetconnect(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();


// Check the number of arguments passed.
if (args.Length() != 1) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}


HINSTANCE hDLL;
hDLL = LoadLibrary("Fwlib32.dll");//加载动态链接库Fwlib32.dll文件;
if (!hDLL) {
// Throw an Error that is passed back to JavaScript
DWORD dRe = GetLastError();
char sz[4];
sprintf(sz, "cat not get lib, error is %d", dRe);
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, sz)));
return;
}


typedef  short(WINAPI *pMax)( unsigned short );//函数指针
pMax pCnc_resetconnect = NULL;
pCnc_resetconnect = (pMax)GetProcAddress(hDLL, "cnc_resetconnect");
if (!pCnc_resetconnect) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "can not get function: cnc_resetconnect from Fwlib32.dll.")));
return;
}


unsigned short a = args[0]->Uint32Value();

short value = pCnc_resetconnect(a);

Local<Integer> num = Uint32::New(isolate, value);

// Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
args.GetReturnValue().Set(num);
}


#define MAX_AXIS        48
typedef struct odbaxis {
short   dummy;             /* dummy */
short   type;              /* axis number */
long    data[MAX_AXIS];    /* data value */
} ODBAXIS;


char* GetOdbaxis(short value, ODBAXIS dta, Isolate* isolate, Local<Object>& obj)
{
Local<Integer> num = Uint32::New(isolate, value);
Local<Integer> dummy = Uint32::New(isolate, dta.dummy);
Local<Integer> type = Uint32::New(isolate, dta.type);
Local<Array> arr = Array::New(isolate, MAX_AXIS );

for (int i = 0; i < MAX_AXIS; ++i)
{
arr->Set(i, Number::New(isolate, dta.data[i]) );
}

obj->Set(String::NewFromUtf8(isolate, "re"), num);
obj->Set(String::NewFromUtf8(isolate, "dummy"), dummy);
obj->Set(String::NewFromUtf8(isolate, "type"), type);
obj->Set(String::NewFromUtf8(isolate, "data"), arr);

return NULL;
}


/* read machine axis position */
//FWLIBAPI short WINAPI cnc_machine(unsigned short, short, short, ODBAXIS *);
void Cnc_machine(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();


// Check the number of arguments passed.
if (args.Length() != 4) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}


HINSTANCE hDLL;
hDLL = LoadLibrary("Fwlib32.dll");//加载动态链接库Fwlib32.dll文件;
if (!hDLL) {
// Throw an Error that is passed back to JavaScript
DWORD dRe = GetLastError();
char sz[4];
sprintf(sz, "cat not get lib, error is %d", dRe);
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, sz)));
return;
}


typedef  short(WINAPI *pMax)(unsigned short, short, short, ODBAXIS *);//函数指针
pMax pCnc_resetconnect = NULL;
pCnc_resetconnect = (pMax)GetProcAddress(hDLL, "cnc_machine");
if (!pCnc_resetconnect) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "can not get function: cnc_machine from Fwlib32.dll.")));
return;
}


unsigned short arg0 = args[0]->Uint32Value();
unsigned short arg1 = args[1]->Uint32Value();
unsigned short arg2 = args[2]->Uint32Value();
ODBAXIS obj = {};
short value = pCnc_resetconnect(arg0, arg1, arg2, &obj);


Local<Object> objR = Object::New(isolate);
char* pReturn = GetOdbaxis(value, obj, isolate, objR);
args.GetReturnValue().Set(objR/*String::NewFromUtf8(isolate, pReturn)*/);
//Local<Integer> num = Uint32::New(isolate, value);


// Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
//args.GetReturnValue().Set(num);
}


//FWLIBAPI short WINAPI cnc_relative( unsigned short, short, short, ODBAXIS * ) ;
void Cnc_relative(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();


// Check the number of arguments passed.
if (args.Length() != 4) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}


HINSTANCE hDLL;
hDLL = LoadLibrary("Fwlib32.dll");//加载动态链接库Fwlib32.dll文件;
if (!hDLL) {
// Throw an Error that is passed back to JavaScript
DWORD dRe = GetLastError();
char sz[4];
sprintf(sz, "cat not get lib, error is %d", dRe);
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, sz)));
return;
}


typedef  short(WINAPI *pMax)(unsigned short, short, short, ODBAXIS *);//函数指针
pMax pCnc_resetconnect = NULL;
pCnc_resetconnect = (pMax)GetProcAddress(hDLL, "cnc_relative");
if (!pCnc_resetconnect) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "can not get function: cnc_relative from Fwlib32.dll.")));
return;
}


unsigned short arg0 = args[0]->Uint32Value();
unsigned short arg1 = args[1]->Uint32Value();
unsigned short arg2 = args[2]->Uint32Value();
ODBAXIS obj = {};
short value = pCnc_resetconnect(arg0, arg1, arg2, &obj );
Local<Object> objR = Object::New(isolate);
char* pReturn = GetOdbaxis(value, obj, isolate, objR);
args.GetReturnValue().Set(objR/*String::NewFromUtf8(isolate, pReturn)*/);
}


//FWLIBAPI short WINAPI cnc_absolute( unsigned short, short, short, ODBAXIS * ) ;
void Cnc_absolute(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();


// Check the number of arguments passed.
if (args.Length() != 4) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}


HINSTANCE hDLL;
hDLL = LoadLibrary("Fwlib32.dll");//加载动态链接库Fwlib32.dll文件;
if (!hDLL) {
// Throw an Error that is passed back to JavaScript
DWORD dRe = GetLastError();
char sz[4];
sprintf(sz, "cat not get lib, error is %d", dRe);
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, sz)));
return;
}


typedef  short(WINAPI *pMax)(unsigned short, short, short, ODBAXIS *);//函数指针
pMax pCnc_resetconnect = NULL;
pCnc_resetconnect = (pMax)GetProcAddress(hDLL, "cnc_absolute");
if (!pCnc_resetconnect) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "can not get function: cnc_absolute from Fwlib32.dll.")));
return;
}


unsigned short arg0 = args[0]->Uint32Value();
unsigned short arg1 = args[1]->Uint32Value();
unsigned short arg2 = args[2]->Uint32Value();
ODBAXIS obj = {};;
short value = pCnc_resetconnect(arg0, arg1, arg2, &obj);


Local<Object> objR = Object::New(isolate);
char* pReturn = GetOdbaxis(value, obj, isolate, objR);
args.GetReturnValue().Set(objR/*String::NewFromUtf8(isolate, pReturn)*/);
}


//FWLIBAPI short WINAPI cnc_distance( unsigned short, short, short, ODBAXIS * ) ;
void Cnc_distance(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();


// Check the number of arguments passed.
if (args.Length() != 4) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}


HINSTANCE hDLL;
hDLL = LoadLibrary("Fwlib32.dll");//加载动态链接库Fwlib32.dll文件;
if (!hDLL) {
// Throw an Error that is passed back to JavaScript
DWORD dRe = GetLastError();
char sz[4];
sprintf(sz, "cat not get lib, error is %d", dRe);
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, sz)));
return;
}


typedef  short(WINAPI *pMax)(unsigned short, short, short, ODBAXIS *);//函数指针
pMax pCnc_resetconnect = NULL;
pCnc_resetconnect = (pMax)GetProcAddress(hDLL, "cnc_absolute");
if (!pCnc_resetconnect) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "can not get function: cnc_distance from Fwlib32.dll.")));
return;
}


unsigned short arg0 = args[0]->Uint32Value();
unsigned short arg1 = args[1]->Uint32Value();
unsigned short arg2 = args[2]->Uint32Value();
ODBAXIS obj = {};;
short value = pCnc_resetconnect(arg0, arg1, arg2, &obj);


Local<Object> objR = Object::New(isolate);
char* pReturn = GetOdbaxis(value, obj, isolate, objR);
args.GetReturnValue().Set(objR/*String::NewFromUtf8(isolate, pReturn)*/);
//Local<Integer> num = Uint32::New(isolate, value);


// Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
//args.GetReturnValue().Set(num);
}


//FWLIBAPI short WINAPI cnc_actf( unsigned short, ODBACT * ) ;
void Cnc_actf(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();


// Check the number of arguments passed.
if (args.Length() != 2) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}


HINSTANCE hDLL;
hDLL = LoadLibrary("Fwlib32.dll");//加载动态链接库Fwlib32.dll文件;
if (!hDLL) {
// Throw an Error that is passed back to JavaScript
DWORD dRe = GetLastError();
char sz[4];
sprintf(sz, "cat not get lib, error is %d", dRe);
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, sz)));
return;
}


typedef  short(WINAPI *pMax)(unsigned short, ODBAXIS *);//函数指针
pMax pCnc_resetconnect = NULL;
pCnc_resetconnect = (pMax)GetProcAddress(hDLL, "cnc_actf");
if (!pCnc_resetconnect) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "can not get function: cnc_actf from Fwlib32.dll.")));
return;
}


unsigned short arg0 = args[0]->Uint32Value();
ODBAXIS obj = {};;
short value = pCnc_resetconnect(arg0, &obj);


Local<Object> objR = Object::New(isolate);
char* pReturn = GetOdbaxis(value, obj, isolate, objR);
args.GetReturnValue().Set(objR/*String::NewFromUtf8(isolate, pReturn)*/);
//Local<Integer> num = Uint32::New(isolate, value);


// Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
//args.GetReturnValue().Set(num);
}


/* read actual spindle speed(S) */
//FWLIBAPI short WINAPI cnc_acts(unsigned short, ODBACT *);
void Cnc_acts(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();


// Check the number of arguments passed.
if (args.Length() != 2) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}


HINSTANCE hDLL;
hDLL = LoadLibrary("Fwlib32.dll");//加载动态链接库Fwlib32.dll文件;
if (!hDLL) {
// Throw an Error that is passed back to JavaScript
DWORD dRe = GetLastError();
char sz[4];
sprintf(sz, "cat not get lib, error is %d", dRe);
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, sz)));
return;
}


typedef  short(WINAPI *pMax)(unsigned short, ODBAXIS *);//函数指针
pMax pCnc_resetconnect = NULL;
pCnc_resetconnect = (pMax)GetProcAddress(hDLL, "cnc_acts");
if (!pCnc_resetconnect) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "can not get function: cnc_acts from Fwlib32.dll.")));
return;
}


unsigned short arg0 = args[0]->Uint32Value();
ODBAXIS obj = {};;
short value = pCnc_resetconnect(arg0, &obj);


Local<Object> objR = Object::New(isolate);
char* pReturn = GetOdbaxis(value, obj, isolate, objR);
args.GetReturnValue().Set(objR/*String::NewFromUtf8(isolate, pReturn)*/);
//Local<Integer> num = Uint32::New(isolate, value);


// Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
//args.GetReturnValue().Set(num);
}


typedef struct odbst {
short dummy[2];     /* dummy                    */
short aut;          /* selected automatic mode  */
short manual;       /* selected manual mode     */
short run;          /* running status           */
short edit;         /* editting status          */
short motion;       /* axis, dwell status       */
short mstb;         /* m, s, t, b status        */
short emergency;    /* emergency stop status    */
short write;        /* writting status          */
short labelskip;    /* label skip status        */
short alarm;        /* alarm status             */
short warning;      /* warning status           */
short battery;      /* battery status           */
} ODBST;


void GetODBST( short iRe, const ODBST& dta, Isolate* isolate, Local<Object>& obj)
{
Local<Integer> num = Uint32::New(isolate, iRe);
Local<Array> dummy = Array::New(isolate, 2);
dummy->Set(0, Uint32::New(isolate, dta.dummy[0]));
dummy->Set(0, Uint32::New(isolate, dta.dummy[1]));
Local<Integer> aut = Uint32::New(isolate, dta.aut);
Local<Integer> manual = Uint32::New(isolate, dta.manual);
Local<Integer> run = Uint32::New(isolate, dta.run);
Local<Integer> edit = Uint32::New(isolate, dta.edit);
Local<Integer> motion = Uint32::New(isolate, dta.motion);
Local<Integer> mstb = Uint32::New(isolate, dta.mstb);
Local<Integer> emergency = Uint32::New(isolate, dta.emergency);
Local<Integer> write = Uint32::New(isolate, dta.write);
Local<Integer> labelskip = Uint32::New(isolate, dta.labelskip);
Local<Integer> alarm = Uint32::New(isolate, dta.alarm);
Local<Integer> warning = Uint32::New(isolate, dta.warning);
Local<Integer> battery = Uint32::New(isolate, dta.battery);


obj->Set(String::NewFromUtf8(isolate, "re"), num);
obj->Set(String::NewFromUtf8(isolate, "dummy"), dummy);
obj->Set(String::NewFromUtf8(isolate, "aut"), aut);
obj->Set(String::NewFromUtf8(isolate, "manual"), manual);
obj->Set(String::NewFromUtf8(isolate, "run"), run);
obj->Set(String::NewFromUtf8(isolate, "edit"), edit);
obj->Set(String::NewFromUtf8(isolate, "motion"), motion);
obj->Set(String::NewFromUtf8(isolate, "mstb"), mstb);
obj->Set(String::NewFromUtf8(isolate, "emergency"), emergency);
obj->Set(String::NewFromUtf8(isolate, "write"), write);
obj->Set(String::NewFromUtf8(isolate, "labelskip"), labelskip);
obj->Set(String::NewFromUtf8(isolate, "alarm"), alarm);
obj->Set(String::NewFromUtf8(isolate, "warning"), warning);
obj->Set(String::NewFromUtf8(isolate, "battery"), battery);
}


/* read CNC status information */
//FWLIBAPI short WINAPI cnc_statinfo(unsigned short, ODBST *);
void Cnc_statinfo(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();


// Check the number of arguments passed.
if (args.Length() != 2) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}


HINSTANCE hDLL;
hDLL = LoadLibrary("Fwlib32.dll");//加载动态链接库Fwlib32.dll文件;
if (!hDLL) {
// Throw an Error that is passed back to JavaScript
DWORD dRe = GetLastError();
char sz[4];
sprintf(sz, "cat not get lib, error is %d", dRe);
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, sz)));
return;
}


typedef  short(WINAPI *pMax)(unsigned short, ODBST *);//函数指针
pMax pCnc_resetconnect = NULL;
pCnc_resetconnect = (pMax)GetProcAddress(hDLL, "cnc_statinfo");
if (!pCnc_resetconnect) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "can not get function: cnc_statinfo from Fwlib32.dll.")));
return;
}


unsigned short arg0 = args[0]->Uint32Value();
ODBST obj = {};;
short value = pCnc_resetconnect(arg0, &obj);


Local<Object> objR = Object::New(isolate);
/*char* pReturn = */GetODBST(value, obj, isolate, objR);
args.GetReturnValue().Set(objR/*String::NewFromUtf8(isolate, pReturn)*/);


//Local<Integer> num = Uint32::New(isolate, value);


// Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
//args.GetReturnValue().Set(num);
}


//0iB
/* read program name under execution */
//FWLIBAPI short WINAPI cnc_exeprgname(unsigned short, ODBEXEPRG *);
void Cnc_exeprgname(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();


// Check the number of arguments passed.
if (args.Length() != 2) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments")));
return;
}


HINSTANCE hDLL;
hDLL = LoadLibrary("Fwlib32.dll");//加载动态链接库Fwlib32.dll文件;
if (!hDLL) {
// Throw an Error that is passed back to JavaScript
DWORD dRe = GetLastError();
char sz[4];
sprintf(sz, "cat not get lib, error is %d", dRe);
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, sz)));
return;
}


typedef  short(WINAPI *pMax)(unsigned short, ODBAXIS *);//函数指针
pMax pCnc_resetconnect = NULL;
pCnc_resetconnect = (pMax)GetProcAddress(hDLL, "cnc_exeprgname");
if (!pCnc_resetconnect) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "can not get function: cnc_exeprgname from Fwlib32.dll.")));
return;
}


unsigned short arg0 = args[0]->Uint32Value();
ODBAXIS obj = {};;
short value = pCnc_resetconnect(arg0, &obj);


Local<Object> objR = Object::New(isolate);
char* pReturn = GetOdbaxis(value, obj, isolate, objR);
args.GetReturnValue().Set(objR/*String::NewFromUtf8(isolate, pReturn)*/);
//Local<Integer> num = Uint32::New(isolate, value);


// Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
//args.GetReturnValue().Set(num);
}




void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "cnc_allclibhndl3", Cnc_allclibhndl3); 
NODE_SET_METHOD(exports, "cnc_resetconnect", Cnc_resetconnect); 
NODE_SET_METHOD(exports, "cnc_machine", Cnc_machine);
NODE_SET_METHOD(exports, "cnc_relative", Cnc_relative); 
NODE_SET_METHOD(exports, "cnc_absolute", Cnc_absolute);
NODE_SET_METHOD(exports, "cnc_distance", Cnc_distance);
NODE_SET_METHOD(exports, "cnc_actf", Cnc_actf);
NODE_SET_METHOD(exports, "cnc_acts", Cnc_acts);
NODE_SET_METHOD(exports, "cnc_exeprgname", Cnc_exeprgname);
NODE_SET_METHOD(exports, "cnc_statinfo", Cnc_statinfo);
ODBAXISEx::Init(exports);
}


NODE_MODULE(addon, init)


}  // namespace demo
2018-10-19 22:13:59 u011607490 阅读数 116
  • Nodejs极速入门

    由King老师精心打造的Nodejs极速入门课程,从Nodejs介绍到环境下载安装,到Express框架的基础使用,用短的时间帮你入门Nodejs! 山哥出品,必属精品!

    31086 人正在学习 去看看 何山

nodemon

nodemon是一个基于node的工具,它可以监测文件内容变化并执行相应的命令

既然它能监测javascript、css、html等文件的变化,那应该也可以监测c++ 吧

  • 先理清各任务的先后执行顺序
  1. 运行nodemon监视cpp文件
  2. 编辑cpp文件并保存,触发nodemon执行命令
  3. nodemon命令1:g++编译cpp文件得到可执行*.out文件
  4. nodemon命令2:命令行下输入可执行*.out文件路径,运行程序
    测试目录

package.json

{
  "name": "npm",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "cppRun": "./main.out",
    "compile": "g++ main.cpp -o main.out",
    "cppBoot": "npm run compile && echo '--- cpp output ---\n' && npm run cppRun && echo '\n--- cpp end ---'",
    "start": "npm run watch-cpp",
    "watch-cpp": "nodemon --watch main.cpp --exec 'npm run cppBoot'"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "nodemon": "^1.18.4"
  }
}
2019-03-24 01:42:15 THEANARKH 阅读数 364
  • Nodejs极速入门

    由King老师精心打造的Nodejs极速入门课程,从Nodejs介绍到环境下载安装,到Express框架的基础使用,用短的时间帮你入门Nodejs! 山哥出品,必属精品!

    31086 人正在学习 去看看 何山

nodejs的很多功能都是通过c++或者通过c++层调用libuv层实现的,nodejs是如何在js层面调用c++的呢?在nodejs里,使用c++层的功能主要有两种方式,第一种就是调用全局变量process,Buffer等,第二种就是通过process.binding函数。

1 process、buffer等全局变量

首先来看第一个种。在nodejs启动的过程中。在Environment::Start函数中生成了process对象。

// 利用v8新建一个函数
  auto process_template = FunctionTemplate::New(isolate());
  // 设置函数名
  process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate(), "process"));
  // 利用函数new一个对象
  auto process_object = process_template->GetFunction()->NewInstance(context()).ToLocalChecked();
  // 设置env的一个属性,val是process_object
  set_process_object(process_object);
  // 设置process对象的属性
  SetupProcessObject(this, argc, argv, exec_argc, exec_argv);

以上代码生成了一个process对象并且保存在env里。然后继续执行LoadEnvironment函数。在该函数里会执行bootstrap_node.js。然后执行bootstrap_node.js导出的函数。

 Local<String> script_name = FIXED_ONE_BYTE_STRING(env->isolate(),"bootstrap_node.js");
 // 执行bootstrap_node.js
 Local<Value> f_value = ExecuteString(env, MainSource(env), script_name);
 Local<Function> f = Local<Function>::Cast(f_value);
 // 全局变量,我们访问全局变量的时候都是global的属性
 Local<Object> global = env->context()->Global()
 // js层的全局变量,类似浏览器的window
 global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global);
 Local<Value> arg = env->process_object();
 // 执行bootstrap_node.js 
 auto ret = f->Call(env->context(), Null(env->isolate()), 1, &arg);

在bootstrap_node.js的函数里执行setupGlobalVariables函数。

global.process = process;
global.Buffer = NativeModule.require('buffer').Buffer;
process.domain = null;
process._exiting = false;

以上代码设置了几个全局变量,我们访问process的时候就是访问global.process。当v8编译执行bootstrap_node.js的时候,传进来了一个c++的对象process,在js层调用global其实就是调用了c++层的global对象,执行global.process = process的时候,即在c++层面给c++层的global对象新增了一个属性process,他的值是传进来的c++对象process。所以当我们在js层面访问process的时候,v8会在c++层面的global对象里查找process属性,这时候就会找到传进来的c++对象。

2 process.binding

我们看一下我们在js里调用process.binding函数的时候,nodejs都做了什么,首先看一下process.binding的函数是怎么来的,我们知道在nodejs启动的时候新建了一个process对象,并且通过node.cc的SetupProcessObject函数挂载了一系列属性。其中设置了一个属性就是binding

env->SetMethod(process, "binding", Binding);

static void Binding(const FunctionCallbackInfo<Value>& args) {
  Environment* env = Environment::GetCurrent(args);

  CHECK(args[0]->IsString());

  Local<String> module = args[0].As<String>();
  node::Utf8Value module_v(env->isolate(), module);

  node_module* mod = get_builtin_module(*module_v);
  Local<Object> exports;
  if (mod != nullptr) {
    exports = InitModule(env, mod, module);
  } else if (!strcmp(*module_v, "constants")) {
    exports = Object::New(env->isolate());
    CHECK(exports->SetPrototype(env->context(),
                                Null(env->isolate())).FromJust());
    DefineConstants(env->isolate(), exports);
  } else if (!strcmp(*module_v, "natives")) {
    exports = Object::New(env->isolate());
    DefineJavaScript(env, exports);
  } else {
    return ThrowIfNoSuchModule(env, *module_v);
  }

  args.GetReturnValue().Set(exports);
}

nodejs的启动流程文章中我们分析过binding函数就是在一个内置模块链表中找到对应的模块。然后执行该模块注册的时候,然后返回一个该模块导出的对象。这就是底层的binding函数做的功能,但是我们在调process.binding的时候,并不是直接执行了c++层的binding函数。在bootstrap_node.js中还封装了一层。

const bindingObj = Object.create(null);

const getBinding = process.binding;
 process.binding = function binding(module) {
   module = String(module);
   let mod = bindingObj[module];
   if (typeof mod !== 'object') {
     mod = bindingObj[module] = getBinding(module);
     moduleLoadList.push(`Binding ${module}`);
   }
   return mod;
 };

nodejs在这加了一层缓存。下面我们以net.js调用tcp_wrap.cc为例看一下js是如何调用c++的功能的。
当我们执行以下代码时,

const { TCP } = process.binding('tcp_wrap');

v8首先编译js代码,然后在执行时访问process对象,根据1中的分析,这时候就会访问c++层的process对象,然后访问binding属性,即上面绑定的binding函数,该函数会调用C++层的binding函数,返回一个导出的对象exports。接下来我们执行TCP.a或者new TCP的时候,其实就类似于调用一个c++对象的属性或者在c++层面new一个对象一样。这个是由v8进行转换的。即v8在编译解析TCP这个字符串的时候他就会生成访问底层TCP类的代码。

理解js是如何调用c++的,不能把思路停留到静态,要结合v8是如何编译和执行js代码的。比如v8在编译这段代码。

const { TCP } = process.binding('tcp_wrap');
const tcp = new TCP();
tcp.listen();

转化成c++代码或者二进制代码反编译成c++后可能是

HashTable *hash = new HashTable();

object * binding(char *str) {
	if (hash[str]) {
		return hash[str];
	}
	return hash[str] = Binding(str);
}
object * Binding(char *str) {}

Object *process = new Object();
Object *Tcp_wrap = new Object();

process->binding = binding
Function *TCP = process.binding('tcp_wrap'); => Function *TCP = Tcp_wrap;
object* tcp = new Tcp_wrap();
tcp.listen();

在js里直接调用c++是不可以的,但是js最终是要编译成二进制代码的。在二进制的世界里,js代码和c++代码就可以通信了,因为nodejs定义的那些c++模块和c++变量都是基于v8的架构的,比如定义了一个process对象,或者Binding函数,都是利用了v8的规则和接口。所以在nodejs里,不管是v8内置的js函数,对象,还是nodejs额外提供的那些模块功能,他最后都是由v8去处理的。虽然无法在js里直接调用c++功能,但是可以在js被编译后使用c++功能。而nodejs的实现方案就是实现一个process对象和Binding函数。js里通过process.binding加载一个c++模块的时候,这段js在编译后执行,首先访问js层的process对象,v8知道js的process对象对应是c++的process对象,就像我们在js里定义一个函数或者对象,在编译后v8也知道应该调用的是c++哪些代码,因为我们怎么写脚本以及使用什么功能但是v8提供的,v8在编译执行我们的js脚本的时候,当我会知道需要执行底层哪些代码。所以v8知道需要执行的是c++层的process对象里的Binding函数,通过底层的Binding,就可以使用c++模块的功能了。
当我们使用非v8提供的内置函数时,nodejs里是通过在执行时查找对应模块的形式去实现的,而不是通过在v8的global变量里挂载新的函数。下面是在v8的global里挂载自定义对象的大致流程。

void say(){}
Handle<FunctionTemplate> a_template = FunctionTemplate::New(callbackWhenNewObject);
a_template ->SetClassName(String::New("a_template"));
Handle<ObjectTemplate> a_template _proto = a_template->PrototypeTemplate();
a_template _proto->Set(String::New("say"), FunctionTemplate::New(say));
// 挂载到全局变量,我们在js里就可以直接访问a_template 
global->Set(String::New("a_template "), a_template );

在js里调用

new a_template().say();

总得来说,js是调用c++功能是通过process.binding去实现的,首先是我们在js里调用js层的process.binding,通过v8提供的功能,js层的process.binding在执行的时候是对应c++层的某段代码的,所以js被编译执行时就可以使用c++提供的功能了,因为这时候c++代码和js代码都被编译成二进制代码,通过process.binding就把这两个层面的代码在底层联系起来。

nodejs调用c++dll感想

阅读数 1664

没有更多推荐了,返回首页