欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
關(guān)于V8 JavaScript Engine的使用方法研究(二)
轉載地址:http://blog.chinaunix.net/space.php?uid=8272118&do=blog&id=2033360

一、寫(xiě)在前面的話(huà)

上回寫(xiě)到了關(guān)于如何在c++的代碼中嵌入v8引擎,實(shí)現javascript腳本中調用c++的函數以及從c++函數中調用javascript腳本中定義的函數(這其實(shí)就是設計模式中所謂的reflection,反射機制的具體實(shí)現)。通過(guò)了了幾行代碼就可以實(shí)現如此復雜的反射機制,由此可見(jiàn)v8引擎帶來(lái)了很大的便利性。但是上回偶只探索了一下實(shí)現c++與javascript腳本之間的函數相互調用,那么如何通過(guò)javascript調用c++的對象呢?按理說(shuō),javascript與c++的對象一定是應該可以互相調用的,但在實(shí)際的應用過(guò)程中,偶發(fā)現通過(guò)c++去調用javascript中定義的對象,似乎用得不那么多(主要是效率問(wèn)題),而從javascript調用c++中已經(jīng)定義好的對象,則是非常方便的,可以利用c++實(shí)現一些高效率的對象,然后由javascript進(jìn)行調用,從而兼顧了效率與靈活性?xún)蓚€(gè)方面。

對于c++代碼中如何為javascript導出一個(gè)對象供其調用,偶在網(wǎng)上google了一下這個(gè)問(wèn)題。比較有名的解釋就是另外一個(gè)叫做cproxyv8的開(kāi)源項目。該項目通過(guò)幾個(gè)代理類(lèi)可以很方便地將c++中的類(lèi)導出到j(luò )avascript空間中,例如,如果開(kāi)發(fā)者導出了一個(gè)叫做CPoint的類(lèi),那么在javascript腳本中可以這樣去使用:
var pt=new CPoint();
看上去貌似非常方便,但是偶查了一下cproxy的known issues,里面明確說(shuō)明了通過(guò)cproxyv8導出的c++類(lèi)是存在“內部釋放”(native disposed)的問(wèn)題的。
例如,如果偶這樣去編寫(xiě)javascipt腳本:
r = new Rect() ;
pt = r.topleft() ;
pt.x = 3 ;
pt.y = 4 ;

// 隔一大段代碼。。。

pt.x = 0 ;  // 注意,這里或許就會(huì )出問(wèn)題
py.y = 10 ;

這一段腳本,看上去是沒(méi)有問(wèn)題的,r是Rect類(lèi)的對象(Rect類(lèi)是從c++中通過(guò)cproxyv8導出到j(luò )avascript中的一個(gè)類(lèi)),那么當調用r.topleft()的時(shí)候,就會(huì )返回一個(gè)Point對象,該對象是r對象的組成部分(Rect類(lèi)是由topleft和rightbottom兩個(gè)Point對象組合而成的),然后,偶們就可以使用pt對象對topleft的x,y坐標進(jìn)行操作。

但是現在問(wèn)題出現了,如果在后續javascript腳本中沒(méi)有任何代碼引用到r對象,那么對于v8引擎來(lái)說(shuō),這個(gè)r對象是可以釋放掉的,一旦r被釋放掉了,那么其內部的topleft和rightbottom兩個(gè)Point對象自然也會(huì )被釋放掉,如此以來(lái),pt變量引用的r.topleft()對象也被釋放掉了。此時(shí),如果再對pt進(jìn)行存取操作,就會(huì )引起錯誤和異常。這也就是所謂的“內部釋放”問(wèn)題了。當然,如果這里的Rect類(lèi)是在javascript腳本中定義的話(huà),則不會(huì )出現這種問(wèn)題。

偶個(gè)人以為,如果真的需要通過(guò)javascript操作和使用c++的對象,還是直接使用全局對象比較好。由于全局對象在javascript腳本執行的整個(gè)生命周期內都是可以訪(fǎng)問(wèn)的,因此,不會(huì )出現上述的內部釋放問(wèn)題。另外,對于cproxyv8,或許是偶鉆研得不夠深入,在最新版本的v8引擎上似乎不可以工作,還有就是一旦使用過(guò)程中出現了問(wèn)題,編譯器會(huì )輸出讓人頭昏眼花的錯誤提示信息,如果不仔細地一條條地追蹤,就會(huì )變得根本不知所云。因此,偶還是決定直接使用v8引擎中提供的類(lèi)和函數來(lái)導出全局的c++對象,而不是通過(guò)代理類(lèi)來(lái)解決問(wèn)題(當然,不可否認cproxyv8確實(shí)提供了極大的便利性!至于用不用這個(gè)cproxyv8,那完全是蘿卜白菜各有所好了)。

二、v8引擎導出c++類(lèi)的方法
1、準備工作
要想為javascript腳本導出一個(gè)全局的c++對象,那至少應該定義一個(gè)c++的類(lèi)吧?!
下面就是偶作實(shí)驗的時(shí)候聲明的一個(gè)c++類(lèi)(偶把它的名字姑且定為CFoo吧,以下是.h文件的定義):
#ifndef _FOO_H_
#define _FOO_H_

#include <v8.h>
#include "canvas.h"

class CFoo {

 public:
  CFoo(CCanvas * canv_ptr) ;
  ~CFoo() ;

 private:
  CCanvas * m_canv_ptr ;  // 這里是保存外部的一個(gè)繪圖對象的指針,把真正的繪圖工作交給它做好了。

 public: // member functions
  inline void SetColor(int r, int g, int b) {
    if(m_canv_ptr) {
      m_canv_ptr->SetDrawColor(r, g, b) ;
    }
  }

  inline void Line(int x1, int y1, int x2, int y2) {
    if(m_canv_ptr) {
      m_canv_ptr->DrawLine(x1, y1, x2, y2) ;
    }
  }

  inline void Commit() {
    if(m_canv_ptr) {
      m_canv_ptr->Commit() ;
    }
  }
 
 
 public: // 下面這些方法必須定義成static類(lèi)型的,用于給javascript腳本調用的。
  static v8::Handle<v8::Value> set_color(const v8::Arguments & args) ;
  static v8::Handle<v8::Value> line(const v8::Arguments & args) ;
  static v8::Handle<v8::Value> commit(const v8::Arguments & args) ;
} ;

#endif

再往下是具體的實(shí)現,里面有一些奇怪的函數,稍后偶再來(lái)講解(以下是.cpp文件):
CFoo::CFoo(CCanvas * canv_ptr):m_canv_ptr(canv_ptr) {}
CFoo::~CFoo() { }

v8::Handle<v8::Value> CFoo::set_color(const v8::Arguments& args) {
  CFoo * foo_ptr =  util_unwrap_obj<CFoo>(args.Holder()) ; // 這里的util_unwrap_obj稍后再給出其定義
  if(args.Length() == 3) {
    foo_ptr->SetColor(args[0]->Int32Value(),
                      args[1]->Int32Value(),
                      args[2]->Int32Value()) ;
  }

  return v8::Undefined() ;
}

v8::Handle<v8::Value> CFoo::line(const v8::Arguments& args) {
  CFoo * foo_ptr =  util_unwrap_obj<CFoo>(args.Holder()) ;
  if(args.Length() == 4) {
    foo_ptr->Line(args[0]->Int32Value(),
                  args[1]->Int32Value(),
                  args[2]->Int32Value(),
                  args[3]->Int32Value()) ;
  }
  return v8::Undefined() ;
}

v8::Handle<v8::Value> CFoo::commit(const v8::Arguments& args) {
  CFoo * foo_ptr =   util_unwrap_obj<CFoo>(args.Holder()) ;
  foo_ptr->Commit() ;
  return v8::Undefined() ;
}

2、創(chuàng )建一個(gè)CFoo全局指針,然后在初始化的時(shí)候創(chuàng )建其對象
static CFoo * g_foo_ptr ;
......

int main(int argc, char * argv[]) {
...
  try {
    g_foo_ptr = new CFoo(g_canv_ptr) ;
  } catch(...) {
    LOG("main, new CFoo object failed!") ;
    delete g_canv_ptr ;
    goto END_MAIN ;
  }
...
  return 0 ;
}

3、初始化javascript執行環(huán)境的時(shí)候,加入該全局對象(看上去似乎很復雜,但仔細看來(lái),實(shí)際上非常簡(jiǎn)單)
static void on_click(const char * js_fname, int x, int y) {
  const int argc = 2 ;
  HandleScope handle_scope ;
  Handle<ObjectTemplate> global_templ ;
  Handle<ObjectTemplate> foo_templ ;
  Handle<External> foo_class_ptr ;
  Handle<Object> foo_class_obj ;
  Handle<Context> exec_context ;
  Handle<String> js_source ;
  Handle<Script> js_compiled ;
  Handle<String> js_func_name ;
  Handle<Value>  js_func_val ;
  Handle<Function> js_func ;
  Handle<Value>  argv[argc] ;
  Handle<Integer> int_x ;
  Handle<Integer> int_y ;

  // 載入javascript腳本代碼,將代碼的文本內容保存在js_source對象中。
  js_source = load_js(js_fname) ;

  // 創(chuàng )建全局的對象模板(這個(gè)模板用于動(dòng)態(tài)創(chuàng )建對象的)
  global_templ = = ObjectTemplate::New() ;

  // 注冊全局函數,這些操作在偶的第一篇文章中已經(jīng)講解的非常清晰了。
  global_templ->Set(String::New("set_draw_color"),
                    FunctionTemplate::New(set_draw_color)) ;

  global_templ->Set(String::New("draw_line"),
                    FunctionTemplate::New(draw_line)) ;

  global_templ->Set(String::New("commit"),
                    FunctionTemplate::New(commit)) ;

  global_templ->Set(String::New("clear"),
                    FunctionTemplate::New(clear)) ;

  global_templ->Set(String::New("draw_bmp"),
                    FunctionTemplate::New(draw_bmp)) ;

  // 創(chuàng )建運行環(huán)境,創(chuàng )建的時(shí)候要傳入global_templ初始化預定義的全局函數環(huán)境
  exec_context = Context::New(NULL, global_templ) ;

  // 設置運行環(huán)境的有效范圍,如果context_scope被析構了,那也代表著(zhù)exec_context也被釋放掉了
  Context::Scope context_scope(exec_context) ;

  // 注意,從這一行開(kāi)始,就已經(jīng)準備往javascript運行環(huán)境中建立全局對象了
  // 這里的ObjectTemplate是用于在運行時(shí)創(chuàng )建對象的(runtime object creator)
  foo_templ = ObjectTemplate::New() ;

  // 此句是設定使用該模板創(chuàng )建的對象有1個(gè)內部的數據區(internal field)
  foo_templ->SetInternalFieldCount(1) ;

  // 上面的代碼是用于創(chuàng )建一個(gè)對象模板的,下面這一行則是使用這個(gè)剛剛創(chuàng )建成功的對象模板創(chuàng )建出一個(gè)對象
  foo_class_obj = foo_templ->NewInstance() ;
 
  // 下面,我們就創(chuàng )建一個(gè)外部對象(External),這個(gè)對象其實(shí)就是在c++對象的外面包上一層皮
  //(可以把Externl類(lèi)理解為一層包袱皮,把c++空間中的對象g_foo_ptr包裹在里面)
  foo_class_ptr = External::New(static_cast<CFoo *>(g_foo_ptr)) ;

  // 定義模板的時(shí)候已經(jīng)明確定義了由該模板創(chuàng )建出來(lái)的每一個(gè)對象內部一定有1個(gè)數據區(field)
  // 現在我們就把包裹好的c++對象放到當前對象的內部數據區里面去
  foo_class_obj->SetInternalField(0, foo_class_ptr) ;

  // 為當前對象設置其對外函數接口
  foo_class_obj->Set(String::New("set_draw_color"),
                     FunctionTemplate::New(CFoo::set_color)->GetFunction()) ;

  foo_class_obj->Set(String::New("draw_line"),
                     FunctionTemplate::New(CFoo::line)->GetFunction()) ;

  foo_class_obj->Set(String::New("commit"),
                   FunctionTemplate::New(CFoo::commit)->GetFunction()) ;

 
  // 這一步是最關(guān)鍵的,從當前的javascript執行環(huán)境中獲取Global()全局環(huán)境對象,然后使用Set函數
  // 把剛剛初始化完畢的foo_class_obj對象放到j(luò )avascript的全局執行環(huán)境中去
  exec_context->Global()->Set(String::New("foo"),
                              foo_class_obj,
                              (PropertyAttribute)(v8::ReadOnly)) ;

  // 這里就是常規的操作了,首先編譯javascript腳本
  js_compiled = Script::Compile(js_source) ;
  if(js_compiled.IsEmpty()) {
    LOG("run_js, js_compiled is empty!") ;
    return ;
  }
 
  // 運行腳本
  js_compiled->Run() ;
 
  // 然后下面的代碼是調用javascript腳本中定義的OnClick函數,同時(shí)將click的按鍵事件的x, y坐標傳入OnClick函數
  js_func_name = String::New("OnClick") ;
  js_func_val = exec_context->Global()->Get(js_func_name) ;
  if(!js_func_val->IsFunction()) {
    LOG("on_click, js_func_val->IsFunction check failed!") ;
  } else {
    js_func = Handle<Function>::Cast(js_func_val) ;
    int_x = Integer::New(x) ;
    int_y = Integer::New(y) ;
    argv[0] = int_x ;
    argv[1] = int_y ;
    js_func->Call(exec_context->Global(), argc, argv) ;
  }
}

ok,如此一來(lái),在javascript腳本中就可以不需要任何創(chuàng )建或者初始化,直接使用“foo”對象,以及該對象所導出的函數了。
在javascript腳本中可以這樣寫(xiě):

function OnClick(x, y) {
    var rect_len = 50 ;
    foo.set_draw_color(0x00, 0xff, 0x00) ;
    foo.draw_line(x, y, x+rect_len, y) ;
    foo.draw_line(x+rect_len, y, x+rect_len, y+rect_len) ;
    foo.draw_line(x+rect_len, y+rect_len, x, y+rect_len) ;
    foo.draw_line(x, y+rect_len, x, y) ;
    foo.commit() ;
}

可能有朋友會(huì )問(wèn),這樣看上去似乎很麻煩,每次都要創(chuàng )建那么多東西,初始化那么多的對象,執行完畢以后,下次執行就又要重新初始化這些東西,效率是不是太低了一點(diǎn)?!其實(shí),偶是為了能夠讓代碼盡可能地清晰起見(jiàn)才這樣寫(xiě)的,把所有的初始化以及對象都以Handle<sometype>或Local<sometype>的方式定義的,一旦HandleScope被析構了,這些東西也就消失了。如果在真正的“有用的”代碼中,這里的很多初始化只需要一次就足夠了,大家可以使用Persistent<sometype>來(lái)聲明和初始化一些變量(例如:ObjectTemplate和Context),下次重復使用的時(shí)候直接拿來(lái)用就可以了,不需要反復地初始化。(但是需要注意一點(diǎn),用Persistent::New()聲明的變量,是需要調用Persistent::Dispose()進(jìn)行釋放的,這一點(diǎn)千萬(wàn)不要忘記了。)

4、最后,關(guān)于包袱皮的解開(kāi)問(wèn)題
剛剛偶通過(guò)External給g_foo_ptr打了一個(gè)包袱皮,然后通過(guò)
foo_class_obj->SetInternalField(0, foo_class_ptr) ;
把包裹好的c++對象送到了javascript空間里面的對象中去了。那么,偶在javascript回調的時(shí)候該如何解開(kāi)這個(gè)包袱皮得到原始的c++對象呢?
現在該回過(guò)頭來(lái)看看剛剛關(guān)于foo.cpp的實(shí)現代碼中的一個(gè)遺留問(wèn)題了,以下面這個(gè)函數為例:
v8::Handle<v8::Value> CFoo::set_color(const v8::Arguments& args) {
  CFoo * foo_ptr =  util_unwrap_obj<CFoo>(args.Holder()) ;
  if(args.Length() == 3) {
    foo_ptr->SetColor(args[0]->Int32Value(),
                      args[1]->Int32Value(),
                      args[2]->Int32Value()) ;
  }

  return v8::Undefined() ;
}

這里有一個(gè)很有趣的函數:
util_unwrap_obj<CFoo>(args.Holder())
這個(gè)函數就是用來(lái)把包袱皮解開(kāi)的,其具體定義(參見(jiàn)utils.h文件)如下:
template<class T>
T* util_unwrap_obj(v8::Handle<v8::Object> obj) {
  v8::Handle<v8::External> field = v8::Handle<v8::External>::Cast(obj->GetInternalField(0)) ;
  void* raw_obj_ptr = field->Value() ;
  return static_cast<T*>(raw_obj_ptr);
}

這個(gè)util_unwrap_obj是一個(gè)模板函數,就是用于解包袱皮的,這里args.Holder()得到的就是當前函數的所有者(也就是holder),從剛剛的代碼大家可以看到,這些函數的都是隸屬于foo對象的,因此,這里的args.Holder()函數返回的結果就是foo對象,該對象在定義對象模板的時(shí)候已經(jīng)定義了它將包含一個(gè)內部數據區(internalfield),而且在該對象初始化的時(shí)候,把g_foo_ptr給打上了個(gè)包袱皮,塞到了其內部數據區里面?,F在就可以反其道而行之:
v8::Handle<v8::External> field = v8::Handle<v8::External>::Cast(obj->GetInternalField(0)) ;
這一句,就是把foo對象內部數據區里面的數據給取出來(lái),大家可以看到其返回的值被強制轉換成External對象(也就是打好包袱皮的那個(gè)對象),然后通過(guò):
void* raw_obj_ptr = field->Value() ;
此句就是從包袱皮中把原始數據給取了出來(lái)(此時(shí),只是把g_foo_ptr的指針給取出來(lái)了,而且是void *類(lèi)型)。最后,通過(guò)強制類(lèi)型轉換,偶就得到了原始的g_foo_ptr指針:
return static_cast<T *>(raw_obj_ptr);

因此,偶在實(shí)現set_color這類(lèi)回調函數的時(shí)候就可以直接這樣用了:
v8::Handle<v8::Value> CFoo::set_color(const v8::Arguments& args) {
  // 就是這一句,這個(gè)util_unwrap_obj模板函數解決了解包袱皮的任務(wù),而且,有了這個(gè)指向CFoo對象的指針以后
  // 剩下的事情就一切簡(jiǎn)單了,通過(guò)指針調用CFoo對象的成員函數即可。
  CFoo * foo_ptr =  util_unwrap_obj<CFoo>(args.Holder()) ;
  if(args.Length() == 3) {
    foo_ptr->SetColor(args[0]->Int32Value(),
                      args[1]->Int32Value(),
                      args[2]->Int32Value()) ;
  }

  return v8::Undefined() ;
}

最后,為了方便共同研究和學(xué)習,偶把做實(shí)驗的代碼也給一并發(fā)上來(lái),文章中存在的任何問(wèn)題,歡迎各位高手不吝斧正,謝謝??!

PS:
測試腳本是t1.js,在使用的時(shí)候,可以:./test t1.js,然后用鼠標單擊屏幕即可看到效果。

文件: sdl_t5.tar.gz
大小: 595KB
下載: 下載
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
DOJO試用手記 - Enjoy Lucky Life Eternally - CSDNB...
深入淺出妙用 Javascript 中 apply、call、bind
深入理解PHP原理之對象(一) | 風(fēng)雪之隅
預處理器
潘凱:C 對象布局及多態(tài)實(shí)現的探索(五)
JS的綁定對象this
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久