Let's build our first add-on. In keeping with tradition, this add-on will result in a Node module that will print out Hello World!
. Even though this is a very simple example, it typifies the structure of all further C++ add-ons you will build. This allows you to incrementally experiment with new commands and structures, growing your knowledge in easy to understand steps.
C++ files are typically given a
.cc
extension. To start, we create a hello.cc
file:
#include <node.h> #include <v8.h> using namespace v8; Handle<Value> Method(const Arguments& args) { HandleScope scope; return scope.Close(String::New("Hello World!")); } void init(Handle<Object> target) { target->Set(String::NewSymbol("hello"), FunctionTemplate::New(Method)->GetFunction()); } NODE_MODULE(hello, init)
This example includes the Node and V8 libraries, building our module using the same libraries Node modules build on NODE_MODULE
is simply a macro that greatly simplifies the export of an initialization function, which every add-on should provide. The name of the module itself should be passed as a first argument. The second argument should point to the initializing function.
You are embedding C++ code into the V8 runtime such that JavaScript can be bound into the relevant scope. V8 must scope all the new allocations made in your code, and so you'll need to wrap the code you write, extending V8. To this end you'll see several instances of the Handle<Value>
syntax, wrapping C++ code in the examples that follow. Comparing these wrappers to what will be defined in the initialization function pushed out to NODE_MODULE
should make it clear how Node is being bound to C++ methods via the V8 bridge.
Note
To learn more about how V8 embeds C++ code, visit https://developers.google.com/v8/embed.
Unlike JavaScript, C++ must be compiled prior to executing. This compile step converts human-readable code into optimized bytecode. To compile C++ code such that it can be easily used as a Node add-on, we will need to use node-gyp
, the tool necessary for building out our add-ons. To install it, simply run npm install -g node-gyp
.
The benefit of using node-gyp
(vs. native compilers like gcc
) is that it removes the pain of dealing with the differences between platforms. You can just write code and know that the build process will work. To that end, let's compile our hello.cc
file.
The first step is to create a binding.gyp
file, which will provide the compiler with necessary build information. A basic file will suffice in our case:
{ "targets": [ { "target_name": "hello", "sources": [ "hello.cc" ] } ] }
Note the target_name
of "hello"
, corresponding to the target->Set(String::NewSymbol("hello")
line in our .cc
file, and the linking of our source hello.cc
file.
Note
In cases where more than one source file needs to be compiled, simply add it to the sources
array.
Within the same directory, enter node-gyp configure
into your terminal. You will see information about the build process printed to your terminal. Once completed a new build/
directory will exist, containing various files. Importantly, there will exist a Makefile
. Go ahead and change current directory into build/
and run make
. You will see something like this in your terminal:
CXX(target) Release/obj.target/hello/hello.o SOLINK_MODULE(target) Release/hello.node SOLINK_MODULE(target) Release/hello.node: Finished
The build process is now complete. As described in the preceding output, a Release/
directory has been created containing a compiled hello.node
file. This is the file we will be linking to within our Node code.
Once we've built and compiled our add-on, we can begin using it immediately within our own Node programs. Jump back into the directory containing hello.cc
. Within this directory create the follow Node program:
var addon = require('./build/Release/hello'); console.log(addon.hello());
Run this program, and you will see Hello World!
displayed in your terminal. You are now a C++ programmer!