In order to be compatible for any version of Node.js and io.js. We can use the Nan library: " https://github.com/nodejs/nan "
If you have used the old version of Nan and want to upgrade it, you should do the following changes.
- // The Legacy Nan example
- unsigned int microSeconds = args[0]->Uint32Value();
- NanReturnValue(NanNew<String>("text"));
- NanReturnThis();
- Handle<Value> key = Nan::New<String>("DOMDocument");
- NODE_SET_PROTOTYPE_METHOD(constructor, "load", load);
- // The latest Nan example
- unsigned int microSeconds = info[0]->Uint32Value();
- info.GetReturnValue().Set(Nan::New<String>("text"));
- info.GetReturnValue().Set(info.This());
- Handle<Value> key = Nan::New<String>("DOMDocument").ToLocalChecked();
- Nan::SetPrototypeMethod(constructor, "load", load);
After that here has more variable conversions for you.
- String::Utf8Value cmd(info[0]);
- unsigned int len = info[0]->Uint32Value();
- bool isBinary = info[0]->BooleanValue();
- Local<Object> bufferObj = info[1]->ToObject();
- char* msg = Buffer::Data(bufferObj);
Pass a JavaScript string into native C/C++ with Nan library
- #include <iostream>
- #include <node.h>
- #include <nan.h>
- using namespace std;
- using namespace v8;
- using namespace nan;
- NAN_METHOD(name)
- {
- Nan::HandleScope scope;
- String::Utf8Value cmd(info[0]);
- string s = string(*cmd);
- info.GetReturnValue().Set(Nan::New<String>(s.c_str()).ToLocalChecked());
- }
Pass a JavaScript integer into native C/C++ with Nan library
- #include <iostream>
- #include <node.h>
- #include <nan.h>
- using namespace std;
- using namespace v8;
- using namespace nan;
- NAN_METHOD(name)
- {
- Nan::HandleScope scope;
- unsigned int len = info[0]->Uint32Value();
- info.GetReturnValue().Set(Nan::New<Integer>(len).ToLocalChecked());
- }
Pass a Node.js binary string into native C/C++ with Nan library
When you have Unicode String which include some un-English letters. Then you must pass the value in a binary format.
- #include <iostream>
- #include <node.h>
- #include <nan.h>
- using namespace std;
- using namespace v8;
- using namespace nan;
- NAN_METHOD(sendData)
- {
- Local<Object> bufferObj = info[0]->ToObject();
- unsigned int len = info[1]->Uint32Value();
- char* msg = Buffer::Data(bufferObj);
- info.GetReturnValue().Set(Nan::New<String>(msg, len).ToLocalChecked());
- // You can not use strlen(msg). Because it is a binary not a string. The end of string is a null byte and the end of binary data is not null.
- }
- //
- var data = "The data include some UTF8 encoding string.";
- length = Buffer.byteLength(data); //UTF8 length
- var buf = new Buffer(length);
- buf.write(data, 0, 'UTF8');
- native.sendData(buf, length);
Pass a JavaScript array into native C/C++ with Nan library
- NAN_METHOD(passArray) {
- Nan::HandleScope scope;
- vector<string> result;
- Handle<Value> val;
- Local<Array> arr = Nan::New<Array>();
- if (info[0]->IsArray()) {
- Handle<Array> jsArray = Handle<Array>::Cast(info[0]);
- for (unsigned int i = 0; i < jsArray->Length(); i++) {
- val = jsArray->Get(i);
- result.push_back(string(*String::Utf8Value(val)));
- Nan::Set(arr, i, val);
- }
- }
- info.GetReturnValue().Set(arr);
- }
Pass a function into native C/C++ with Nan library
- #include <node.h>
- #include <nan.h>
- using namespace node;
- using namespace v8;
- NAN_METHOD(runCallback) {
- Nan::HandleScope scope;
- Local<Object> context = Local<Object>::Cast(info[0]);
- Handle<Value> param1 = Handle<Value>::Cast(info[1]);
- Local<v8::Function > callback_handle = Local<v8::Function>::Cast(info[2]);
- Local<Value> argv[1] = { param1 };
- Nan::MakeCallback(context, callback_handle, 1, argv);
- info.GetReturnValue().SetUndefined();
- }
- void init (Handle<Object> target){
- Nan::HandleScope scope;
- Nan::SetMethod(target, "runCallback", runCallback);
- }
- NODE_MODULE(obj, init)
After the success of compiling, then you can write a Node.js code to test the native shared object.
- var native = require('./build/Release/callback.node');
- function person() {
- this.age = 18;
- }
- person.prototype.run = function () {console.log("My age is " + this.age);};
- var p = new person();
- p.age = 20;
- native.runCallback(
- p, "the value",
- function (param1) {
- this.run();
- console.log("param1 = " + param1);
- }
- );
How to return "this" context
- info.GetReturnValue().Set(info.This());
Examples
You can see more examples at my Node.js library called phplike.
Old Version of Nan library
The following is only used for the old version of Node.js 0.10.x. Node.js have already upgrade the latest JavaScript V8 engine. And we will used the new code to write a Node.js addon as stated previous paragraph.
Pass a JavaScript string into C/C++
- #include <iostream>
- #include "v8.h"
- #include <string>
- using namespace std;
- using namespace v8;
- Handle<Value> passString(const Arguments& args)
- {
- String::Utf8Value cmd(args[0]);
- string s = string(*cmd);
- return String::New(s.c_str());
- //Integer::New(1)
- }
Pass a JavaScript integer into C/C++
- #include <iostream>
- #include "v8.h"
- #include <string>
- using namespace std;
- using namespace v8;
- Handle<Value> passInt(const Arguments& args)
- {
- unsigned int microSeconds = args[0]->Uint32Value();
- return True();
- }
Pass a JavaScript array into C/C++
- #include <iostream>
- #include "v8.h"
- #include "node.h"
- #include <string>
- #include <vector>
- using namespace std;
- using namespace v8;
- using namespace node;
- Handle<Value> passArray(const Arguments& args) {
- vector<string> result;
- Handle<Value> val;
- if (args[0]->IsArray()) {
- Handle<Array> jsArray = Handle<Array>::Cast(arg[0]);
- for (int i = 0; i < jsArray->Length(); i++) {
- val = jsArray->Get(Integer::New(i));
- result.push_back(string(*String::Utf8Value(val)));
- }
- }
- return String::New("success");
- }
Pass a JavaScript object into C/C++
Here is a example to get the value from a specific keyname and if this value is a function , we convert it into the variable type of v8 function Handle.
- #include <iostream>
- #include "v8.h"
- #include "node.h"
- #include <string>
- using namespace std;
- using namespace v8;
- using namespace node;
- Handle<Value> passObject(const Arguments& args) {
- if (args[0]->IsObject()) {
- Handle<Object> object = Handle<Object>::Cast(args[0]);
- Handle<Value> fieldValue = object->Get(String::New("key1"));
- Handle<Value> callback = object->Get(String::New("callback"));
- if (callback->IsFunction()) {
- Handle<Function> fn = Handle<Function>::Cast(callback);
- }
- }
- }
Using the forloop to get every key and value from the object.
- #include <iostream>
- #include "v8.h"
- #include "node.h"
- #include <string>
- #include <map>
- using namespace std;
- using namespace v8;
- using namespace node;
- Handle<Value> passObject(const Arguments& args) {
- map<string, string> options;
- int i,n;
- if (args[0]->IsObject()) {
- jsOptions = Handle<Object>::Cast(args[0]);
- propertyNames = jsOptions->GetPropertyNames();
- n = propertyNames->Length();
- for (i = 0; i < n ; i++) {
- Handle<Value> b = propertyNames->Get(Integer::New(i));
- string c = string(*String::Utf8Value(b));
- Handle<Value> v = jsOptions->Get(b);
- options[c] = string(*String::Utf8Value(v));
- }
- }
- }
Pass a JavaScript boolean into C/C++
- #include <iostream>
- #include "v8.h"
- #include <string>
- using namespace std;
- using namespace v8;
- Handle<Value> passInt(const Arguments& args)
- {
- bool = args[0]->BooleanValue();
- return bool;
- }
Pass a JavaScript binary into C/C++ char*
- #include <iostream>
- #include "v8.h"
- #include <string>
- using namespace std;
- using namespace v8;
- Handle<Value> passBinary(const Arguments& args)
- {
- Local<Object> bufferObj = args[1]->ToObject();
- char* msg = Buffer::Data(bufferObj);
- }
目前回應 Comments(2 comments)
Sosh 2015/09/02
Can you point me in the direction of the documentation for that NanMakeCallback() function you used? I can't find any. Thanks.
ReplyAdmin
Here is the documentation of NnaMakeCallBack(), But I think this documentation is not clear enough.
https://github.com/nodejs/nan/blob/master/doc/node_misc.md#api_nan_make_callback
v8::Local<v8::Value> Nan::MakeCallback(
v8::Local<v8::Object> target,
v8::Local<v8::Function> func,
int argc,
v8::Local<v8::Value>* argv
);
John Smith 2015/06/17
Thanks for this tutorial! It really helped me to write my first native Extension for Node.js
ReplyBut I had problems with callbacks! I want to call the function of an object. That's why I did as an example:
var obj = {callback: function(){console.log("Callback!!!"}};
var callback_func = function(obj, func){ obj[func]() };
Extension.callback(obj, "callback", callback_func)
And in my extension:
NAN_METHOD(callback) {
Handle callback_obj = Handle::Cast(args[0]);
v8::String::Utf8Value callback_funcObj(args[1]->ToString());
const char *callback_func = *callback_funcObj;
v8::Local callback_handle = args[2].As();
and then when I wanted to call It:
v8::Local argv[2] = { callback_obj, NanNew(callback_func)};
NanMakeCallback(NanGetCurrentContext()->Global(), callback_handle, 2, argv);
My question: Is there a better solution on how to trigger a Callback from a specific object? Thanks
Admin