Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorArtur Adib <arturadib@gmail.com>2012-01-19 19:18:15 +0400
committerBen Noordhuis <info@bnoordhuis.nl>2012-01-19 19:31:36 +0400
commite282c0a0813dd700cc6d9862e27eba448f43b1c9 (patch)
tree2e877b26265893f611afcbbb6896976409d33855 /doc
parentf7b612550a8716b0266a83a86bad060e2688eb1b (diff)
docs: document common add-on design patterns
Diffstat (limited to 'doc')
-rw-r--r--doc/api/addons.markdown547
1 files changed, 542 insertions, 5 deletions
diff --git a/doc/api/addons.markdown b/doc/api/addons.markdown
index 6f99f8be48e..4527b24063b 100644
--- a/doc/api/addons.markdown
+++ b/doc/api/addons.markdown
@@ -6,7 +6,8 @@ knowledge of several libraries:
- V8 JavaScript, a C++ library. Used for interfacing with JavaScript:
creating objects, calling functions, etc. Documented mostly in the
- `v8.h` header file (`deps/v8/include/v8.h` in the Node source tree).
+ `v8.h` header file (`deps/v8/include/v8.h` in the Node source tree),
+ which is also available [online](http://izs.me/v8-docs/main.html).
- [libuv](https://github.com/joyent/libuv), C event loop library. Anytime one
needs to wait for a file descriptor to become readable, wait for a timer, or
@@ -22,12 +23,15 @@ Node statically compiles all its dependencies into the executable. When
compiling your module, you don't need to worry about linking to any of these
libraries.
+
+### Hello world
+
To get started let's make a small Addon which is the C++ equivalent of
the following Javascript code:
exports.hello = function() { return 'world'; };
-To get started we create a file `hello.cc`:
+First we create a file `hello.cc`:
#include <node.h>
#include <v8.h>
@@ -40,7 +44,8 @@ To get started we create a file `hello.cc`:
}
void init(Handle<Object> target) {
- NODE_SET_METHOD(target, "hello", Method);
+ target->Set(String::NewSymbol("hello"),
+ FunctionTemplate::New(Method)->GetFunction());
}
NODE_MODULE(hello, init)
@@ -87,5 +92,537 @@ the recently built module:
console.log(addon.hello()); // 'world'
-For the moment, that is all the documentation on addons. Please see
-<https://github.com/pietern/hiredis-node> for a real example.
+Please see patterns below for further information or
+<https://github.com/pietern/hiredis-node> for an example in production.
+
+
+## Addon patterns
+
+Below are some addon patterns to help you get started. Consult the online
+[v8 reference](http://izs.me/v8-docs/main.html) for help with the various v8
+calls, and v8's [Embedder's Guide](http://code.google.com/apis/v8/embed.html)
+for an explanation of several concepts used such as handles, scopes,
+function templates, etc.
+
+To compile these examples, create the `wscript` file below and run
+`node-waf configure build`:
+
+ srcdir = '.'
+ blddir = 'build'
+ VERSION = '0.0.1'
+
+ def set_options(opt):
+ opt.tool_options('compiler_cxx')
+
+ def configure(conf):
+ conf.check_tool('compiler_cxx')
+ conf.check_tool('node_addon')
+
+ def build(bld):
+ obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
+ obj.target = 'addon'
+ obj.source = ['addon.cc']
+
+In cases where there is more than one `.cc` file, simply add the file name to the
+`obj.source` array, e.g.:
+
+ obj.source = ['addon.cc', 'myexample.cc']
+
+
+#### Function arguments
+
+The following pattern illustrates how to read arguments from JavaScript
+function calls and return a result. This is the main and only needed source
+`addon.cc`:
+
+ #define BUILDING_NODE_EXTENSION
+ #include <node.h>
+
+ using namespace v8;
+
+ Handle<Value> Add(const Arguments& args) {
+ HandleScope scope;
+
+ if (args.Length() < 2) {
+ ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
+ return scope.Close(Undefined());
+ }
+
+ if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
+ ThrowException(Exception::TypeError(String::New("Wrong arguments")));
+ return scope.Close(Undefined());
+ }
+
+ Local<Number> num = Number::New(args[0]->NumberValue() +
+ args[1]->NumberValue());
+ return scope.Close(num);
+ }
+
+ void Init(Handle<Object> target) {
+ target->Set(String::NewSymbol("add"),
+ FunctionTemplate::New(Add)->GetFunction());
+ }
+
+ NODE_MODULE(addon, Init)
+
+You can test it with the following JavaScript snippet:
+
+ var addon = require('./build/Release/addon');
+
+ console.log( 'This should be eight:', addon.add(3,5) );
+
+
+#### Callbacks
+
+You can pass JavaScript functions to a C++ function and execute them from
+there. Here's `addon.cc`:
+
+ #define BUILDING_NODE_EXTENSION
+ #include <node.h>
+
+ using namespace v8;
+
+ Handle<Value> RunCallback(const Arguments& args) {
+ HandleScope scope;
+
+ Local<Function> cb = Local<Function>::Cast(args[0]);
+ const unsigned argc = 1;
+ Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) };
+ cb->Call(Context::GetCurrent()->Global(), argc, argv);
+
+ return scope.Close(Undefined());
+ }
+
+ void Init(Handle<Object> target) {
+ target->Set(String::NewSymbol("runCallback"),
+ FunctionTemplate::New(RunCallback)->GetFunction());
+ }
+
+ NODE_MODULE(addon, Init)
+
+To test it run the following JavaScript snippet:
+
+ var addon = require('./build/Release/addon');
+
+ addon.runCallback(function(msg){
+ console.log(msg); // 'hello world'
+ });
+
+
+#### Object factory
+
+You can create and return new objects from within a C++ function with this
+`addon.cc` pattern, which returns an object with property `msg` that echoes
+the string passed to `createObject()`:
+
+ #define BUILDING_NODE_EXTENSION
+ #include <node.h>
+
+ using namespace v8;
+
+ Handle<Value> CreateObject(const Arguments& args) {
+ HandleScope scope;
+
+ Local<Object> obj = Object::New();
+ obj->Set(String::NewSymbol("msg"), args[0]->ToString());
+
+ return scope.Close(obj);
+ }
+
+ void Init(Handle<Object> target) {
+ target->Set(String::NewSymbol("createObject"),
+ FunctionTemplate::New(CreateObject)->GetFunction());
+ }
+
+ NODE_MODULE(addon, Init)
+
+To test it in JavaScript:
+
+ var addon = require('./build/Release/addon');
+
+ var obj1 = addon.createObject('hello');
+ var obj2 = addon.createObject('world');
+ console.log(obj1.msg+' '+obj2.msg); // 'hello world'
+
+
+#### Function factory
+
+This pattern illustrates how to create and return a JavaScript function that
+wraps a C++ function:
+
+ #define BUILDING_NODE_EXTENSION
+ #include <node.h>
+
+ using namespace v8;
+
+ Handle<Value> MyFunction(const Arguments& args) {
+ HandleScope scope;
+ return scope.Close(String::New("hello world"));
+ }
+
+ Handle<Value> CreateFunction(const Arguments& args) {
+ HandleScope scope;
+
+ Local<FunctionTemplate> tpl = FunctionTemplate::New(MyFunction);
+ Local<Function> fn = tpl->GetFunction();
+ fn->SetName(String::NewSymbol("theFunction")); // omit this to make it anonymous
+
+ return scope.Close(fn);
+ }
+
+ void Init(Handle<Object> target) {
+ target->Set(String::NewSymbol("createFunction"),
+ FunctionTemplate::New(CreateFunction)->GetFunction());
+ }
+
+ NODE_MODULE(addon, Init)
+
+
+To test:
+
+ var addon = require('./build/Release/addon');
+
+ var fn = addon.createFunction();
+ console.log(fn()); // 'hello world'
+
+
+#### Wrapping C++ objects
+
+Here we will create a wrapper for a C++ object/class `MyObject` that can be
+instantiated in JavaScript through the `new` operator. First prepare the main
+module `addon.cc`:
+
+ #define BUILDING_NODE_EXTENSION
+ #include <node.h>
+ #include "myobject.h"
+
+ using namespace v8;
+
+ void InitAll(Handle<Object> target) {
+ MyObject::Init(target);
+ }
+
+ NODE_MODULE(addon, InitAll)
+
+Then in `myobject.h` make your wrapper inherit from `node::ObjectWrap`:
+
+ #ifndef MYOBJECT_H
+ #define MYOBJECT_H
+
+ #include <node.h>
+
+ class MyObject : public node::ObjectWrap {
+ public:
+ 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);
+ double counter_;
+ };
+
+ #endif
+
+And in `myobject.cc` implement the various methods that you want to expose.
+Here we expose the method `plusOne` by adding it to the constructor's
+prototype:
+
+ #define BUILDING_NODE_EXTENSION
+ #include <node.h>
+ #include "myobject.h"
+
+ using namespace v8;
+
+ MyObject::MyObject() {};
+ MyObject::~MyObject() {};
+
+ void MyObject::Init(Handle<Object> target) {
+ // Prepare constructor template
+ Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
+ tpl->SetClassName(String::NewSymbol("MyObject"));
+ tpl->InstanceTemplate()->SetInternalFieldCount(1);
+ // Prototype
+ tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),
+ FunctionTemplate::New(PlusOne)->GetFunction());
+
+ Persistent<Function> constructor = Persistent<Function>::New(tpl->GetFunction());
+ target->Set(String::NewSymbol("MyObject"), constructor);
+ }
+
+ Handle<Value> MyObject::New(const Arguments& args) {
+ HandleScope scope;
+
+ MyObject* obj = new MyObject();
+ obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
+ obj->Wrap(args.This());
+
+ return args.This();
+ }
+
+ Handle<Value> MyObject::PlusOne(const Arguments& args) {
+ HandleScope scope;
+
+ MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());
+ obj->counter_ += 1;
+
+ return scope.Close(Number::New(obj->counter_));
+ }
+
+Test it with:
+
+ var addon = require('./build/Release/addon');
+
+ var obj = new addon.MyObject(10);
+ console.log( obj.plusOne() ); // 11
+ console.log( obj.plusOne() ); // 12
+ console.log( obj.plusOne() ); // 13
+
+
+#### Factory of wrapped objects
+
+This is useful when you want to be able to create native objects without
+explicitly instantiating them with the `new` operator in JavaScript, e.g.
+
+ var obj = addon.createObject();
+ // instead of:
+ // var obj = new addon.Object();
+
+Let's register our `createObject` method in `addon.cc`:
+
+ #define BUILDING_NODE_EXTENSION
+ #include <node.h>
+ #include "myobject.h"
+
+ using namespace v8;
+
+ Handle<Value> CreateObject(const Arguments& args) {
+ HandleScope scope;
+ return scope.Close(MyObject::NewInstance(args));
+ }
+
+ void InitAll(Handle<Object> target) {
+ MyObject::Init();
+
+ target->Set(String::NewSymbol("createObject"),
+ FunctionTemplate::New(CreateObject)->GetFunction());
+ }
+
+ NODE_MODULE(addon, InitAll)
+
+In `myobject.h` we now introduce the static method `NewInstance` that takes
+care of instantiating the object (i.e. it does the job of `new` in JavaScript):
+
+ #define BUILDING_NODE_EXTENSION
+ #ifndef MYOBJECT_H
+ #define MYOBJECT_H
+
+ #include <node.h>
+
+ class MyObject : public node::ObjectWrap {
+ public:
+ static void Init();
+ static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);
+
+ private:
+ MyObject();
+ ~MyObject();
+
+ static v8::Persistent<v8::Function> constructor;
+ static v8::Handle<v8::Value> New(const v8::Arguments& args);
+ static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);
+ double counter_;
+ };
+
+ #endif
+
+The implementation is similar to the above in `myobject.cc`:
+
+ #define BUILDING_NODE_EXTENSION
+ #include <node.h>
+ #include "myobject.h"
+
+ using namespace v8;
+
+ MyObject::MyObject() {};
+ MyObject::~MyObject() {};
+
+ Persistent<Function> MyObject::constructor;
+
+ void MyObject::Init() {
+ // Prepare constructor template
+ Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
+ tpl->SetClassName(String::NewSymbol("MyObject"));
+ tpl->InstanceTemplate()->SetInternalFieldCount(1);
+ // Prototype
+ tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),
+ FunctionTemplate::New(PlusOne)->GetFunction());
+
+ constructor = Persistent<Function>::New(tpl->GetFunction());
+ }
+
+ Handle<Value> MyObject::New(const Arguments& args) {
+ HandleScope scope;
+
+ MyObject* obj = new MyObject();
+ obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
+ obj->Wrap(args.This());
+
+ return args.This();
+ }
+
+ Handle<Value> MyObject::NewInstance(const Arguments& args) {
+ HandleScope scope;
+
+ const unsigned argc = 1;
+ Handle<Value> argv[argc] = { args[0] };
+ Local<Object> instance = constructor->NewInstance(argc, argv);
+
+ return scope.Close(instance);
+ }
+
+ Handle<Value> MyObject::PlusOne(const Arguments& args) {
+ HandleScope scope;
+
+ MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());
+ obj->counter_ += 1;
+
+ return scope.Close(Number::New(obj->counter_));
+ }
+
+Test it with:
+
+ var addon = require('./build/Release/addon');
+
+ var obj = addon.createObject(10);
+ console.log( obj.plusOne() ); // 11
+ console.log( obj.plusOne() ); // 12
+ console.log( obj.plusOne() ); // 13
+
+ var obj2 = addon.createObject(20);
+ console.log( obj2.plusOne() ); // 21
+ console.log( obj2.plusOne() ); // 22
+ console.log( obj2.plusOne() ); // 23
+
+
+#### Passing wrapped objects around
+
+In addition to wrapping and returning C++ objects, you can pass them around
+by unwrapping them with Node's `node::ObjectWrap::Unwrap` helper function.
+In the following `addon.cc` we introduce a function `add()` that can take on two
+`MyObject` objects:
+
+ #define BUILDING_NODE_EXTENSION
+ #include <node.h>
+ #include "myobject.h"
+
+ using namespace v8;
+
+ Handle<Value> CreateObject(const Arguments& args) {
+ HandleScope scope;
+ return scope.Close(MyObject::NewInstance(args));
+ }
+
+ Handle<Value> Add(const Arguments& args) {
+ HandleScope scope;
+
+ MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>(
+ args[0]->ToObject());
+ MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>(
+ args[1]->ToObject());
+
+ double sum = obj1->Val() + obj2->Val();
+ return scope.Close(Number::New(sum));
+ }
+
+ void InitAll(Handle<Object> target) {
+ MyObject::Init();
+
+ target->Set(String::NewSymbol("createObject"),
+ FunctionTemplate::New(CreateObject)->GetFunction());
+
+ target->Set(String::NewSymbol("add"),
+ FunctionTemplate::New(Add)->GetFunction());
+ }
+
+ NODE_MODULE(addon, InitAll)
+
+To make things interesting we introduce a public method in `myobject.h` so we
+can probe private values after unwrapping the object:
+
+ #define BUILDING_NODE_EXTENSION
+ #ifndef MYOBJECT_H
+ #define MYOBJECT_H
+
+ #include <node.h>
+
+ class MyObject : public node::ObjectWrap {
+ public:
+ static void Init();
+ static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);
+ double Val() const { return val_; }
+
+ private:
+ MyObject();
+ ~MyObject();
+
+ static v8::Persistent<v8::Function> constructor;
+ static v8::Handle<v8::Value> New(const v8::Arguments& args);
+ double val_;
+ };
+
+ #endif
+
+The implementation of `myobject.cc` is similar as before:
+
+ #define BUILDING_NODE_EXTENSION
+ #include <node.h>
+ #include "myobject.h"
+
+ using namespace v8;
+
+ MyObject::MyObject() {};
+ MyObject::~MyObject() {};
+
+ Persistent<Function> MyObject::constructor;
+
+ void MyObject::Init() {
+ // Prepare constructor template
+ Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
+ tpl->SetClassName(String::NewSymbol("MyObject"));
+ tpl->InstanceTemplate()->SetInternalFieldCount(1);
+
+ constructor = Persistent<Function>::New(tpl->GetFunction());
+ }
+
+ Handle<Value> MyObject::New(const Arguments& args) {
+ HandleScope scope;
+
+ MyObject* obj = new MyObject();
+ obj->val_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
+ obj->Wrap(args.This());
+
+ return args.This();
+ }
+
+ Handle<Value> MyObject::NewInstance(const Arguments& args) {
+ HandleScope scope;
+
+ const unsigned argc = 1;
+ Handle<Value> argv[argc] = { args[0] };
+ Local<Object> instance = constructor->NewInstance(argc, argv);
+
+ return scope.Close(instance);
+ }
+
+Test it with:
+
+ var addon = require('./build/Release/addon');
+
+ var obj1 = addon.createObject(10);
+ var obj2 = addon.createObject(20);
+ var result = addon.add(obj1, obj2);
+
+ console.log(result); // 30