2014
Nov
25

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.

Nan Conversion
  1. // The Legacy Nan example
  2. unsigned int microSeconds = args[0]->Uint32Value();
  3. NanReturnValue(NanNew<String>("text"));
  4. NanReturnThis();
  5. Handle<Value> key = Nan::New<String>("DOMDocument");
  6. NODE_SET_PROTOTYPE_METHOD(constructor, "load", load);
  7.  
  8. // The latest Nan example
  9. unsigned int microSeconds = info[0]->Uint32Value();
  10. info.GetReturnValue().Set(Nan::New<String>("text"));
  11. info.GetReturnValue().Set(info.This());
  12. Handle<Value> key = Nan::New<String>("DOMDocument").ToLocalChecked();
  13. Nan::SetPrototypeMethod(constructor, "load", load);

After that here has more variable conversions for you.

Brief
  1. String::Utf8Value cmd(info[0]);
  2. unsigned int len = info[0]->Uint32Value();
  3. bool isBinary = info[0]->BooleanValue();
  4.  
  5. Local<Object> bufferObj = info[1]->ToObject();
  6. char* msg = Buffer::Data(bufferObj);

Pass a JavaScript string into native C/C++ with Nan library

Example
  1. #include <iostream>
  2. #include <node.h>
  3. #include <nan.h>
  4. using namespace std;
  5. using namespace v8;
  6. using namespace nan;
  7.  
  8. NAN_METHOD(name)
  9. {
  10. Nan::HandleScope scope;
  11. String::Utf8Value cmd(info[0]);
  12. string s = string(*cmd);
  13. info.GetReturnValue().Set(Nan::New<String>(s.c_str()).ToLocalChecked());
  14. }

Pass a JavaScript integer into native C/C++ with Nan library

Example
  1. #include <iostream>
  2. #include <node.h>
  3. #include <nan.h>
  4. using namespace std;
  5. using namespace v8;
  6. using namespace nan;
  7.  
  8. NAN_METHOD(name)
  9. {
  10. Nan::HandleScope scope;
  11. unsigned int len = info[0]->Uint32Value();
  12. info.GetReturnValue().Set(Nan::New<Integer>(len).ToLocalChecked());
  13. }

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.

Example
  1. #include <iostream>
  2. #include <node.h>
  3. #include <nan.h>
  4. using namespace std;
  5. using namespace v8;
  6. using namespace nan;
  7.  
  8. NAN_METHOD(sendData)
  9. {
  10. Local<Object> bufferObj = info[0]->ToObject();
  11. unsigned int len = info[1]->Uint32Value();
  12. char* msg = Buffer::Data(bufferObj);
  13.  
  14. info.GetReturnValue().Set(Nan::New<String>(msg, len).ToLocalChecked());
  15. // 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.
  16. }
Node.js call native function.
  1. //
  2. var data = "The data include some UTF8 encoding string.";
  3. length = Buffer.byteLength(data); //UTF8 length
  4. var buf = new Buffer(length);
  5. buf.write(data, 0, 'UTF8');
  6. native.sendData(buf, length);

Pass a JavaScript array into native C/C++ with Nan library

Pass array and return array
  1. NAN_METHOD(passArray) {
  2. Nan::HandleScope scope;
  3. vector<string> result;
  4. Handle<Value> val;
  5. Local<Array> arr = Nan::New<Array>();
  6.  
  7. if (info[0]->IsArray()) {
  8. Handle<Array> jsArray = Handle<Array>::Cast(info[0]);
  9. for (unsigned int i = 0; i < jsArray->Length(); i++) {
  10. val = jsArray->Get(i);
  11. result.push_back(string(*String::Utf8Value(val)));
  12. Nan::Set(arr, i, val);
  13. }
  14. }
  15. info.GetReturnValue().Set(arr);
  16. }

Pass a function into native C/C++ with Nan library

callback.cc
  1. #include <node.h>
  2. #include <nan.h>
  3. using namespace node;
  4. using namespace v8;
  5.  
  6. NAN_METHOD(runCallback) {
  7. Nan::HandleScope scope;
  8. Local<Object> context = Local<Object>::Cast(info[0]);
  9. Handle<Value> param1 = Handle<Value>::Cast(info[1]);
  10. Local<v8::Function > callback_handle = Local<v8::Function>::Cast(info[2]);
  11.  
  12. Local<Value> argv[1] = { param1 };
  13. Nan::MakeCallback(context, callback_handle, 1, argv);
  14. info.GetReturnValue().SetUndefined();
  15. }
  16.  
  17. void init (Handle<Object> target){
  18. Nan::HandleScope scope;
  19. Nan::SetMethod(target, "runCallback", runCallback);
  20. }
  21.  
  22. NODE_MODULE(obj, init)

After the success of compiling, then you can write a Node.js code to test the native shared object.

call.js
  1. var native = require('./build/Release/callback.node');
  2.  
  3. function person() {
  4. this.age = 18;
  5. }
  6.  
  7. person.prototype.run = function () {console.log("My age is " + this.age);};
  8.  
  9. var p = new person();
  10. p.age = 20;
  11.  
  12. native.runCallback(
  13. p, "the value",
  14. function (param1) {
  15. this.run();
  16. console.log("param1 = " + param1);
  17. }
  18. );

How to return "this" context

Example
  1. info.GetReturnValue().Set(info.This());

Examples

You can see more examples at my Node.js library called phplike.



Old Version of Nan library

Please migrate your legacy code to support the latest version of Nan library as soon as possibile.

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++

Example
  1. #include <iostream>
  2. #include "v8.h"
  3. #include <string>
  4. using namespace std;
  5. using namespace v8;
  6.  
  7. Handle<Value> passString(const Arguments& args)
  8. {
  9. String::Utf8Value cmd(args[0]);
  10. string s = string(*cmd);
  11. return String::New(s.c_str());
  12. //Integer::New(1)
  13. }

Pass a JavaScript integer into C/C++

Example
  1. #include <iostream>
  2. #include "v8.h"
  3. #include <string>
  4. using namespace std;
  5. using namespace v8;
  6. Handle<Value> passInt(const Arguments& args)
  7. {
  8. unsigned int microSeconds = args[0]->Uint32Value();
  9.  
  10. return True();
  11. }

Pass a JavaScript array into C/C++

Example
  1. #include <iostream>
  2. #include "v8.h"
  3. #include "node.h"
  4. #include <string>
  5. #include <vector>
  6. using namespace std;
  7. using namespace v8;
  8. using namespace node;
  9.  
  10. Handle<Value> passArray(const Arguments& args) {
  11. vector<string> result;
  12. Handle<Value> val;
  13. if (args[0]->IsArray()) {
  14. Handle<Array> jsArray = Handle<Array>::Cast(arg[0]);
  15. for (int i = 0; i < jsArray->Length(); i++) {
  16. val = jsArray->Get(Integer::New(i));
  17. result.push_back(string(*String::Utf8Value(val)));
  18. }
  19. }
  20.  
  21. return String::New("success");
  22. }

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.

Example
  1. #include <iostream>
  2. #include "v8.h"
  3. #include "node.h"
  4. #include <string>
  5. using namespace std;
  6. using namespace v8;
  7. using namespace node;
  8.  
  9. Handle<Value> passObject(const Arguments& args) {
  10.  
  11. if (args[0]->IsObject()) {
  12. Handle<Object> object = Handle<Object>::Cast(args[0]);
  13. Handle<Value> fieldValue = object->Get(String::New("key1"));
  14. Handle<Value> callback = object->Get(String::New("callback"));
  15. if (callback->IsFunction()) {
  16. Handle<Function> fn = Handle<Function>::Cast(callback);
  17. }
  18. }
  19. }


Using the forloop to get every key and value from the object.

Example
  1. #include <iostream>
  2. #include "v8.h"
  3. #include "node.h"
  4. #include <string>
  5. #include <map>
  6. using namespace std;
  7. using namespace v8;
  8. using namespace node;
  9.  
  10. Handle<Value> passObject(const Arguments& args) {
  11. map<string, string> options;
  12. int i,n;
  13. if (args[0]->IsObject()) {
  14. jsOptions = Handle<Object>::Cast(args[0]);
  15. propertyNames = jsOptions->GetPropertyNames();
  16. n = propertyNames->Length();
  17. for (i = 0; i < n ; i++) {
  18. Handle<Value> b = propertyNames->Get(Integer::New(i));
  19. string c = string(*String::Utf8Value(b));
  20. Handle<Value> v = jsOptions->Get(b);
  21. options[c] = string(*String::Utf8Value(v));
  22. }
  23.  
  24. }
  25. }

Pass a JavaScript boolean into C/C++

Example
  1. #include <iostream>
  2. #include "v8.h"
  3. #include <string>
  4. using namespace std;
  5. using namespace v8;
  6. Handle<Value> passInt(const Arguments& args)
  7. {
  8. bool = args[0]->BooleanValue();
  9.  
  10. return bool;
  11. }

Pass a JavaScript binary into C/C++ char*

Example
  1. #include <iostream>
  2. #include "v8.h"
  3. #include <string>
  4. using namespace std;
  5. using namespace v8;
  6. Handle<Value> passBinary(const Arguments& args)
  7. {
  8. Local<Object> bufferObj = args[1]->ToObject();
  9. char* msg = Buffer::Data(bufferObj);
  10. }

Reference


目前回應 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.

    Reply

    Admin

    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

    );

     

    • The first parameter is the scope of function. ( Just like the JS context "this" )
    • The second parameter is the callback function which you want to execute after something finished.
    • The third parameter is the number of parameter that should pass to the callback function.
    • The fourth parameter is the parameters of the callback function.

     

  • John Smith 2015/06/17

    Thanks for this tutorial! It really helped me to write my first native Extension for Node.js

    But 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

    Reply

    Admin

    Here is a example about how to execute a callback function on Nodejs addon for the latest version of Nan higher than 2.0.0.
     
    Nodejs Addon

    #include <node.h>
    #include <nan.h>
     
    using namespace node;
    using namespace v8;
     
    NAN_METHOD(runCallback) {
        Nan::HandleScope scope;
        Local<Object> callback_obj = 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(callback_obj, callback_handle, 1, argv);
        info.GetReturnValue().SetUndefined();
    }
     
    void  init (Handle<Object> target){
        Nan::HandleScope scope;
        Nan::SetMethod(target, "runCallback", runCallback);
    }
     
    NODE_MODULE(obj, init)
     
     
    JavaScript

    var native = require('./build/Release/test.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);
    });

回應 (Leave a comment)