Chinaunix首页 | 论坛 | 博客
  • 博客访问: 203860
  • 博文数量: 55
  • 博客积分: 1305
  • 博客等级: 中尉
  • 技术积分: 350
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-11 15:18
文章分类

全部博文(55)

文章存档

2012年(1)

2011年(54)

分类: LINUX

2011-06-03 09:27:16

一、写在前面的话
上回写到了关于如何在c++的代码中嵌入v8引擎,实现javascript脚本中调用c++的函数以及从c++函数中调用javascript脚本中定义的函数(这其实就是设计模式中所谓的reflection,反射机制的具体实现)。通过了了几行代码就可以实现如此复杂的反射机制,由此可见v8引擎带来了很大的便利性。但是上回偶只探索了一下实现c++与javascript脚本之间的函数相互调用,那么如何通过javascript调用c++的对象呢?按理说,javascript与c++的对象一定是应该可以互相调用的,但在实际的应用过程中,偶发现通过c++去调用javascript中定义的对象,似乎用得不那么多(主要是效率问题),而从javascript调用c++中已经定义好的对象,则是非常方便的,可以利用c++实现一些高效率的对象,然后由javascript进行调用,从而兼顾了效率与灵活性两个方面。

对于c++代码中如何为javascript导出一个对象供其调用,偶在网上google了一下这个问题。比较有名的解释就是另外一个叫做cproxyv8的开源项目。该项目通过几个代理类可以很方便地将c++中的类导出到javascript空间中,例如,如果开发者导出了一个叫做CPoint的类,那么在javascript脚本中可以这样去使用:
var pt=new CPoint();
看上去貌似非常方便,但是偶查了一下cproxy的known issues,里面明确说明了通过cproxyv8导出的c++类是存在“内部释放”(native disposed)的问题的。
例如,如果偶这样去编写javascipt脚本:
r = new Rect() ; 
pt = r.topleft() ; 
pt.x = 3 ; 
pt.y = 4 ; 

// 隔一大段代码。。。

pt.x = 0 ;  // 注意,这里或许就会出问题
py.y = 10 ; 

这一段脚本,看上去是没有问题的,r是Rect类的对象(Rect类是从c++中通过cproxyv8导出到javascript中的一个类),那么当调用r.topleft()的时候,就会返回一个Point对象,该对象是r对象的组成部分(Rect类是由topleft和rightbottom两个Point对象组合而成的),然后,偶们就可以使用pt对象对topleft的x,y坐标进行操作。

但是现在问题出现了,如果在后续javascript脚本中没有任何代码引用到r对象,那么对于v8引擎来说,这个r对象是可以释放掉的,一旦r被释放掉了,那么其内部的topleft和rightbottom两个Point对象自然也会被释放掉,如此以来,pt变量引用的r.topleft()对象也被释放掉了。此时,如果再对pt进行存取操作,就会引起错误和异常。这也就是所谓的“内部释放”问题了。当然,如果这里的Rect类是在javascript脚本中定义的话,则不会出现这种问题。

偶个人以为,如果真的需要通过javascript操作和使用c++的对象,还是直接使用全局对象比较好。由于全局对象在javascript脚本执行的整个生命周期内都是可以访问的,因此,不会出现上述的内部释放问题。另外,对于cproxyv8,或许是偶钻研得不够深入,在最新版本的v8引擎上似乎不可以工作,还有就是一旦使用过程中出现了问题,编译器会输出让人头昏眼花的错误提示信息,如果不仔细地一条条地追踪,就会变得根本不知所云。因此,偶还是决定直接使用v8引擎中提供的类和函数来导出全局的c++对象,而不是通过代理类来解决问题(当然,不可否认cproxyv8确实提供了极大的便利性!至于用不用这个cproxyv8,那完全是萝卜白菜各有所好了)。

二、v8引擎导出c++类的方法
1、准备工作
要想为javascript脚本导出一个全局的c++对象,那至少应该定义一个c++的类吧?!
下面就是偶作实验的时候声明的一个c++类(偶把它的名字姑且定为CFoo吧,以下是.h文件的定义):
#ifndef _FOO_H_
#define _FOO_H_

#include
#include "canvas.h"

class CFoo {

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

 private:
  CCanvas * m_canv_ptr ;  // 这里是保存外部的一个绘图对象的指针,把真正的绘图工作交给它做好了。

 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类型的,用于给javascript脚本调用的。
  static v8::Handle set_color(const v8::Arguments & args) ; 
  static v8::Handle line(const v8::Arguments & args) ; 
  static v8::Handle commit(const v8::Arguments & args) ; 
} ; 

#endif

再往下是具体的实现,里面有一些奇怪的函数,稍后偶再来讲解(以下是.cpp文件):
CFoo::CFoo(CCanvas * canv_ptr):m_canv_ptr(canv_ptr) {}
CFoo::~CFoo() { }

v8::Handle CFoo::set_color(const v8::Arguments& args) {
  CFoo * foo_ptr =  util_unwrap_obj(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 CFoo::line(const v8::Arguments& args) {
  CFoo * foo_ptr =  util_unwrap_obj(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 CFoo::commit(const v8::Arguments& args) {
  CFoo * foo_ptr =   util_unwrap_obj(args.Holder()) ; 
  foo_ptr->Commit() ; 
  return v8::Undefined() ; 
}

2、创建一个CFoo全局指针,然后在初始化的时候创建其对象
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执行环境的时候,加入该全局对象(看上去似乎很复杂,但仔细看来,实际上非常简单)
static void on_click(const char * js_fname, int x, int y) {
  const int argc = 2 ; 
  HandleScope handle_scope ; 
  Handle global_templ ; 
  Handle foo_templ ; 
  Handle foo_class_ptr ;
  Handle foo_class_obj ; 
  Handle exec_context ;
  Handle js_source ; 
  Handle 阅读(1623) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~