You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
8.0 KiB
263 lines
8.0 KiB
## Object Wrappers |
|
|
|
The `ObjectWrap` class can be used to make wrapped C++ objects and a factory of wrapped objects. |
|
|
|
- <a href="#api_nan_object_wrap"><b><code>Nan::ObjectWrap</code></b></a> |
|
|
|
|
|
<a name="api_nan_object_wrap"></a> |
|
### Nan::ObjectWrap() |
|
|
|
A reimplementation of `node::ObjectWrap` that adds some API not present in older versions of Node. Should be preferred over `node::ObjectWrap` in all cases for consistency. |
|
|
|
Definition: |
|
|
|
```c++ |
|
class ObjectWrap { |
|
public: |
|
ObjectWrap(); |
|
|
|
virtual ~ObjectWrap(); |
|
|
|
template <class T> |
|
static inline T* Unwrap(v8::Local<v8::Object> handle); |
|
|
|
inline v8::Local<v8::Object> handle(); |
|
|
|
inline Nan::Persistent<v8::Object>& persistent(); |
|
|
|
protected: |
|
inline void Wrap(v8::Local<v8::Object> handle); |
|
|
|
inline void MakeWeak(); |
|
|
|
/* Ref() marks the object as being attached to an event loop. |
|
* Refed objects will not be garbage collected, even if |
|
* all references are lost. |
|
*/ |
|
virtual void Ref(); |
|
|
|
/* Unref() marks an object as detached from the event loop. This is its |
|
* default state. When an object with a "weak" reference changes from |
|
* attached to detached state it will be freed. Be careful not to access |
|
* the object after making this call as it might be gone! |
|
* (A "weak reference" means an object that only has a |
|
* persistent handle.) |
|
* |
|
* DO NOT CALL THIS FROM DESTRUCTOR |
|
*/ |
|
virtual void Unref(); |
|
|
|
int refs_; // ro |
|
}; |
|
``` |
|
|
|
See the Node documentation on [Wrapping C++ Objects](https://nodejs.org/api/addons.html#addons_wrapping_c_objects) for more details. |
|
|
|
### This vs. Holder |
|
|
|
When calling `Unwrap`, it is important that the argument is indeed some JavaScript object which got wrapped by a `Wrap` call for this class or any derived class. |
|
The `Signature` installed by [`Nan::SetPrototypeMethod()`](methods.md#api_nan_set_prototype_method) does ensure that `info.Holder()` is just such an instance. |
|
In Node 0.12 and later, `info.This()` will also be of such a type, since otherwise the invocation will get rejected. |
|
However, in Node 0.10 and before it was possible to invoke a method on a JavaScript object which just had the extension type in its prototype chain. |
|
In such a situation, calling `Unwrap` on `info.This()` will likely lead to a failed assertion causing a crash, but could lead to even more serious corruption. |
|
|
|
On the other hand, calling `Unwrap` in an [accessor](methods.md#api_nan_set_accessor) should not use `Holder()` if the accessor is defined on the prototype. |
|
So either define your accessors on the instance template, |
|
or use `This()` after verifying that it is indeed a valid object. |
|
|
|
### Examples |
|
|
|
#### Basic |
|
|
|
```c++ |
|
class MyObject : public Nan::ObjectWrap { |
|
public: |
|
static NAN_MODULE_INIT(Init) { |
|
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New); |
|
tpl->SetClassName(Nan::New("MyObject").ToLocalChecked()); |
|
tpl->InstanceTemplate()->SetInternalFieldCount(1); |
|
|
|
Nan::SetPrototypeMethod(tpl, "getHandle", GetHandle); |
|
Nan::SetPrototypeMethod(tpl, "getValue", GetValue); |
|
|
|
constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked()); |
|
Nan::Set(target, Nan::New("MyObject").ToLocalChecked(), |
|
Nan::GetFunction(tpl).ToLocalChecked()); |
|
} |
|
|
|
private: |
|
explicit MyObject(double value = 0) : value_(value) {} |
|
~MyObject() {} |
|
|
|
static NAN_METHOD(New) { |
|
if (info.IsConstructCall()) { |
|
double value = info[0]->IsUndefined() ? 0 : Nan::To<double>(info[0]).FromJust(); |
|
MyObject *obj = new MyObject(value); |
|
obj->Wrap(info.This()); |
|
info.GetReturnValue().Set(info.This()); |
|
} else { |
|
const int argc = 1; |
|
v8::Local<v8::Value> argv[argc] = {info[0]}; |
|
v8::Local<v8::Function> cons = Nan::New(constructor()); |
|
info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked()); |
|
} |
|
} |
|
|
|
static NAN_METHOD(GetHandle) { |
|
MyObject* obj = Nan::ObjectWrap::Unwrap<MyObject>(info.Holder()); |
|
info.GetReturnValue().Set(obj->handle()); |
|
} |
|
|
|
static NAN_METHOD(GetValue) { |
|
MyObject* obj = Nan::ObjectWrap::Unwrap<MyObject>(info.Holder()); |
|
info.GetReturnValue().Set(obj->value_); |
|
} |
|
|
|
static inline Nan::Persistent<v8::Function> & constructor() { |
|
static Nan::Persistent<v8::Function> my_constructor; |
|
return my_constructor; |
|
} |
|
|
|
double value_; |
|
}; |
|
|
|
NODE_MODULE(objectwrapper, MyObject::Init) |
|
``` |
|
|
|
To use in Javascript: |
|
|
|
```Javascript |
|
var objectwrapper = require('bindings')('objectwrapper'); |
|
|
|
var obj = new objectwrapper.MyObject(5); |
|
console.log('Should be 5: ' + obj.getValue()); |
|
``` |
|
|
|
#### Factory of wrapped objects |
|
|
|
```c++ |
|
class MyFactoryObject : public Nan::ObjectWrap { |
|
public: |
|
static NAN_MODULE_INIT(Init) { |
|
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New); |
|
tpl->InstanceTemplate()->SetInternalFieldCount(1); |
|
|
|
Nan::SetPrototypeMethod(tpl, "getValue", GetValue); |
|
|
|
constructor().Reset(Nan::GetFunction(tpl).ToLocalChecked()); |
|
} |
|
|
|
static NAN_METHOD(NewInstance) { |
|
v8::Local<v8::Function> cons = Nan::New(constructor()); |
|
double value = info[0]->IsNumber() ? Nan::To<double>(info[0]).FromJust() : 0; |
|
const int argc = 1; |
|
v8::Local<v8::Value> argv[1] = {Nan::New(value)}; |
|
info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked()); |
|
} |
|
|
|
// Needed for the next example: |
|
inline double value() const { |
|
return value_; |
|
} |
|
|
|
private: |
|
explicit MyFactoryObject(double value = 0) : value_(value) {} |
|
~MyFactoryObject() {} |
|
|
|
static NAN_METHOD(New) { |
|
if (info.IsConstructCall()) { |
|
double value = info[0]->IsNumber() ? Nan::To<double>(info[0]).FromJust() : 0; |
|
MyFactoryObject * obj = new MyFactoryObject(value); |
|
obj->Wrap(info.This()); |
|
info.GetReturnValue().Set(info.This()); |
|
} else { |
|
const int argc = 1; |
|
v8::Local<v8::Value> argv[argc] = {info[0]}; |
|
v8::Local<v8::Function> cons = Nan::New(constructor()); |
|
info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked()); |
|
} |
|
} |
|
|
|
static NAN_METHOD(GetValue) { |
|
MyFactoryObject* obj = ObjectWrap::Unwrap<MyFactoryObject>(info.Holder()); |
|
info.GetReturnValue().Set(obj->value_); |
|
} |
|
|
|
static inline Nan::Persistent<v8::Function> & constructor() { |
|
static Nan::Persistent<v8::Function> my_constructor; |
|
return my_constructor; |
|
} |
|
|
|
double value_; |
|
}; |
|
|
|
NAN_MODULE_INIT(Init) { |
|
MyFactoryObject::Init(target); |
|
Nan::Set(target, |
|
Nan::New<v8::String>("newFactoryObjectInstance").ToLocalChecked(), |
|
Nan::GetFunction( |
|
Nan::New<v8::FunctionTemplate>(MyFactoryObject::NewInstance)).ToLocalChecked() |
|
); |
|
} |
|
|
|
NODE_MODULE(wrappedobjectfactory, Init) |
|
``` |
|
|
|
To use in Javascript: |
|
|
|
```Javascript |
|
var wrappedobjectfactory = require('bindings')('wrappedobjectfactory'); |
|
|
|
var obj = wrappedobjectfactory.newFactoryObjectInstance(10); |
|
console.log('Should be 10: ' + obj.getValue()); |
|
``` |
|
|
|
#### Passing wrapped objects around |
|
|
|
Use the `MyFactoryObject` class above along with the following: |
|
|
|
```c++ |
|
static NAN_METHOD(Sum) { |
|
Nan::MaybeLocal<v8::Object> maybe1 = Nan::To<v8::Object>(info[0]); |
|
Nan::MaybeLocal<v8::Object> maybe2 = Nan::To<v8::Object>(info[1]); |
|
|
|
// Quick check: |
|
if (maybe1.IsEmpty() || maybe2.IsEmpty()) { |
|
// return value is undefined by default |
|
return; |
|
} |
|
|
|
MyFactoryObject* obj1 = |
|
Nan::ObjectWrap::Unwrap<MyFactoryObject>(maybe1.ToLocalChecked()); |
|
MyFactoryObject* obj2 = |
|
Nan::ObjectWrap::Unwrap<MyFactoryObject>(maybe2.ToLocalChecked()); |
|
|
|
info.GetReturnValue().Set(Nan::New<v8::Number>(obj1->value() + obj2->value())); |
|
} |
|
|
|
NAN_MODULE_INIT(Init) { |
|
MyFactoryObject::Init(target); |
|
Nan::Set(target, |
|
Nan::New<v8::String>("newFactoryObjectInstance").ToLocalChecked(), |
|
Nan::GetFunction( |
|
Nan::New<v8::FunctionTemplate>(MyFactoryObject::NewInstance)).ToLocalChecked() |
|
); |
|
Nan::Set(target, |
|
Nan::New<v8::String>("sum").ToLocalChecked(), |
|
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(Sum)).ToLocalChecked() |
|
); |
|
} |
|
|
|
NODE_MODULE(myaddon, Init) |
|
``` |
|
|
|
To use in Javascript: |
|
|
|
```Javascript |
|
var myaddon = require('bindings')('myaddon'); |
|
|
|
var obj1 = myaddon.newFactoryObjectInstance(5); |
|
var obj2 = myaddon.newFactoryObjectInstance(10); |
|
console.log('sum of object values: ' + myaddon.sum(obj1, obj2)); |
|
```
|
|
|