Realm is a (primarily) mobile database. We have a storage engine written in C++. My role in Realm is to bridge this C++ engine to other languages, including Node.js. This talk touches on:
- The basics of V8 internals and API
- How to wrap C++ classes, thus enabling you to…
- Write your own extensions
Why Extensions in C++? (3:08)
Demo C++ Classes (4:48)
I have two C++ classes:
Person, which can have a
lastname() and a
birthday() (methods, and a string to print a person); and
Book class (where you store all your persons in, i.e. address book application; methods: add or look up a person, get a person by the area operator, remove them or check the number of persons). The first
birthday() are getters and setters. You can set and get the first name of a person. Very simple classes: if you want to add a person to a book, you use a person object; look up can return a person object (tricky to do in Node).
V8 Concepts (6:35)
Node.js is built upon V8.
Local handles are allocated on the stack; the life-time is scope-based. Persistent handles can live through more than one function call and survive the change of the scope.
Get more development news like this
Functions, in most programming languages, can return a value; but in V8 you do not return an object - you set the return value using
GetReturnValue() .Set(). You cannot return a local object with a local handle: a local object is in that scope only, and that is stack allocated - that would be reclaimed by the garbage collector when you return from the function.
Object, …). They are represented as C++ classes in the V8 API. The object is the most generic. A number can be an integer or a double.
Breaking Changes: 0.10 → 0.12 (10:10)
In February 2015, Node.js 0.12 was released: they were upgrading V8 API. I wrote an extension using Node 0.10: after the changes, we could no longer compile it. This talk it about 0.12+. The version numbering scheme of Node has now changed [incremented from 0.12 (beginning of the year) to 5.0 (last week)].
Building Extensions (12:51)
If you want to build an extension, you have to write a number of wrapper classes (e.g. if I have a
person.cpp class, I make a
person_wrap.cpp file). Then, you write a
bindings.gyp file explaining the build process (e.g. the target name, ‘funstuff.cpp’; source code files; list the classes I want to wrap, and the wrapper classes; OS X specific extensions). ‘funstuff.cpp’ is setting up the extension; it calls two functions, called Init. For wrapper classes, there is a method setting up the wrapper class, initializing it, and adding it to the V8 engine: InitAll. There is a macro called Node module, which sets it all up. You then type Node-gyp configure, and build. Next, you want to wrap a class.
Wrapping a Class (16:03)
This is the header file of BookWrap (see video for code). We want to inherit from
ObjectWrap, then is a
node::ObjectWrap. You have to have an Init function, and a new function creating a new object. We have to keep a reference to the object we are going to wrap (Book* m_book). This is one of the persistent handles, and a function:
Adding a Class to V8 (17:39)
Init(). This function template new will call
BookWrap::New:, constructing a new object.
Instantiate an Object (19:45)
If we want to allocate a new object of this type (Book), it calls a new method: it creates the wrapper object and the wrapped object, and adds it to V8 runtime. I have to
GetReturnValue(), and set the return value. I ask
new Book() with this module. I should probably either implement it or throw an exception saying it is not allowed. We have a new object in deviate runtime, and the garbage collector can take care of it. I am asking for the
args.length(); if my constructor needed to take arguments, I could do that, if I need to to set that up in my constructor.
If I have this lookup method that returned (e.g. I have this book, and you could look up a person, by his first name or her first name, and then return that object), I have to be able to take an object in one class and return another object of another type.
Instantiate Objects (25:35)
In my person wrap class I want to create a new person wrap object, using a book only. I set up a call to the constructor (first lines; see video). Then I add the person, and return it. I use an
Indexed Getters and Setters (27:54)
Accessors are useful for known properties. I tried to set up named property handlers, but they do not work anymore. Very often in C++ you have a limited number of getters and setters (only the ones in your C++ class); you only want to wrap them. The accessors are simple to use. You set them up by this SetAccessor: you include it into your Init, where you set up your class.
Function for representing them.
Throwing Exceptions (34:07)
Catching Exceptions (36:10)
TryCatch. After a function call, you can ask, was a Exception thrown?:
If you want to bridge between Node.js 0.10 and 0.12+, there is a native abstraction for Node, called NAN, which tries to make few macros. You can use them as same source code for both versions.
Learn More (41:10)
About the content
This talk was delivered live in November 2015 at Øredev. The video was transcribed by Realm and is published here with the permission of the conference organizers.