Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

Writing Node.js/Electron native C++ addons for beginners

How Node.js and Electron can be used to build cross platform desktop apps with JavaScript, HTML, CSS ... and C++.

Slides

Slides will be online at
http://www.heindl-solutions.com/

Andreas Heindl Software Solutions


more details at http://www.heindl-solutions.com/

Why JavaScript?

Node.js

JavaScript on the server

Native Node.js Addons

Task: Development of a simple C++ addon

prepare your system

Install Visual Studio 2015 Community Edition

Install Python 2.7

Setup Node.js release version

Setup Node.js debug version

prepare your project

mkdir example1
cd example1
npm init --yes

your code comes here


create binding.gyp


build release version

node-gyp configure

or

build debug version

node-gyp configure --debug --node-dir="C:\nodejs\node610-debug"

example1.cpp

#include <cmath>
#include <nan.h>

static int factorial_impl(int n) {
    int ret = 1; for (int i = 1; i <= n; ++i) { ret *= i; } return ret;
}

NAN_METHOD(factorial) {
//void export_factorial(const Nan::FunctionCallbackInfo<v8::Value>& info) {

    if (info.Length() != 1) {
        Nan::ThrowTypeError("Wrong number of arguments");
        return;
    }

    if (!info[0]->IsNumber()) {
        Nan::ThrowTypeError("Argument should be a number");
        return;
    }

    double arg0 = info[0]->NumberValue();
    v8::Local<v8::Number> num = Nan::New(factorial_impl(static_cast<int>(arg0)));

    info.GetReturnValue().Set(num);
}

void Init(v8::Local<v8::Object> exports) {  
    NAN_EXPORT(exports, factorial);
    //exports->Set(Nan::New("factorial").ToLocalChecked(),
    //    Nan::New<v8::FunctionTemplate>(factorial)->GetFunction());
}

NODE_MODULE(example1, Init)

binding.gyp

{
  "targets": [
    {
      "target_name": "example1",
      "sources": [
        "example1.cpp"
      ],
      "include_dirs": ["<!(node -e \"require('nan')\")"]
    }
  ]
}

Using Visual Studio?

optional: example1.vcxproj.user

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <LocalDebuggerWorkingDirectory>$(ProjectDir)..</LocalDebuggerWorkingDirectory>
    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
    <LocalDebuggerCommand>C:\nodejs\node610\node.exe</LocalDebuggerCommand>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <LocalDebuggerWorkingDirectory>$(ProjectDir)..</LocalDebuggerWorkingDirectory>
    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
    <LocalDebuggerCommand>C:\nodejs\node610-debug\Debug\node.exe</LocalDebuggerCommand>
  </PropertyGroup>
</Project>

Test example code

Run node:

node


In REPL, type:

const example1 = require('./build/Release/example1.node')
example1.factorial(5); // 120

Electron

Electron is a framework for creating native applications with web technologies like JavaScript, HTML, and CSS. It takes care of the hard parts so you can focus on the core of your application.

build example1 for use in Electron

cd example1
node-gyp rebuild --target=1.4.1 --arch=x64 --dist-url=https://atom.io/download/atom-shell

quick test

git clone https://github.com/electron/electron-quick-start myapp
npm start

in Web Developer Console type

const example1 = require('./example1/build/Release/example1')
console.log(example1.factorial(5); // 120

nan/examples/async_pi_estimate/async.cc

/*********************************************************************
 * NAN - Native Abstractions for Node.js
 *
 * Copyright (c) 2016 NAN contributors
 *
 * MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
 ********************************************************************/

#include <nan.h>
#include "pi_est.h"  // NOLINT(build/include)
#include "async.h"  // NOLINT(build/include)

using v8::Function;
using v8::Local;
using v8::Number;
using v8::Value;
using Nan::AsyncQueueWorker;
using Nan::AsyncWorker;
using Nan::Callback;
using Nan::HandleScope;
using Nan::New;
using Nan::Null;
using Nan::To;

class PiWorker : public AsyncWorker {
 public:
  PiWorker(Callback *callback, int points)
    : AsyncWorker(callback), points(points), estimate(0) {}
  ~PiWorker() {}

  // Executed inside the worker-thread.
  // It is not safe to access V8, or V8 data structures
  // here, so everything we need for input and output
  // should go on `this`.
  void Execute () {
    estimate = Estimate(points);
  }

  // Executed when the async work is complete
  // this function will be run inside the main event loop
  // so it is safe to use V8 again
  void HandleOKCallback () {
    HandleScope scope;

    Local<Value> argv[] = {
        Null()
      , New<Number>(estimate)
    };

    callback->Call(2, argv);
  }

 private:
  int points;
  double estimate;
};

// Asynchronous access to the `Estimate()` function
NAN_METHOD(CalculateAsync) {
  int points = To<int>(info[0]).FromJust();
  Callback *callback = new Callback(info[1].As<Function>());

  AsyncQueueWorker(new PiWorker(callback, points));
}

Nan::ObjectWrap


class MyObject : public Nan::ObjectWrap {
 public:
  static NAN_MODULE_INIT(Init) {
    v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
    tpl->SetClassName(Nan::New("MyObject").ToLocalChecked());
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    SetPrototypeMethod(tpl, "getHandle", GetHandle);
    SetPrototypeMethod(tpl, "getValue", GetValue);

    constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked());
    Nan::Set(target, Nan::New("MyObject").ToLocalChecked(),
      Nan::GetFunction(tpl).ToLocalChecked());
  }

 private:
  explicit MyObject(double value = 0) : value_(value) {}
  ~MyObject() {}

  static NAN_METHOD(New) {
    if (info.IsConstructCall()) {
      double value = info[0]->IsUndefined() ? 0 : Nan::To<double>(info[0]).FromJust();
      MyObject *obj = new MyObject(value);
      obj->Wrap(info.This());
      info.GetReturnValue().Set(info.This());
    } else {
      const int argc = 1;
      v8::Local<v8::Value> argv[argc] = {info[0]};
      v8::Local<v8::Function> cons = Nan::New(constructor());
      info.GetReturnValue().Set(cons->NewInstance(argc, argv));
    }
  }

  static NAN_METHOD(GetHandle) {
    MyObject* obj = Nan::ObjectWrap::Unwrap<MyObject>(info.Holder());
    info.GetReturnValue().Set(obj->handle());
  }

  static NAN_METHOD(GetValue) {
    MyObject* obj = Nan::ObjectWrap::Unwrap<MyObject>(info.Holder());
    info.GetReturnValue().Set(obj->value_);
  }

  static inline Nan::Persistent<v8::Function> & constructor() {
    static Nan::Persistent<v8::Function> my_constructor;
    return my_constructor;
  }

  double value_;
};

NODE_MODULE(objectwrapper, MyObject::Init)

To use in Javascript


var objectwrapper = require('bindings')('objectwrapper');

var obj = new objectwrapper.MyObject(5);
console.log('Should be 5: ' + obj.getValue());

Caveats

Links

Writing Node.js/Electron native C++ addons for beginners

How Node.js and Electron can be used to build cross platform desktop apps with JavaScript, HTML, CSS ... and C++.

END

Questions?

Created with impress.js available at https://github.com/impress/impress.js
Presented with Electron available at http://electron.atom.io/
Slides will be online at http://www.heindl-solutions.com/