Node Addon as bridge between javascript and C++
#include <node.h> 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<ClassType> 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<Value>& 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<Value>& 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<Function> callback = Local<Function>::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<Number> num = Number::New(isolate, sum); Local<Value> argv[1] = { num }; callback->Call(Null(isolate), 1, argv); } //return obj void getPerson (const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); //create object Local<Object> 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<Value>& args) { Isolate* isolate = args.GetIsolate(); //get object from js Local<Object> person = Local<Object>::Cast(args[0]); //get value from object Local<String> 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<Value>& args) { Isolate* isolate = args.GetIsolate(); //create js function Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, sayHiTo); Local<Function> 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<Object> 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))