2012-01-24 15 views
8

Tôi có 2 lớp C++ được hiển thị dưới dạng lớp javascript, VanillaOptionNoomraEngine, cả hai đều được kế thừa từ ObjectWrap.Làm thế nào để kiểm tra loại đúng khi gọi ObjectWrap :: Unwrap trong một add-on Nodejs?

Trong phương pháp sau đây trong NoomraEngine, tôi đang phải nhận một trước "bọc" VanillaOption:

Handle<Value> 
NoomraEngine::Price(const Arguments& args) { 
    HandleScope scope; 
    Local<Object> object = args[0]->ToObject(); // VanillaOption expected in args[0] 

    VanillaOption* equityOption = ObjectWrap::Unwrap<VanillaOption>(object); 

    Local<Number> x = Number::New(this->price(equityOption)); 
    return scope.Close(x); 
} 

Tất cả mọi thứ hoạt động tốt, ngoại trừ rằng nếu tôi vượt qua các loại sai phương pháp, tai nạn nút trong ObjectWrap::Unwrap.

Câu hỏi của tôi là làm cách nào để đảm bảo rằng tôi đã nhận được loại chính xác trong số args[0]?

Trả lời

3

EDIT: một phương pháp tốt hơn so với một động cơ V8 trần dưới đây là sử dụng NanHasInstance (https://github.com/rvagg/nan#api_nan_has_instance)

Trong MyObject::Init:

Local<FunctionTemplate> tpl = NanNew<FunctionTemplate>(New); 
tpl->SetClassName(NanNew<String>("MyObject")); 
... 
NanAssignPersistent(prototype, tpl); 

nơi prototype là một tĩnh Persistent<FunctionTemplate> thành viên của MyObject.

Sử dụng như thế này:

if (NanHasInstance(prototype, handle)) { 
    MyObject* obj = ObjectWrap::Unwrap<MyObject>(handle); 
    ... 
} 

Với sự báo trước rằng đây là đi đầu tiên của tôi tại viết một addon Node, tôi giải quyết vấn đề này chính xác bằng cách kiểm tra các nguyên mẫu của đối tượng với wrapper của riêng tôi xung quanh UnWrap.

Dưới đây là một bản vá để lớp bản demo nhà máy addon cho thấy phương pháp này: https://github.com/petli/node-addon-examples/commit/d3e92cd060a26da2623690718e78f9005db060a8

Nó sẽ chỉ hỗ trợ đối tượng nhà máy tạo ra, chứ không phải những nơi một constructor được tiếp xúc để người dùng có thể kế thừa từ lớp cơ sở. Tuy nhiên, điều đó có thể được khái quát hóa bằng cách đi bộ chuỗi nguyên mẫu.

Nói tóm lại, nó nắm lấy các tham chiếu đến các nguyên mẫu lớp dự kiến ​​trong MyObject::Init:

Local<Object> obj = constructor->NewInstance(); 
prototype = Persistent<Value>::New(obj->GetPrototype()); 

Và sau đó kiểm tra rằng trước khi dereferencing đối tượng:

MyObject* MyObject::CheckedUnWrap(Handle<Object> handle) 
{ 
    if (!handle.IsEmpty() && handle->InternalFieldCount() == 1) { 
    Handle<Value> objproto = handle->GetPrototype(); 
    if (objproto == prototype) { 
     // OK, this is us 
     return ObjectWrap::Unwrap<MyObject>(handle); 
    } 
    } 

    ThrowException(Exception::TypeError(String::New("<this> is not a MyObject"))); 
    return NULL; 
} 

Tất cả các chức năng sau đó sử dụng CheckedUnWrap thay :

Handle<Value> MyObject::PlusOne(const Arguments& args) { 
    HandleScope scope; 

    MyObject* obj = CheckedUnWrap(args.This()); 
    if (obj) { 
    obj->counter_ += 1; 
    return scope.Close(Number::New(obj->counter_)); 
    } 
    else { 
    // Invalid type, an exception has been thrown so return an empty value 
    return Handle<Value>(); 
    } 
} 

Tôi là đồng nghiệp o xem xét thêm một lĩnh vực nội bộ và thiết lập cho một số con trỏ ma thuật, nhưng sau đó mã sẽ phụ thuộc vào đó node::ObjectWrap sẽ không thay đổi cách nó sử dụng các trường nội bộ.

+0

Cám ơn câu trả lời của bạn được cập nhật. Tôi đang xem lại vấn đề này và hóa ra câu trả lời của bạn "tốt hơn" so với câu trả lời được chấp nhận trước đó. Vì vậy, chấp nhận của bạn – BigONotation

3

Cập nhật: Kể từ NanHasInstance đã bị phản đối, giải pháp mới cho câu trả lời này là sử dụng bool FunctionTemplate::HasInstance(Local<Value> object). Hàm này trả về true nếu đối tượng đã cho là một cá thể của mẫu hàm này.

Persistent<Function> Wrapper::constructor; 
Persistent<FunctionTemplate> Wrapper::tpl; 

Sau đó, trong chức năng Wrapper::Init() của bạn, thiết lập các đối tượng dai dẳng công cộng:

Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New); 
// ... 
Wrapper::tpl.Reset(isolate, tpl); 

Bây giờ để unwrap:

Local<FunctionTemplate> wrapper_tpl = Wrapper::tpl.Get(isolate); 
if (!(wrapper_tpl->HasInstance(args[0]))) { 
    isolate->ThrowException(Exception::TypeError(
     String::NewFromUtf8(isolate, "Argument must be a Wrapper object"))); 
    return; 
} 
// Now we are safe to call ObjectWrap::Unwrap