Constructing JS Objects in C++

I was unable to find documentation describing how to instantiate a JavaScript class inside a C++ function and return it to JavaScript. This post describes how to do so, in the context of the Node.js addon documentation examples. I was guided towards this solution by the wonderfully helpful people in the #node.js & #v8 IRC rooms on freenode. Thank you all for your patience in trying to understand my poor explanations.

The source for this example is available on GitHub. I recommend reading both the Node.js addons guide, and Konstantin Käfer’s excellent Node.js / V8 presentation.


The Code

We can accomplish our goal by making modifications to both the MyObject header and implementation files. The modification to the header file is simple: we just want to declare the new method signature and make the v8::Persistent constructor available as a static property, so it may be accessed from within the MyObject methods (in myobject.h):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef MYOBJECT_H
#define MYOBJECT_H
 
#include <node.h>
 
class MyObject : public node::ObjectWrap {
 public:
 
  // This allows us to use the constructor in the implementation's methods
  static v8::Persistent<v8::Function> constructor;
  static void Init(v8::Handle<v8::Object> target);
 
 private:
  MyObject();
  ~MyObject();
 
  static v8::Handle<v8::Value> New(const v8::Arguments& args);
  static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);
 
  // The method that will construct & return the new JavaScript object
  static v8::Handle<v8::Value> CreateNewMyObject(const v8::Arguments& args);
  double counter_;
};
 
#endif

Only one method needs to be added to the implementation (myobject.cc):

1
2
3
4
5
Handle<Value> MyObject::CreateNewMyObject(const v8::Arguments& args) {
  HandleScope scope;
  Handle<Value> myObject = MyObject::constructor->NewInstance();
  return scope.Close(myObject);
}

The key here, for me at least was on line #3. Two points about this line:

  1. Although it seems intuitively correct, we don’t use: MyObject *myObject = new MyObject(). Although this would provide an instance of the C++ class MyObject, it isn’t suitable for returning to JavaScript, as its instance->handle_ won’t be populated (we’ll get malloc errors or a segfault).
  2. The resulting object, somewhat counter-intuitively (for me at least) is of the Handle<Value> type. This means that to perform operations on the underlying MyObject object, one must unwrap it. This can be done with:
    1
    
    MyObject *myObjectInstance = ObjectWrap::Unwrap<MyObject>(myObject);

Compile & Profit

After compilation (use $ node-gyp configure && node-gyp build), one can use the construction method like:

1
2
3
var addon = require('./build/Release/addon');
 
var newObj = obj.makeNewMyObject();

This is a very basic example – I use this methodology to instantiate objects and pass them as parameters to JavaScript callback functions.

Did you struggle with some aspects of learning C++/V8? What aspects were tough for you?

Comments (2) | Trackback