SlideShare a Scribd company logo
1 of 45
Download to read offline
Build the C link
          Nikhil Marathe
Introduction

 • V8 is a powerful, fast JavaScript engine
 • It is self contained and easy to embed
     JS is the new Lua?
 • node.js is a thin wrapper around V8 and evented I/O (libuv)
Follow along
Slides and code
   git clone git://github.com/nikhilm/jsfoo-pune-2012.git
We want to


Use C/C++ libraries in node.js
Exchange data between C++ ⇔ JS
Do asynchronous I/O
Getting started

#include <v8.h>
#include <node.h>

using namespace v8;

extern "C" {
    static void Init(Handle<Object> target) {
    }
    NODE_MODULE(firststep, Init)
}
Build

# firststep/wscript
import Options

def set_options(opt):
    opt.tool_options("compiler_cxx")

def configure(conf):
    conf.check_tool("compiler_cxx")
    conf.check_tool("node_addon")

def build(bld):
    obj = bld.new_task_gen("cxx", "shlib", "node_addon")
    obj.target = "firststep"
    obj.source = "firststep.cc"
Run

$ node-waf configure build
...
'build' finished successfully (0.327s)

$ node

> require('./build/Release/firststep')
{}
Architecture




1          you will need http://bespin.cz/~ondras/html/index.html
Handles

 • Think of them as smart pointers, GCed by V8
 • Also encode scope (Use HandleScope to manage handles)
 • Local - GCed as they go out of scope:

   Local<String> name; // also Handle<...>

 • Persistent - Must be manually disposed:

   Persistent<String> globalVariable;
Injecting primitives
#include <math.h>
#include <v8.h>
#include <node.h>
#include <node_version.h>
using namespace v8;

extern "C" {
    static void Init(Handle<Object> target) {
        target->Set(String::NewSymbol("pi"),
                    Number::New(M_PI));

        NODE_DEFINE_CONSTANT(target, NODE_MINOR_VERSION);

        target->Set(String::New("name"), String::New("Nikhil"));
    }
    NODE_MODULE(primitives, Init)
}
Simple functions




 exports.square = function(n) {
     return n * n;
 }

We want to do this in C++
Simple functions
Registering with V8:

 Handle<Value> Square(const Arguments &args)

 static void Init(Handle<Object> target) {
         HandleScope scope;

          Handle<FunctionTemplate> squareTpl =
              FunctionTemplate::New(Square);

          target->Set(String::New("square"),
                      squareTpl->GetFunction());
     }
Simple functions
Implementation:

 Handle<Value> Square(const Arguments &args) {
     HandleScope scope;

     int a = args[0]->Int32Value();
     int sq = a * a;

     return scope.Close(Integer::New(sq));
 }

explain scope.Close
Templates
FunctionTemplate                        ???

FunctionTemplate::GetFunction           square [Function]

FunctionTemplate::InstanceTemplate()    What `this` would be in 'new square()'

FunctionTemplate::PrototypeTemplate()   square.prototype
Simple objects



 exports.Inventory = function() {
     this.items = 257;
 }

 // later
 var iv = new Inventory();
 console.log(iv.items);

This is the classic object oriented JS style
Simple objects
static void Init(Handle<Object> target) {
        HandleScope scope;

        Handle<FunctionTemplate> inventoryTpl =
            FunctionTemplate::New(Inventory);

        Handle<ObjectTemplate> instance =
            inventoryTpl->InstanceTemplate();

        instance->Set(String::New("items"), Integer::New(257));

        target->Set(String::NewSymbol("Inventory"),
                    inventoryTpl->GetFunction());
    }


Handle<Value> Inventory(const Arguments &args) {
    return args.This();
}
Methods

Inventory.prototype.addStock = function(newStock) {
    this.items += newStock;
}

Inventory.prototype.ship = function(orders) {
    if (this.items < orders)
        throw Exception("Not enough items");

    this.items -= orders
}
Methods
Registering prototype methods

 // operating on inventoryTpl->PrototypeTemplate()
         NODE_SET_PROTOTYPE_METHOD(inventoryTpl,
                                   "addStock",
                                   AddStock);
         NODE_SET_PROTOTYPE_METHOD(inventoryTpl,
                                   "ship",
                                   Ship);

         target->Set(String::NewSymbol("Inventory"),
                     inventoryTpl->GetFunction());
Methods
Accessing object properties
Handle<Value> AddStock(const Arguments &args) {
    HandleScope scope;

    Handle<Object> This = args.This();

    int items = This->Get(String::New("items"))->Uint32Value();

    items += args[0]->Uint32Value();

    This->Set(String::New("items"), Integer::New(items));

    return Undefined();
}
Methods
Throwing an exception
Handle<Value> Ship(const Arguments &args) {
    HandleScope scope;

    Handle<Object> This = args.This();
    int items = This->Get(String::New("items"))->Uint32Value();

    int orders = args[0]->Uint32Value();

    if (items < orders)
        return ThrowException(String::New("Not enough items"));

    This->Set(String::New("items"), Integer::New(items - orders));

    return Undefined();
}
ObjectWrap

 • Associate native C++ objects with JS objects
 • Node specific class which manages garbage collection
 • Stored internally in fields
ObjectWrap

// native C++ class
namespace Library {
class Inventory {
    Inventory();
    void addStock(int);
    int ship(int);
    int getItems();

     int items; // private
};
}
ObjectWrap
Setting internal field count

 Handle<ObjectTemplate> instance =
             inventoryTpl->InstanceTemplate();

          instance->SetInternalFieldCount(1);
ObjectWrap
Wrapping

namespace binding {
class Inventory : public ObjectWrap {
public:
    static Handle<Value> New(const Arguments &args) {
        Inventory *wrapper = new Inventory();
        wrapper->Wrap(args.Holder());
        return args.Holder();
    }
ObjectWrap
Unwrapping
static Handle<Value> Ship(const Arguments &args) {
        // extract
        Inventory *wrapper = Unwrap<Inventory>(args.Holder());

        int orders = args[0]->Uint32Value();
        int result = wrapper->inv->ship(orders);

        if (result == -1)
            return ThrowException(String::New("Not enough items"));

        return Undefined();
    }
Going Async

 • The easiest way is to use uv_queue_work()
 • Every async call requires a set of 3 functions

       1. Set up and invoke uv_queue_work()
      2. Do blocking task (run in separate thread)
       3. Clean up (run in main thread)
 • Use a 'baton' to pass around data

       • uv_request_t is used by libuv
         • But it’s data field is important to store the baton
           itself
 • Slightly cumbersome :(
Going Async
var inv = new (require('./build/Release/async')).Inventory()

inv.reshelve(function() {
    console.log("Reshelving done");
})
console.log("After reshelve in source");
for (var i = 1; i < 5; i++)
    setTimeout(function() {
        console.log("Tick");
    }, i*1000);
Going Async
The native blocking code (method of class Library::Inventory)

 void reshelve() {
         sleep(5);
     }
Going Async
The baton
struct ReshelveBaton {
    uv_work_t request;
    Persistent<Function> callback;
    Inventory *wrapper;
    // any other data that has to be sent to the callback
    // or for async processing.
}
Going Async
JS callback
static Handle<Value> Reshelve(const Arguments &args) {
        Inventory *wrapper = Unwrap<Inventory>(args.Holder());

        Handle<Function> cb = Handle<Function>::Cast(args[0]);

        ReshelveBaton *baton = new ReshelveBaton();
        baton->request.data = baton;
        baton->callback = Persistent<Function>::New(cb);
        baton->wrapper = wrapper;

        uv_queue_work(Loop(), &baton->request,
                      ReshelveAsync, ReshelveAsyncAfter);

        return Undefined();
    }
Going Async
Thread pool function

 static void ReshelveAsync(uv_work_t *req) {
         // This runs in a separate thread
         // NO V8 interaction should be done
         ReshelveBaton *baton =
             static_cast<ReshelveBaton*>(req->data);
         // if you want to modify baton values
         // do synchronous work here
         baton->wrapper->inv->reshelve();
     }
Going Async
Clean up
static void ReshelveAsyncAfter(uv_work_t *req) {
        ReshelveBaton *baton =
            static_cast<ReshelveBaton*>(req->data);

           Handle<Value> argv[] = { Null() }; // no error
           baton->callback->Call(Context::GetCurrent()->Global(),
                                 1, argv);

           baton->callback.Dispose();
           delete baton;
    }
Going Async
Output

After reshelve in source
Tick
Tick
Tick
Tick
Reshelving done
Linking your library
Linking external libs in Waf:
def configure(conf):
  # ...
  # uses pkg-config
  conf.check_cfg(package='<pkg-config name>', args='--cflags --libs',
  uselib_store='ALIAS')

def build(bld):
  # ...
  obj.uselib = 'ALIAS'
Holder vs This

 • args.This() is always the this object passed in to the function
 • args.Holder() runs up the prototype chain to the ‘right’
   object
 • Signatures decide the ‘right’ object, automatically handled
   by NODE_PROTOTYPE_SET_METHOD
 • Always use Holder() to be on the safe side
Things I haven’t covered

 • Accessors

        • Per property accessors
        • Indexed accessors ( object[5] )
        • Named property accessors ( object.property )
 • Function Signatures and HasInstance for type safety
 • Emitting events using new JS only EventEmitter
 • Details of libuv
 • Using V8 on its own
You might want to look at

 • https://github.com/weaver/uuidjs
 • https://github.com/nikhilm/node-taglib
 • https://github.com/pietern/hiredis-node
End notes
Contact:
  • @nikhilcutshort
  • nsm.nikhil@gmail.com



Cover image by Munjal Savla (by-nc-sa)
Extra material below
Calling JS functions

var calljs = require("./build/Release/calljs")

var f = function() {
    console.log("This", this);
    console.log(arguments);
}

calljs.apply(f, { cool: "dude" }, "one", 2, 3.14);
Calling JS functions
Handle<Value> Apply(const Arguments &args) {
    HandleScope scope;

    Handle<Function> func = Handle<Function>::Cast(args[0]);
    Handle<Object> receiver = args[1]->ToObject();

    Handle<Value> *argv = new Handle<Value>[args.Length() - 2];
    for (int i = 2; i < args.Length(); i++)
        argv[i-2] = args[i];

    func->Call(receiver, args.Length()-2, argv);

    delete argv;
    return Undefined();
}
Strings to-and-fro
v8::String -> C string

 Handle<Value> Print(const Arguments &args) {
     HandleScope scope;

     for (int i = 0; i < args.Length(); i++) {
         if (!args[i]->IsString())
             continue;

          // also String::AsciiValue
          String::Utf8Value val(args[i]);
          printf("%s ", *val); // <<<<<<
     }
     return Undefined();
 }
Strings to-and-fro
C string -> v8::String
Handle<Value> Read(const Arguments &args) {
    HandleScope scope;

     char str[1024];
     fgets(str, 1023, stdin);

     Local<String> v8String = String::New(str, strlen(str));
     return scope.Close(v8String);
}

More Related Content

What's hot

버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
민태 김
 

What's hot (20)

Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJS
 
Introduction to Django
Introduction to DjangoIntroduction to Django
Introduction to Django
 
The Basics of MongoDB
The Basics of MongoDBThe Basics of MongoDB
The Basics of MongoDB
 
Defending against Java Deserialization Vulnerabilities
 Defending against Java Deserialization Vulnerabilities Defending against Java Deserialization Vulnerabilities
Defending against Java Deserialization Vulnerabilities
 
Git and Github Session
Git and Github SessionGit and Github Session
Git and Github Session
 
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
버전관리를 들어본적 없는 사람들을 위한 DVCS - Git
 
Ruby on Rails Presentation
Ruby on Rails PresentationRuby on Rails Presentation
Ruby on Rails Presentation
 
Spring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with ThymeleafSpring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
 
MongoDB - Aggregation Pipeline
MongoDB - Aggregation PipelineMongoDB - Aggregation Pipeline
MongoDB - Aggregation Pipeline
 
Flask – Python
Flask – PythonFlask – Python
Flask – Python
 
Distributed computing with spark
Distributed computing with sparkDistributed computing with spark
Distributed computing with spark
 
javascript objects
javascript objectsjavascript objects
javascript objects
 
Redis data modeling examples
Redis data modeling examplesRedis data modeling examples
Redis data modeling examples
 
Hey! There's OCaml in my Rust!
Hey! There's OCaml in my Rust!Hey! There's OCaml in my Rust!
Hey! There's OCaml in my Rust!
 
An introduction to MongoDB
An introduction to MongoDBAn introduction to MongoDB
An introduction to MongoDB
 
Apache Spark Data Source V2 with Wenchen Fan and Gengliang Wang
Apache Spark Data Source V2 with Wenchen Fan and Gengliang WangApache Spark Data Source V2 with Wenchen Fan and Gengliang Wang
Apache Spark Data Source V2 with Wenchen Fan and Gengliang Wang
 
Introduction to react_js
Introduction to react_jsIntroduction to react_js
Introduction to react_js
 
Découverte de Elastic search
Découverte de Elastic searchDécouverte de Elastic search
Découverte de Elastic search
 
Best Practices in Qt Quick/QML - Part II
Best Practices in Qt Quick/QML - Part IIBest Practices in Qt Quick/QML - Part II
Best Practices in Qt Quick/QML - Part II
 
Best Practices in Qt Quick/QML - Part I
Best Practices in Qt Quick/QML - Part IBest Practices in Qt Quick/QML - Part I
Best Practices in Qt Quick/QML - Part I
 

Viewers also liked

Mastering the IoT With JavaScript and C++ - Günter Obiltschnig
Mastering the IoT With JavaScript and C++ - Günter ObiltschnigMastering the IoT With JavaScript and C++ - Günter Obiltschnig
Mastering the IoT With JavaScript and C++ - Günter Obiltschnig
WithTheBest
 
Writing Node.js Bindings - General Principles - Gabriel Schulhof
Writing Node.js Bindings - General Principles - Gabriel SchulhofWriting Node.js Bindings - General Principles - Gabriel Schulhof
Writing Node.js Bindings - General Principles - Gabriel Schulhof
WithTheBest
 

Viewers also liked (14)

Node.js/io.js Native C++ Addons
Node.js/io.js Native C++ AddonsNode.js/io.js Native C++ Addons
Node.js/io.js Native C++ Addons
 
Node.js extensions in C++
Node.js extensions in C++Node.js extensions in C++
Node.js extensions in C++
 
Extending Node.js using C++
Extending Node.js using C++Extending Node.js using C++
Extending Node.js using C++
 
node-db: La excusa perfecta para hablar de C++ y Node.js
node-db: La excusa perfecta para hablar de C++ y Node.jsnode-db: La excusa perfecta para hablar de C++ y Node.js
node-db: La excusa perfecta para hablar de C++ y Node.js
 
Unleash your inner console cowboy
Unleash your inner console cowboyUnleash your inner console cowboy
Unleash your inner console cowboy
 
Mastering the IoT With JavaScript and C++ - Günter Obiltschnig
Mastering the IoT With JavaScript and C++ - Günter ObiltschnigMastering the IoT With JavaScript and C++ - Günter Obiltschnig
Mastering the IoT With JavaScript and C++ - Günter Obiltschnig
 
Slidecast power point
Slidecast power pointSlidecast power point
Slidecast power point
 
Writing Node.js Bindings - General Principles - Gabriel Schulhof
Writing Node.js Bindings - General Principles - Gabriel SchulhofWriting Node.js Bindings - General Principles - Gabriel Schulhof
Writing Node.js Bindings - General Principles - Gabriel Schulhof
 
Informix - The Ideal Database for IoT
Informix - The Ideal Database for IoTInformix - The Ideal Database for IoT
Informix - The Ideal Database for IoT
 
Preparing for a technical interview
Preparing for a technical interviewPreparing for a technical interview
Preparing for a technical interview
 
How to use Hadoop for operational and transactional purposes by RODRIGO MERI...
 How to use Hadoop for operational and transactional purposes by RODRIGO MERI... How to use Hadoop for operational and transactional purposes by RODRIGO MERI...
How to use Hadoop for operational and transactional purposes by RODRIGO MERI...
 
Node Foundation Membership Overview 20160907
Node Foundation Membership Overview 20160907Node Foundation Membership Overview 20160907
Node Foundation Membership Overview 20160907
 
Functional Programming with Streams in node.js
Functional Programming with Streams in node.jsFunctional Programming with Streams in node.js
Functional Programming with Streams in node.js
 
Linux Profiling at Netflix
Linux Profiling at NetflixLinux Profiling at Netflix
Linux Profiling at Netflix
 

Similar to Writing native bindings to node.js in C++

In Class AssignmetzCST280W13a-1.pdfCST 280 In-Class Pract.docx
In Class AssignmetzCST280W13a-1.pdfCST 280 In-Class Pract.docxIn Class AssignmetzCST280W13a-1.pdfCST 280 In-Class Pract.docx
In Class AssignmetzCST280W13a-1.pdfCST 280 In-Class Pract.docx
bradburgess22840
 
JavaScript (without DOM)
JavaScript (without DOM)JavaScript (without DOM)
JavaScript (without DOM)
Piyush Katariya
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
Kiyotaka Oku
 

Similar to Writing native bindings to node.js in C++ (20)

In Class AssignmetzCST280W13a-1.pdfCST 280 In-Class Pract.docx
In Class AssignmetzCST280W13a-1.pdfCST 280 In-Class Pract.docxIn Class AssignmetzCST280W13a-1.pdfCST 280 In-Class Pract.docx
In Class AssignmetzCST280W13a-1.pdfCST 280 In-Class Pract.docx
 
Javascript Everywhere
Javascript EverywhereJavascript Everywhere
Javascript Everywhere
 
JVM Mechanics: Understanding the JIT's Tricks
JVM Mechanics: Understanding the JIT's TricksJVM Mechanics: Understanding the JIT's Tricks
JVM Mechanics: Understanding the JIT's Tricks
 
The uniform interface is 42
The uniform interface is 42The uniform interface is 42
The uniform interface is 42
 
Day 1
Day 1Day 1
Day 1
 
V8
V8V8
V8
 
삼성 바다 앱개발 실패 노하우 2부
삼성 바다 앱개발 실패 노하우 2부삼성 바다 앱개발 실패 노하우 2부
삼성 바다 앱개발 실패 노하우 2부
 
How to Write Node.js Module
How to Write Node.js ModuleHow to Write Node.js Module
How to Write Node.js Module
 
The Ring programming language version 1.9 book - Part 90 of 210
The Ring programming language version 1.9 book - Part 90 of 210The Ring programming language version 1.9 book - Part 90 of 210
The Ring programming language version 1.9 book - Part 90 of 210
 
Build Widgets
Build WidgetsBuild Widgets
Build Widgets
 
Google App Engine Developer - Day3
Google App Engine Developer - Day3Google App Engine Developer - Day3
Google App Engine Developer - Day3
 
Agile Iphone Development
Agile Iphone DevelopmentAgile Iphone Development
Agile Iphone Development
 
JavaScript (without DOM)
JavaScript (without DOM)JavaScript (without DOM)
JavaScript (without DOM)
 
Expert JavaScript tricks of the masters
Expert JavaScript  tricks of the mastersExpert JavaScript  tricks of the masters
Expert JavaScript tricks of the masters
 
OBJECTS IN Object Oriented Programming .ppt
OBJECTS IN Object Oriented Programming .pptOBJECTS IN Object Oriented Programming .ppt
OBJECTS IN Object Oriented Programming .ppt
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
 
Reactive programming with RxJava
Reactive programming with RxJavaReactive programming with RxJava
Reactive programming with RxJava
 
Reactive programming on Android
Reactive programming on AndroidReactive programming on Android
Reactive programming on Android
 
Java script advance-auroskills (2)
Java script advance-auroskills (2)Java script advance-auroskills (2)
Java script advance-auroskills (2)
 
I os 04
I os 04I os 04
I os 04
 

Recently uploaded

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Recently uploaded (20)

presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 

Writing native bindings to node.js in C++

  • 1. Build the C link Nikhil Marathe
  • 2. Introduction • V8 is a powerful, fast JavaScript engine • It is self contained and easy to embed JS is the new Lua? • node.js is a thin wrapper around V8 and evented I/O (libuv)
  • 3. Follow along Slides and code git clone git://github.com/nikhilm/jsfoo-pune-2012.git
  • 4. We want to Use C/C++ libraries in node.js Exchange data between C++ ⇔ JS Do asynchronous I/O
  • 5. Getting started #include <v8.h> #include <node.h> using namespace v8; extern "C" { static void Init(Handle<Object> target) { } NODE_MODULE(firststep, Init) }
  • 6. Build # firststep/wscript import Options def set_options(opt): opt.tool_options("compiler_cxx") def configure(conf): conf.check_tool("compiler_cxx") conf.check_tool("node_addon") def build(bld): obj = bld.new_task_gen("cxx", "shlib", "node_addon") obj.target = "firststep" obj.source = "firststep.cc"
  • 7. Run $ node-waf configure build ... 'build' finished successfully (0.327s) $ node > require('./build/Release/firststep') {}
  • 8. Architecture 1 you will need http://bespin.cz/~ondras/html/index.html
  • 9.
  • 10. Handles • Think of them as smart pointers, GCed by V8 • Also encode scope (Use HandleScope to manage handles) • Local - GCed as they go out of scope: Local<String> name; // also Handle<...> • Persistent - Must be manually disposed: Persistent<String> globalVariable;
  • 11. Injecting primitives #include <math.h> #include <v8.h> #include <node.h> #include <node_version.h> using namespace v8; extern "C" { static void Init(Handle<Object> target) { target->Set(String::NewSymbol("pi"), Number::New(M_PI)); NODE_DEFINE_CONSTANT(target, NODE_MINOR_VERSION); target->Set(String::New("name"), String::New("Nikhil")); } NODE_MODULE(primitives, Init) }
  • 12. Simple functions exports.square = function(n) { return n * n; } We want to do this in C++
  • 13. Simple functions Registering with V8: Handle<Value> Square(const Arguments &args) static void Init(Handle<Object> target) { HandleScope scope; Handle<FunctionTemplate> squareTpl = FunctionTemplate::New(Square); target->Set(String::New("square"), squareTpl->GetFunction()); }
  • 14. Simple functions Implementation: Handle<Value> Square(const Arguments &args) { HandleScope scope; int a = args[0]->Int32Value(); int sq = a * a; return scope.Close(Integer::New(sq)); } explain scope.Close
  • 15. Templates FunctionTemplate ??? FunctionTemplate::GetFunction square [Function] FunctionTemplate::InstanceTemplate() What `this` would be in 'new square()' FunctionTemplate::PrototypeTemplate() square.prototype
  • 16. Simple objects exports.Inventory = function() { this.items = 257; } // later var iv = new Inventory(); console.log(iv.items); This is the classic object oriented JS style
  • 17. Simple objects static void Init(Handle<Object> target) { HandleScope scope; Handle<FunctionTemplate> inventoryTpl = FunctionTemplate::New(Inventory); Handle<ObjectTemplate> instance = inventoryTpl->InstanceTemplate(); instance->Set(String::New("items"), Integer::New(257)); target->Set(String::NewSymbol("Inventory"), inventoryTpl->GetFunction()); } Handle<Value> Inventory(const Arguments &args) { return args.This();
  • 18. }
  • 19. Methods Inventory.prototype.addStock = function(newStock) { this.items += newStock; } Inventory.prototype.ship = function(orders) { if (this.items < orders) throw Exception("Not enough items"); this.items -= orders }
  • 20. Methods Registering prototype methods // operating on inventoryTpl->PrototypeTemplate() NODE_SET_PROTOTYPE_METHOD(inventoryTpl, "addStock", AddStock); NODE_SET_PROTOTYPE_METHOD(inventoryTpl, "ship", Ship); target->Set(String::NewSymbol("Inventory"), inventoryTpl->GetFunction());
  • 21. Methods Accessing object properties Handle<Value> AddStock(const Arguments &args) { HandleScope scope; Handle<Object> This = args.This(); int items = This->Get(String::New("items"))->Uint32Value(); items += args[0]->Uint32Value(); This->Set(String::New("items"), Integer::New(items)); return Undefined(); }
  • 22. Methods Throwing an exception Handle<Value> Ship(const Arguments &args) { HandleScope scope; Handle<Object> This = args.This(); int items = This->Get(String::New("items"))->Uint32Value(); int orders = args[0]->Uint32Value(); if (items < orders) return ThrowException(String::New("Not enough items")); This->Set(String::New("items"), Integer::New(items - orders)); return Undefined(); }
  • 23. ObjectWrap • Associate native C++ objects with JS objects • Node specific class which manages garbage collection • Stored internally in fields
  • 24. ObjectWrap // native C++ class namespace Library { class Inventory { Inventory(); void addStock(int); int ship(int); int getItems(); int items; // private }; }
  • 25. ObjectWrap Setting internal field count Handle<ObjectTemplate> instance = inventoryTpl->InstanceTemplate(); instance->SetInternalFieldCount(1);
  • 26. ObjectWrap Wrapping namespace binding { class Inventory : public ObjectWrap { public: static Handle<Value> New(const Arguments &args) { Inventory *wrapper = new Inventory(); wrapper->Wrap(args.Holder()); return args.Holder(); }
  • 27. ObjectWrap Unwrapping static Handle<Value> Ship(const Arguments &args) { // extract Inventory *wrapper = Unwrap<Inventory>(args.Holder()); int orders = args[0]->Uint32Value(); int result = wrapper->inv->ship(orders); if (result == -1) return ThrowException(String::New("Not enough items")); return Undefined(); }
  • 28. Going Async • The easiest way is to use uv_queue_work() • Every async call requires a set of 3 functions 1. Set up and invoke uv_queue_work() 2. Do blocking task (run in separate thread) 3. Clean up (run in main thread) • Use a 'baton' to pass around data • uv_request_t is used by libuv • But it’s data field is important to store the baton itself • Slightly cumbersome :(
  • 29. Going Async var inv = new (require('./build/Release/async')).Inventory() inv.reshelve(function() { console.log("Reshelving done"); }) console.log("After reshelve in source"); for (var i = 1; i < 5; i++) setTimeout(function() { console.log("Tick"); }, i*1000);
  • 30. Going Async The native blocking code (method of class Library::Inventory) void reshelve() { sleep(5); }
  • 31. Going Async The baton struct ReshelveBaton { uv_work_t request; Persistent<Function> callback; Inventory *wrapper; // any other data that has to be sent to the callback // or for async processing. }
  • 32. Going Async JS callback static Handle<Value> Reshelve(const Arguments &args) { Inventory *wrapper = Unwrap<Inventory>(args.Holder()); Handle<Function> cb = Handle<Function>::Cast(args[0]); ReshelveBaton *baton = new ReshelveBaton(); baton->request.data = baton; baton->callback = Persistent<Function>::New(cb); baton->wrapper = wrapper; uv_queue_work(Loop(), &baton->request, ReshelveAsync, ReshelveAsyncAfter); return Undefined(); }
  • 33. Going Async Thread pool function static void ReshelveAsync(uv_work_t *req) { // This runs in a separate thread // NO V8 interaction should be done ReshelveBaton *baton = static_cast<ReshelveBaton*>(req->data); // if you want to modify baton values // do synchronous work here baton->wrapper->inv->reshelve(); }
  • 34. Going Async Clean up static void ReshelveAsyncAfter(uv_work_t *req) { ReshelveBaton *baton = static_cast<ReshelveBaton*>(req->data); Handle<Value> argv[] = { Null() }; // no error baton->callback->Call(Context::GetCurrent()->Global(), 1, argv); baton->callback.Dispose(); delete baton; }
  • 35. Going Async Output After reshelve in source Tick Tick Tick Tick Reshelving done
  • 36. Linking your library Linking external libs in Waf: def configure(conf): # ... # uses pkg-config conf.check_cfg(package='<pkg-config name>', args='--cflags --libs', uselib_store='ALIAS') def build(bld): # ... obj.uselib = 'ALIAS'
  • 37. Holder vs This • args.This() is always the this object passed in to the function • args.Holder() runs up the prototype chain to the ‘right’ object • Signatures decide the ‘right’ object, automatically handled by NODE_PROTOTYPE_SET_METHOD • Always use Holder() to be on the safe side
  • 38. Things I haven’t covered • Accessors • Per property accessors • Indexed accessors ( object[5] ) • Named property accessors ( object.property ) • Function Signatures and HasInstance for type safety • Emitting events using new JS only EventEmitter • Details of libuv • Using V8 on its own
  • 39. You might want to look at • https://github.com/weaver/uuidjs • https://github.com/nikhilm/node-taglib • https://github.com/pietern/hiredis-node
  • 40. End notes Contact: • @nikhilcutshort • nsm.nikhil@gmail.com Cover image by Munjal Savla (by-nc-sa)
  • 42. Calling JS functions var calljs = require("./build/Release/calljs") var f = function() { console.log("This", this); console.log(arguments); } calljs.apply(f, { cool: "dude" }, "one", 2, 3.14);
  • 43. Calling JS functions Handle<Value> Apply(const Arguments &args) { HandleScope scope; Handle<Function> func = Handle<Function>::Cast(args[0]); Handle<Object> receiver = args[1]->ToObject(); Handle<Value> *argv = new Handle<Value>[args.Length() - 2]; for (int i = 2; i < args.Length(); i++) argv[i-2] = args[i]; func->Call(receiver, args.Length()-2, argv); delete argv; return Undefined(); }
  • 44. Strings to-and-fro v8::String -> C string Handle<Value> Print(const Arguments &args) { HandleScope scope; for (int i = 0; i < args.Length(); i++) { if (!args[i]->IsString()) continue; // also String::AsciiValue String::Utf8Value val(args[i]); printf("%s ", *val); // <<<<<< } return Undefined(); }
  • 45. Strings to-and-fro C string -> v8::String Handle<Value> Read(const Arguments &args) { HandleScope scope; char str[1024]; fgets(str, 1023, stdin); Local<String> v8String = String::New(str, strlen(str)); return scope.Close(v8String); }