注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

让一切都逝去吧

排骨炖泥菜/猪肝炒苹果/鱼籽狗肉汤/狗头薏米汤

 
 
 

日志

 
 

c++扩展node  

2014-10-01 11:42:10|  分类: js |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
      如题,需要安装node-gyp
npm install node-gyp -g
      node-gyp的url:https://github.com/TooTallNate/node-gyp
上面有安装使用说明,里面对python要求是v2.7,本机版本是v3.4,为了使用node-gyp需要把环境变量临时改回v2.7
You will also need to install:

On Unix:
python (v2.7 recommended, v3.x.x is not supported)
make
A proper C/C++ compiler toolchain, like GCC
On Windows:
Python (v2.7.3 recommended, v3.x.x is not supported)
Windows XP/Vista/7:
Microsoft Visual Studio C++ 2010 (Express version works well)
For 64-bit builds of node and native modules you will also need the Windows 7 64-bit SDK
If the install fails, try uninstalling any C++ 2010 x64&x86 Redistributable that you have installed first.
If you get errors that the 64-bit compilers are not installed you may also need the compiler update for the Windows SDK 7.1
Windows 7/8:
Microsoft Visual Studio C++ 2012 for Windows Desktop (Express version works well)

node-gyp命令说明
Command Description
build Invokes make/msbuild.exe and builds the native addon
clean Removes any the build dir if it exists
configure Generates project build files for the current platform
rebuild Runs "clean", "configure" and "build" all in a row
install Installs node development header files for the given version
list Lists the currently installed node development file versions
remove Removes the node development header files for the given version

下面写一个C++ 文件,hello.cc
#include <node.h>
#include <v8.h>
using namespace v8;
Handle<Value> Hello(const Arguments& args)
{
    //...
}
返回值是Handle<Value>
V8 里使用 Handle 类型来托管 JavaScript 对象,与 C++ 的 std::sharedpointer 类似,Handle 类型间的赋值均是直接传递对象引用,但不同的是,V8 使用自己的 GC 来管理对象生命周期,而不是智能指针常用的引用计数。

JavaScript 类型在 C++ 中均有对应的自定义类型,如 String 、 Integer 、 Object 、 Date 、 Array 等,严格遵守在 JavaScript 中的继承关系。 C++ 中使用这些类型时,必须使用 Handle 托管,以使用 GC 来管理它们的生命周期,而不使用原生栈和堆。
而这个所谓的 Value ,从 V8 引擎的头文件 v8.h 中的各种继承关系中可以看出来,其实就是 JavaScript 中各种对象的基类。

Arguments
  这个就是传入这个函数的参数了。我们都知道在 Node.js 中,参数个数是乱来的。而这些参数传进去到 C++ 中的时候,就转变成了这个 Arguments 类型的对象了。

Handle<Value> Hello(const Arguments& args)
{
    HandleScope scope;
    return scope.Close(String::New("world"));
}
HandleScope
Handle 的生命周期和 C++ 智能指针不同,并不是在 C++ 语义的 scope 内生存(即{} 包围的部分),而需要通过 HandleScope 手动指定。HandleScope 只能分配在栈上,HandleScope 对象声明后,其后建立的 Handle 都由 HandleScope 来管理生命周期,HandleScope 对象析构后,其管理的 Handle 将由 GC 判断是否回收。

所以呢,我们得在需要管理他的生命周期的时候申明这个 Scope 。为什么代码不这么写呢?
Handle<Value> Hello(const Arguments& args)
{
    HandleScope scope;
    return String::New("world");
}
  因为当函数返回时,scope 会被析构,其管理的Handle也都将被回收,所以这个 String 就会变得没有意义。
这时需要HandleScope::Close(Handle<T> Value) 函数!这个函数的用处就是关闭这个 Scope 并且把里面的参数转交给上一个 Scope 管理,也就是进入这个函数前的 Scope。

导出对象
void init(Handle<Object> exports)
{
    exports->Set(String::NewSymbol("hello"),
        FunctionTemplate::New(Hello)->GetFunction());
}
还有另外一种导出方式也可以
void init(Handle<Object> exports)
{
NODE_SET_METHOD(exports, "mtc", mtc);
}

最后要申明
NODE_MODULE(hello, init);
NODE_MODULE 是一个宏,它的意思呢就是说我们采用 init 这个初始化函数来把要导出的东西导出到 hello 中。这个 hello 是指你的这个最终编译好的二进制文件名叫什么,记得要除去后缀名。

在nodejs项目里添加binding.gyp
{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ]
    }
  ]
}

configure
  在文件搞好之后,我们要在这个目录下面执行这个命令了:
$ node-gyp configure
  如果一切正常的话,应该会生成一个 build 的目录,然后里面有相关文件,也许是 M$ Visual Studio 的 vcxproj 文件等,也许是 Makefile ,视平台而定。vcxproj 文件可以用visiual studio打开,这样可以用之编辑c++代码。

build
  Makefile 也生成好之后,我们就开始构造编译了:
$ node-gyp build
build成功后build/Release 目录,下面有一个 hello.node 文件。

建一个文件 jianfeizao.js:
var addon = require("./build/Release/hello");
console.log(addon.hello());
就可以使用c++编写的node模块了。

    传递参数,就是要使用Arguments类型的变量args。使用说明参看
http://izs.me/v8-docs/classv8_1_1Arguments.html
v8说明文档
http://izs.me/v8-docs/main.html
String的说明
http://izs.me/v8-docs/classv8_1_1String.html#af60c183b13ca1caebc7ea5d4bde593e6
总是就是args.Length()获得参数个数,然后一系列IsXxx()的方法判断类型,
IsArray()
IsBoolean()
IsDate()
IsFunction()
IsInt32()
IsNativeError()
IsNull()
IsNumber()
IsRegExp()
IsString()
...
下面是相加操作的例子
Handle<Value> Add(const Arguments& args)
{
HandleScope scope;
if (args.Length() < 2)
{
ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
return scope.Close(Undefined());
}
if (!args[0]->IsNumber() || !args[1]->IsNumber())
{
ThrowException(Exception::TypeError(String::New("Wrong arguments")));
return scope.Close(Undefined());
}
Local<Number> num = Number::New(args[0]->NumberValue() + args[1]->NumberValue());
return scope.Close(num);
}
获得参数值的方法是XxxValue()这样的,只有String特殊点,是这样的
args[0]->ToString()

ThrowException
    抛出异常,相当于在 Node.js 本地文件中执行了一条 throw() 语句一样。比如说:
ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
  就相当于执行了一条 Node.js 的:
throw new TypeError("Wrong number of arguments");

Undefined()
  具体就是一个空值,因为有些函数并不需要返回什么具体的值,或者说没有返回值,这个时候就需要用 Undefined() 来代替。

两个方法可以共存,新增的Add方法在init里面添加一个导出
void init(Handle<Object> exports)
{
    exports->Set(String::NewSymbol("hello"),
        FunctionTemplate::New(Hello)->GetFunction());
    exports->Set(String::NewSymbol("add"),
        FunctionTemplate::New(Add)->GetFunction());
}
就可以这样使用
console.log(addon.add(1, 1));

      如果需要将两个字符拼接起来,在c++里面好麻烦。String::New()接收的是个char*类型,于是拼接就是要把两个char*类型变量拼接起来。目前了解到有两个方法,把char*转成stl中的string,使用string的+连接再转回char*;另外就是使用strcat函数。
首先,获取字符串类型参数
Local<String> a = args[0]->ToString();
char* w = new char[a->Length()];
a->WriteAscii(w);
假设另外有个char* 类型变量b
std::string b = std::string(w);
s = b + s;
b = s.c_str();
char * pstr=new char[s.length() + 1];//strcat
strcpy(pstr, s.c_str());
然后使用pstr作为返回值
return scope.Close(String::New(pstr));

直接连接char*
char* param = " yeah yeah yeah";
char* result = (char *)malloc((std::strlen(w) + std::strlen(param)) * sizeof(char));
strcpy(result,w);  
    strcat(result,param);  
    char* str=(LPSTR)result;
然后使用str作为返回值
return scope.Close(String::New(str));

回调函数
Handle<Value> RunCallback(const Arguments& args)
{
HandleScope scope;
Local<Function> cb = Local<Function>::Cast(args[0]);
const unsigned argc = 1;
Local<Value> argv[argc] = { Local<Value>::New(String::New("dan dan")) };
cb->Call(Context::GetCurrent()->Global(), argc, argv);
return scope.Close(Undefined());
}

Local<T> 继承自 Handle<T>。
Handle 有两种类型, Local Handle 和 Persistent Handle ,类型分别是 Local<T> : Handle<T> 和 Persistent<T> : Handle<T> ,前者和 Handle<T> 没有区别生存周期都在 scope 内。而后者的生命周期脱离 scope ,你需要手动调用 Persistent::Dispose 结束其生命周期。也就是说 Local Handle 相当于在 C++`在栈上分配对象而 Persistent Handle 相当于 C++ 在堆上分配对象。
参数列表
  终端命令行调用 C/C++ 之后怎么取命令行参数?
#include <stdio.h>
void main(int argc, char* argv[])
{
    // ...
}
argc 就是命令行参数个数,argv[] 就是各个参数了。那么调用 Node.js 的回调函数,v8 也采用了类似的方法:
V8EXPORT Local<Value> v8::Function::Call(Handle<Object>recv,
    int argc,
    Handle<Value> argv[]
);
后面两个参数就不多说了,一个是参数个数,另一个就是一个参数的数组了。至于第一个参数 Handle<Object> recv:

It is the same as apply in JS. In JS, you do

var context = ...;
cb.apply(context, [ ...args...]);
The object passed as the first argument becomes this within the function scope. More documentation on MDN. If you don't know JS well, you can read more about JS's this here: http://unschooled.org/2012/03/understanding-javascript-this/

  总之其作用就是指定了被调用函数的 this 指针。这个 Call 的用法就跟 JavaScript 中的 bind()、call()、apply() 类似。
  所以我们要做的事情就是先把参数表建好,然后传入这个 Call 函数供其执行。
  第一步,显示转换函数,因为本来是 Object 类型:
Local<Function> cb = Local<Function>::Cast(args[0]);
  第二步,建立参数表(数组):
Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) };
最后调用函数系列
  调用 cb ,把参数传进去:
cb->Call(Context::GetCurrent()->Global(), 1, argv);
  这里第一个参数 Context::GetCurrent()->Global() 所代表的意思就是获取全局上下文作为函数的 this;第二个参数就是参数表中的个数(毕竟虽然 Node.js 的数组是有长度属性的,但是 C++ 里面数组的长度实际上系统是不知道的,还得你自己传进一个数来说明数组长度);最后一个参数就是刚才我们建立好的参数表了。

nodejs项目里定义个函数
var func = function(msg) {
    console.log(msg);
};
依然添加导出
void init(Handle<Object> exports)
{
    exports->Set(String::NewSymbol("hello"),
        FunctionTemplate::New(Hello)->GetFunction());
    exports->Set(String::NewSymbol("add"),
        FunctionTemplate::New(Add)->GetFunction());
    exports->Set(String::NewSymbol("callback"),
        FunctionTemplate::New(RunCallback)->GetFunction());
}
在nodejs中使用
addon.callback(func);
注意这callback函数的参数直接是函数类型的变量func,这里不能使用"func",这样会报类型错误。

参考
http://segmentfault.com/blog/xadillax/1190000000453606
http://segmentfault.com/blog/xadillax/1190000000456743
http://segmentfault.com/blog/waterblog/1190000000516986
  评论这张
 
阅读(837)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017