Archive for the 'Lua' Category

在 UltraEdit 里正确显示 Lua 的块注释

2007-12-27 22:33 | by 2ndboy

  我算得上是 UltraEdit 的铁杆用户啦(虽然是 D 版用户:)),所以写 Lua 程序当然用 UltraEdit 啦!让 UltraEdit 支持 Lua 的语法高亮很简单,去 UE 的官网,下载 Lua 的语法文件集成在自带的语法高亮文件里就可以啦。

  但是 UE 官网上这个语法文件貌似比较旧,是 02 年 8 月发布的,块注释居然用的是“[[”和“]]”:“Block Comment On = [[ Block Comment Off = ]]”。于是手工改成“Block Comment On = –[[ Block Comment Off = –]]”保存,结果发现行注释“–”是可以正确显示的,但是块注释就只能显示快注释开始标记所在的那一行。貌似是 UE 的 bug?估计跟行注释有冲突,因为语法文件里是这么写的“Line Comment = — Block Comment On = –[[ Block Comment Off = –]]”。试了一下把行注释放在块注释后面也不行,开动 Google,结果在 UE 的论坛上找到了答案

  把 Lua 的语法文件第一行改成如下这个样子就可以啦:
/L12″Lua” Block Comment On = –[[ Block Comment Off = –]] Block Comment On Alt = — Escape Char = \ String Chars = “‘ File Extensions = LUA

  查了一下 UE 的帮助文档,上面对“Block Comment On Alt”的描述是这样的“The second set of block comments are in the form “Block Comment On Alt = ” and “Block Comment Off Alt = ” followed by up to five characters each that define the comment designators. If a Block Comment On Alt is defined but the Block Comment Off Alt is not defined the commenting will stop at the end of the line.”。看来块注释指示器有两种,这个办法还真是比较巧妙,但是要熟读 UE 文档才能想的到呀。

  改完以后再看 Lua 代码里的块注释,现在已经可以完美显示啦:D

Update:WordPress 会对英文减号做转换,上面的所有 — 都是两个连写的英文减号。

Lua 函数的缺省参数和可变参数列表

2007-12-8 21:29 | by 2ndboy

  Lua 里面的函数有个“奇怪的”特性——调用时参数个数可以传任意多个。比方说有个函数的定义是 function Foo( a, b ),那么你调用的时候可以写 Foo( “2ndboy”, “test” ),也可以写 Foo( 123 ),还可以写 Foo( 123, 987, “2ndboy” ),都不算错。如果实参个数少于形参个数,那么其余参数的值就是 nil;反之如果多于形参个数,那么多出来的值就被抛弃了。

  想到利用这个特性可以模拟一下缺省参数,Lua 是没有内建对缺省参数的支持的(比如 C++ 里面的 int Foo( int a, int b = 999 ))。如果发现某个参数为 nil,那么就给它赋一个缺省值:

  1. function Foo( a, b )
  2.      if nil == b then
  3.          b = 999
  4.      end
  5.      Bar( a, b )
  6.  end

不过这不是一个好的实现,因为函数的用户也许真的想传一个 nil 过来:)

  Lua 有内建对可变参数列表的支持,语法跟 C/C++ 一样,同样是三个英文句点:function Foo( a, … )。在函数内部,Lua 会且仅会把可变参数部分放在一个叫 arg 的表里面,你可以通过 arg.n 得到可变部分的参数的个数。对于我刚才举的这个例子,如果用 Foo( 666, 999 ) 来调用,则 arg.n == 1,用 arg[1] 可以取到 999 这个值。所以我想,用可变参数列表来模拟缺省参数也许是个更好的主意:

  1. function Foo( a, ... )
  2.      if 1 == arg.n then
  3.          Bar( a, arg[1] )
  4.      else
  5.          Bar( a, 999 )
  6.      end
  7.  end

—–

我现在在用的语法着色插件 CoolCode 还不支持 Lua,试了一下发现把 Lua 代码当作 Ruby 来渲染效果也不错,在官方还未支持之前,先这么用着。

在 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.  }

在 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 就不难了。