Lua 中写 C 扩展库时用到的一些技巧
今天方舟组的同事问到我一些 lua 的问题,主要是关于 C 扩展库的。我觉得有些技巧性的东西挺值得跟大家分享一下,那么就写篇 blog 吧。
通常,C 扩展库中 C 代码会有一些数据要放在 lua 状态机中。Lua 提供的方案是放在它的 注册表 中。如文档所言,因为 Lua 的注册表是全局共享的,选择 key 的时候就要千万小心了。整数 key 已经被 reference 系统用掉了,一般我们会采用字符串作 key 。
从 C 中压入字符串的效率不是最高,这是因为外部字符串进入状态机时需要重新 hash 并检查唯一性所致。关于避免直接压入字符串的问题,以前写过一篇 blog 谈过。( btw, 以前那个方法也不是最好的解决方案,不过还是可以作为一个参考的 :)
很容易想到,最方便且能保证唯一性的 key 是一个 light userdata 。这一点,在参考手册以及 Programming in Lua 中都有提到。
我们可以用一个 static 变量的地址作为 key 在注册表做索引保存需要的值。比如如下代码:
这样就把一个 C 中的数字 myNumber 放在注册表中了。如果以后要读出来当然可以用这样的代码:
这里用的是一个 static 变量的地址作 key ,所以绝对不会和被的扩展库冲突。但是这样做有一个缺点,就是 key 这个东西以一个全局变量形式出现,不太美观。而且,当我们想用到这个 key 的时候,不是那么容易拿到。要么,你得 extern 出这个 key ;要么你需要以一个单件的形式暴露一个函数拿到这个 key ,效率上或许又打了一些折扣了。
现在就给出一个折中的方案,或许能让绝大多数人满意: 那就是,以一个 light userdata 做 key ,这个 key 本身再和一个唯一字符串关联起来。每次再需要拿到 key 指针的函数中这些写:
这样,我们就可以对 key 做一次惰性初始化了。以上只是示例,初始化部分可以做成一个函数和宏方便重复调用。
顺便再提一个容易被忽略的小技巧: 我们通常用 full userdata 来实现复杂的 C 结构,或是 C++ 中的对象,把它们用于 Lua 中。经常我们需要把 Lua 中的一堆数据关联在这个 userdata 上。为了让 Lua 的 gc 过程可以正确的工作,我们需要做一些额外的工作。
最下策的解决方案是,在 userdata 中保存一个相关的 Lua 表的 reference ,然后给 userdata 加上一个元表,实现一个 gc 的元方法。在 gc 事件发生时,unref 掉这个 Lua 表。这个方案最糟糕的地方在于,实现过于繁琐。而且给 gc 过程带来许多额外的执行开销。
中策是在注册表中保留一个映射表,并把它设置为 weak table 。每次 userdata 创建出来,就以 userdata 为 key 在这个影射表中保存相关联的 Lua 数据。那么这样,垃圾回收的过程就可以由 Lua 本身自行维护了。其不方便之处在于,每次 C 函数想访问 userdata 相关的 Lua 数据时,需要先拿到注册表中的这个映射表(这个操作或许用的到这篇 blog 前半部分提到的东西)。
其实我们有一个上策,Lua 设计的时候就考虑到了。那就是 full userdata 可以拥有一个独立的环境表。这个东西对 Lua 本身毫无意义。但是却参与 gc 。我们只需要用 lua_getfenv
和 lua_setfenv
来读写这个表就够了 :D
Comments
Posted by: 卓小豪 | (11) November 21, 2017 02:56 PM
Posted by: microsoft office 2007 | (10) November 20, 2010 11:05 AM
Posted by: cloud | (9) September 23, 2010 03:40 AM
Posted by: struct | (8) November 24, 2006 04:12 PM
Posted by: struct | (7) November 24, 2006 04:09 PM
Posted by: N/A | (6) November 22, 2006 07:20 PM
Posted by: 11ff | (5) November 22, 2006 01:38 PM
Posted by: 11ff | (4) November 21, 2006 06:41 PM
Posted by: 小神 | (3) November 18, 2006 11:50 PM
Posted by: Bogy | (2) November 18, 2006 12:06 AM
Posted by: 一岁就很帅 | (1) November 17, 2006 11:24 PM