Archive for November, 2007

在 Lua 里使用 C++ 的 Class

2007-11-25 22:56 | by 2ndboy

  在 Lua 里调用 C/C++ 里面的函数比较简单,需要被 Lua 调用的 C/C++ 函数都长一个德性:

  1. int CFunc( lua_State *pLua );

Lua script 传过来的参数都能从 pLua 里通过 stack 拿到,需要返回给 script 的数据也需要入栈,之后通过 C function 的返回值告诉 script 一共返回了几个值。

把上面这个函数注册一下在 Lua script 里就能用啦:

  1. lua_pushcfunction( pLua, CFunc );
  2.  lua_setglobal( pLua, "CFunc" );

这个操作也可以用 Lua.h 里面定义的 lua_register() 宏一步搞定。

  为了在 Lua 里使用 C++ 的类还真是让我这个 Lua 初学者费了一番周折,记录一把跟大家分享一下。在 Lua 里构造一个 class 需要用到 Lua 的 metatable,这个东西很灵活,基本的语法什么的就不说啦,简单的说 metatable 可以改变“宿主”的一些默认行为。还要用到 userdata,要在 Lua 里保存和使用 C/C++ 的结构啊对象什么的肯定要用到这个东西。

下面就是在 Lua 里构造一个 class 的代码:

  1. Foo = {}
  2.  
  3.  function Foo:new()
  4.      local object = {}
  5.      object.object = NewObject()  -- object.object is a userdata
  6.      setmetatable( object, self )
  7.      self.__index = self
  8.      return object
  9.  end
  10.  
  11.  function Foo:Bar( By2ndboy )
  12.      FooFunc( "Bar", self.object, By2ndboy )
  13.  end

具体的原理可以参考《Programming in Lua》,上面的 NewObject() 和 FooFunc() 都是 C function,其中 NewObject() 用来实际创建 C++ object,FooFunc() 是这个 C++ object 的使用接口。我们来看看 NewObject() 的实现:

  1. typedef struct
  2.  {
  3.      Foo *m_pObject;
  4.  } FooObject;
  5.  
  6.  int
  7.  NewObject( lua_State *pLua )
  8.  {
  9.      FooObject *pObject = (FooObject *)lua_newuserdata( pLua, sizeof( FooObject ) );
  10.      pObject->m_pObject = new Foo;
  11.      return( 1 );
  12.  }

实现很简单,就是用一个 struct 来保存 Foo 对象实例的指针,但是这样没办法实现 Foo 对象的析构,我们来改造一下上述代码:

  1. int
  2.  NewObject( lua_State *pLua )
  3.  {
  4.      FooObject *pObject = (FooObject *)lua_newuserdata( pLua, sizeof( FooObject ) );
  5.      pObject->m_pObject = new Foo;
  6.  
  7.      // Create/Get metatable in registry and push it onto stack
  8.      if( 1 == luaL_newmetatable( pLua, "forGc" ) )
  9.      {
  10.          lua_pushstring( pLua, "__gc" );
  11.          lua_pushcfunction( pLua, DeleteObject );
  12.          lua_settable( pLua, -3 );
  13.      }
  14.  
  15.      // Set metatable for userdata(used by gc)
  16.      lua_setmetatable( pLua, -2 );
  17.  
  18.      return( 1 );
  19.  }
  20.  
  21.  int
  22.  DeleteObject( lua_State *pLua )
  23.  {
  24.      FooObject *pObject = (FooObject *)lua_touserdata( pLua, 1 );
  25.      delete pObject->m_pObject;
  26.      return( 0 );
  27.  }

原理就是通过给 userdata 设置 metatable 来改变 Lua GC 在回收这个 userdata 时的行为,转而调用我们设定的 C function 来删除 Foo object 实例。Lua 里规定 userdata 的 metatable 不能在 script 里面设定,一定要在 C/C++ 里面做。这样改过之后在 Lua 里写下如下代码就会触发 DeleteObject() 这个函数:

  1. function test()
  2.      local a = Foo:new()
  3.  end

最后来看一下 FooFunc() 的实现,也非常简单:

  1. int
  2.  FooFunc( lua_State *pLua )
  3.  {
  4.      string strAction( lua_tostring( pLua, 1 ) );
  5.      FooObject *pObject = (FooObject *)lua_touserdata( pLua, 2 );
  6.      if( strAction == "Bar" )
  7.          pObject->m_pObject->Bar( lua_tonumber( pLua, 3 ) );
  8.  
  9.      return( 0 );
  10.  }

今日杂谈

2007-11-20 21:40 | by 2ndboy

  虽然最近超忙,但是决心花点时间修整一下快要荒芜的 Blog:)

  已经记不得是从什么时候开始,每天都会有潮水般的垃圾留言涌入,删到手软。最近有端时间没上来了,一下堆了近两千条垃圾留言,连管理页面都打不开,没办法只有直接在 PHPMyAdmin 里用 SQL 删留言,顺便也研究了一把 WordPress 的数据表结构。用 SQL 删除还未通过审核的垃圾留言可以这样:

  1. delete from wp_comments where comment_approved='0'

  接着的几天断断续续抽了点时间在网上找防垃圾留言的插件,试了很多都不大满意,有些图片验证插件甚至可以被发帖器突破。最后用了跟橘子一样的 AuthImage,经过两天的试用效果还不错。用周星星同学的话来说,整个世界终于清净啦!

—–

  最近也在挤时间看半个多月前在当当买的《精通正则表达式(Mastering Regular Expressions)》第三版,很不错的书!其实刚毕业的时候就想抽时间把这个威力巨大的葵花宝典(不用自宫的那种:))学一下,但是一来工作中用到的地方实在是少之又少,二来计划经常赶不上变化这就一拖再拖,直到前不久看到余晟译了第三版这才下定决心要看它一看,看书毕竟是把一种技术学扎实的最好方法。

  记得以前在 CSDN 上看过一个叫“帮你免于失业的十大软件技术”,里面列举的技术包括:XML、Web Service、OO、Java/C++/C#/VB.NET、JavaScript、Regular Expressions、Design Patterns、FLash,Linux/Windows 和 SQL,个人感觉把正则表达式列在里面实在是相当的合理。

  昨天正好有个需要解析 HTML 页面内容的小东西要做,凭着最近看书积攒的一小点功力就用 RE 实现了一把,结果发现以前要用一对代码完成的任务被很精简优雅的搞定了,RE 真是个好东西呀,估计以前没接触过正则表达式的人随便翻翻《精通正则表达式》这本书就会被里面短小又强大的例子给镇住:)

—–

  最近也关注了 Android 一把,虽然自己不做手机开发,但是兴趣还是比较大的。我在 SDK 发布的第一时间就下载装起来跑了跑,又看了些在线文档。不过一开始以为 Google 怎么着也得推个 C++ 的开发接口吧,结果下载了发现只有 Java 接口。抛开开发接口不说,这种大开放的平台从我个人看来感觉应该还是超有前途的,只是有点担心被各个公司拿过去修改再发布最后会不会搞出无数个互不兼容的东西来,这个还要骑驴看唱本——走着瞧。

Android 应用程序的生命周期

2007-11-18 11:41 | by 2ndboy

【译自:http://code.google.com/android/intro/lifecycle.html

  大多数情况下,每个 Android 程序都运行在自己的 Linux 进程空间里。当程序代码需要运行的时候进程就被创建出来,在程序已不在被使用或者系统需要回收其所占内存再分配给其它程序使用之前,这个进程一直存在。

  Android 的一个非常重要且不同寻常的特性就在于应用程序进程的的生命期由不得它自己直接控制。应用程序生命期由系统通过对如下几个条件的组合来裁决:系统知道应用程序的一部分正在运行,应用程序正在处理的事情对用户有多重要,系统中的可用内存还有多少。

  对应用程序开发者来说,理解各个组件(特别是Activity,Service 和 IntentReceiver)对应用程序生命期的不同影响是非常重要的。对组件不正确的使用可能会导致应用正在处理重要事务时被系统干掉。

  对进程生命期处理不当的一个常见例子是一个 IntentReceiver 在其 onReceiveIntent() 方法中接收到一个 intent 时启动了一个线程,接着就从 onReceiveIntent() 中返回了。一旦这个函数返回,系统就会认为这个 IntentReceiver 不再活动,因此它的宿主进程也就不会再被用到了(除非其内部的其它应用程序组件处于激活状态)。这时系统可能随时干掉这个进程,终止运行于其中的线程以回收内存。对这个问题的解决方案是在这个进程中启动一个 Service,这样系统就知道这个进程中仍然有些工作在做。

  为了在内存不足时裁决哪个进程应该被干掉,Android 会根据运行在进程中的组件和和组件的状态给进程们排出一个重要级别来。这些重要级别按重要性来排列依次是:

1. 前台进程就是在屏幕顶层,持有 Activity 正在跟用户进行交互的(其 onResume() 已经被调用过),或者是一个正在运行的 IntentReceiver(其 onReceiveIntent() 方法正在执行)进程。这样的进程在系统中非常少,只有当内存非常短缺,短缺到了连这些进程都无法继续运行的时候这类进程才会被干掉。通常此时移动设备已经到达了内存极限,这样做才能让用户界面不停止响应。

2. 可见进程就是持有 Activity,在屏幕上可见但又不是前台进程的那个进程(他的 onPause() 方法被调用过)。……

3. 服务进程……

4. 后台进程……

5. 空进程……

未完……

在 C++ 中遍历 Lua table

2007-11-16 23:13 | by 2ndboy

  最近一个项目里要用到 Lua,于是有机会来学习如何在 C++ 里嵌入 Lua。用编译型语言和脚本语言协作来开发软件是不错的主意,Emacs 的 C + Lisp 就是一个展示这种具有良好扩展性和弹性架构很好的例子。其实我们身边还有很多这样的例子,比如在浏览器里使用 JavaScript,这就是在宿主程序里通过脚本语言调用宿主功能最典型的例子。UltraEdit 的新版里也集成了通过 JavaScript 来使用编辑器功能的特性。

  今天遇到了一个问题,就是通过 Lua C API 来取一个 table 中的数据。貌似 Lua 里没有判断指定 key 是否存在的 API。如果直接 lua_pushstring() 然后再 lua_gettable(),而指定的 key 又不存在的话程序会直接退出,比较的不爽。

  Google 了一把发现一种遍历 table 的方法,这样就可以在遍历的过程中去检查指定的 key 是否存在了。可以用 lua_next() 来遍历 table,但是这个 API 理解起来还是需要点时间的,特此记录一笔。使用 lua_next() 的一般形式是:

  1. // 进行下面步骤前先将 table 压入栈顶
  2.  int nIndex = lua_gettop( pLua )// 取 table 索引值
  3.  lua_pushnil( pLua )// nil 入栈作为初始 key
  4.  while( 0 != lua_next( pLua, nIndex ) )
  5.  {
  6.      // 现在栈顶(-1)是 value,-2 位置是对应的 key
  7.      // 这里可以判断 key 是什么并且对 value 进行各种处理
  8.      lua_pop( pLua, 1 )// 弹出 value,让 key 留在栈顶
  9.  }
  10.  // 现在栈顶是 table

lua_next() 这个函数的工作过程是:
1) 先从栈顶弹出一个 key
2) 从栈指定位置的 table 里取下一对 key-value,先将 key 入栈再将 value 入栈
3) 如果第 2 步成功则返回非 0 值,否则返回 0,并且不向栈中压入任何值

第 2 步中从 table 里取出所谓“下一对 key-value”是相对于第 1 步中弹出的 key 的。table 里第一对 key-value 的前面没有数据,所以先用 lua_pushnil() 压入一个 nil 充当初始 key。

注意开始的时候先用 lua_gettop() 取了一下 table 在栈中的正索引(前面说过了,在进行这个 lua_next() 过程之前先将 table 入栈,所以栈大小就是 table 的正索引),后面的 lua_next() 过程中不断的有元素出入栈,所以使用正索引来定位 table 比较方便。

到了 table 中已经没有 key-value 对时,lua_next() 先弹出最后一个 key,然后发现已经没有数据了会返回 0,while 循环结束。所以这个 lua_next() 过程结束以后 table 就又位于栈顶了。

  刚接触 Lua 不久,所以遇到一个问题罗嗦了大半天,其实 Lua 跟宿主打交道都靠这个 stack,这一点跟 MSIL 很像!掌握了 stack 的使用以后在 C/C++ 里嵌入 Lua 就不难了。