Node Addon

Node Addon as bridge between javascript and C++

 

#include 

namespace HelloWorldDemo {
  using v8::FunctionCallbackInfo;  //used for passing arguments and returning val 
  using v8::Isolate;               //v8 VM which has memory heap
  using v8::Local;                 //Local is template of handle, Local means handle of ClassType
  using v8::Object;
  using v8::String;
  using v8::Value;
  using v8::Null;
  using v8::Number;
  using v8::Function;
  using v8::Exception;
  using v8::FunctionTemplate;
  
  //hello method
  void hello (const FunctionCallbackInfo& args) {
    
    //get v8 VM
    Isolate* isolate = args.GetIsolate();
    
    //create js string "hello world" in v8 VM heap
    args.GetReturnValue().Set(String::NewFromUtf8(isolate, "hello world."));
  }

  //accumulate method
  void accumulate (const FunctionCallbackInfo& args) {
    Isolate* isolate = args.GetIsolate();
        
    /*throw exception*/
    if (args.Length() < 1) {
      isolate->ThrowException(Exception::TypeError(
        String::NewFromUtf8(isolate, "Arguments Number Error.")
      ));
      return;
    }

    /*throw exception*/
    if (!args[args.Length() - 1]->IsFunction()) {
      isolate->ThrowException(Exception::TypeError(
        String::NewFromUtf8(isolate, "No Callback Error.")
      ));
      return;
    }
    
    //get callback function from js
    Local callback = Local::Cast(args[args.Length() - 1]);
    
    //accumulate
    double sum = 0.0;
    for (int i = 0; i < args.Length() - 1; ++i) {
      /* throw exception if invalid number */
      if (!args[i]->IsNumber()) {
        isolate->ThrowException(Exception::TypeError(
          String::NewFromUtf8(isolate, "Arguments Type Error.")
        ));
        return;
      } else {
        sum += args[i]->NumberValue();
      }
    }
    
    //call callback with accumulated result
    Local num = Number::New(isolate, sum);
    Local argv[1] = { num };
    callback->Call(Null(isolate), 1, argv);
  }
  
  //return obj
  void getPerson (const FunctionCallbackInfo& args) {
    Isolate* isolate = args.GetIsolate();
    
    //create object
    Local obj = Object::New(isolate);
    //set (key,val) to object
    obj->Set(
      String::NewFromUtf8(isolate, "firstname"),
      String::NewFromUtf8(isolate, "Java")
    );
    obj->Set(
      String::NewFromUtf8(isolate, "lastname"),
      String::NewFromUtf8(isolate, "Script")
    );
    //return object to js
    args.GetReturnValue().Set(obj);
  }

  //pass object to C++
  void sayHiTo (const FunctionCallbackInfo& args) {
    Isolate* isolate = args.GetIsolate();
    //get object from js
    Local person = Local::Cast(args[0]);
    //get value from object
    Local fullname = String::Concat(
      person->Get(String::NewFromUtf8(isolate, "firstname"))->ToString(),
      person->Get(String::NewFromUtf8(isolate, "lastname"))->ToString()
    );
    //return value to js
    args.GetReturnValue().Set(String::Concat(
      String::NewFromUtf8(isolate, "Hi, "),fullname
    ));
  }

  //return function 
  void getFunction (const FunctionCallbackInfo& args) {
    Isolate* isolate = args.GetIsolate();
    //create js function
    Local tpl = FunctionTemplate::New(isolate, sayHiTo);
    Local fn = tpl->GetFunction();
    fn->SetName(String::NewFromUtf8(isolate, "sayHiTo"));
    //return function to js
    args.GetReturnValue().Set(fn);
  }


  //initializtion function which is called when module is loaded into NodeJS for first time
  void init (Local exports) {
    /* exports method hello */
    /* same as javascript's module.exports.hello = hello */
    /* NODE_SET_METHOD can have many */
    
    NODE_SET_METHOD(exports, "hello", hello);
    NODE_SET_METHOD(exports, "accumulate", accumulate);
    NODE_SET_METHOD(exports, "getPerson", getPerson);
    NODE_SET_METHOD(exports, "getFunction", getFunction);
  }

  //macro, set initializtion method of module
  NODE_MODULE(NODE_GYP_MODULE_NAME, init)
} 
  
 

 

//build addon
file: binding.gyp
{
   "targets": [

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


command:
node-gyp configure
node-gyp build

Using:

const myaddon = require('./build/Release/myaddon')

console.log('[HelloWorldDemo]' + myaddon.hello())

myaddon.accumulate(1, 3, 4, 7, (sum) => {
  console.log('[FunctionArgumentsAndCallbacksDemo] 1 + 3 + 4 + 7 = ' + sum)
})

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

let someone = myaddon.getPerson()
console.log('[ReturnObjectDemo] ' + someone.firstname + someone.lastname)

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