Search code examples
c++node.jsv8prototypal-inheritance

Implementing inheritance in node.js bindings


I am writing Node.js bindings around a C++ library. I can identify key objects in the C++ library that I can expose as classes to the Node.js (i.e. derivatives of ObjectWrap). I can also see an inheritance relationship between these objects.

How can I expose ClassA,ClassB,ClassC as node.js classes (derivatives of ObjectWrap) and manipulate their prototypes (in v8 C++ code) so that ClassB and ClassC are derivates of ClassA?


Solution

  • This can be done using v8::FunctionTemplate's Inherit method. It's explained here. Here's a working example.

    C++ code:

    #include <v8.h>
    #include <node.h>
    
    using namespace node;
    using namespace v8;
    
    class BaseClass : ObjectWrap
    {
    public:
      static Persistent<FunctionTemplate> s_ct;
      static void Init(v8::Handle<Object> target)
      {
        Local<FunctionTemplate> t = FunctionTemplate::New(New);
    
        s_ct = Persistent<FunctionTemplate>::New(t);
        s_ct->InstanceTemplate()->SetInternalFieldCount(1);
        s_ct->SetClassName(String::NewSymbol("BaseClass"));
    
        NODE_SET_PROTOTYPE_METHOD(s_ct, "getName", getName);
    
        target->Set(String::NewSymbol("BaseClass"), s_ct->GetFunction());
      }
    
      BaseClass(){}    
      ~BaseClass(){}
    
      static v8::Handle<Value> New(const Arguments& args)
      {
        HandleScope scope;
        return args.This();
      }
    
      static v8::Handle<Value> getName(const Arguments& args)
      {
        HandleScope scope;
        return scope.Close(String::New("Base"));
      }
    };
    
    Persistent<FunctionTemplate> BaseClass::s_ct;
    
    class DerivedClass : ObjectWrap
    {
    public:
      static Persistent<FunctionTemplate> s_ct;
      static void Init(v8::Handle<Object> target)
      {
        Local<FunctionTemplate> t = FunctionTemplate::New(New);
    
        s_ct = Persistent<FunctionTemplate>::New(t);
    
        // XXX Inherit from BaseClass
        s_ct->Inherit(BaseClass::s_ct); 
    
        s_ct->InstanceTemplate()->SetInternalFieldCount(1);
        s_ct->SetClassName(String::NewSymbol("DerivedClass"));
    
        NODE_SET_PROTOTYPE_METHOD(s_ct, "getAge", getAge);
    
        target->Set(String::NewSymbol("DerivedClass"), s_ct->GetFunction());
      }
    
      DerivedClass() {}
      ~DerivedClass() {}
    
      static v8::Handle<Value> New(const Arguments& args) {
        HandleScope scope;
        return args.This();
      }
    
      static v8::Handle<Value> getAge(const Arguments& args)
      {
        HandleScope scope;
        return scope.Close(Number::New(42));
      }
    };
    
    Persistent<FunctionTemplate> DerivedClass::s_ct;
    
    extern "C" {
      static void init (v8::Handle<Object> target)
      {
        BaseClass::Init(target);
        DerivedClass::Init(target);
      }
    
      NODE_MODULE(mymodule, init);
    }
    

    Put it in standard node-gyp configuration and you can test it with following javascript

    var mymodule = require('./build/Release/mymodule');
    var baseObject = new mymodule.BaseClass();
    var dervObject = new mymodule.DerivedClass();
    
    console.log(baseObject.getName());
    console.log(dervObject.getName(), dervObject.getAge());