Lua 中写 C 扩展库时用到的一些技巧(转)

通常,C 扩展库中 C 代码会有一些数据要放在 lua 状态机中。Lua 提供的方案是放在它的 注册表 中。如文档所言,因为 Lua 的注册表是全局共享的,选择 key 的时候就要千万小心了。整数 key 已经被 reference 系统用掉了,一般我们会采用字符串作 key 。

从 C 中压入字符串的效率不是最高,这是因为外部字符串进入状态机时需要重新 hash 并检查唯一性所致。关于避免直接压入字符串的问题,以前写过一篇 blog 谈过。( btw, 以前那个方法也不是最好的解决方案,不过还是可以作为一个参考的 :)

很容易想到,最方便且能保证唯一性的 key 是一个 light userdata 。这一点,在参考手册以及 Programming in Lua 中都有提到。

我们可以用一个 static 变量的地址作为 key 在注册表做索引保存需要的值。比如如下代码:

static const void *key = 0; lua_pushlightuserdata(L, (void *)&key); lua_pushnumber(L, myNumber); lua_settable(L, LUA_REGISTRYINDEX);

这样就把一个 C 中的数字 myNumber 放在注册表中了。如果以后要读出来当然可以用这样的代码:

lua_pushlightuserdata(L, (void *)&key); lua_gettable(L, LUA_REGISTRYINDEX); myNumber = lua_tonumber(L, -1);

这里用的是一个 static 变量的地址作 key ,所以绝对不会和被的扩展库冲突。但是这样做有一个缺点,就是 key 这个东西以一个全局变量形式出现,不太美观。而且,当我们想用到这个 key 的时候,不是那么容易拿到。要么,你得 extern 出这个 key ;要么你需要以一个单件的形式暴露一个函数拿到这个 key ,效率上或许又打了一些折扣了。

现在就给出一个折中的方案,或许能让绝大多数人满意: 那就是,以一个 light userdata 做 key ,这个 key 本身再和一个唯一字符串关联起来。每次再需要拿到 key 指针的函数中这些写:

static const void *key = 0; if (key==0) { lua_getfield (L, LUA_REGISTRYINDEX, "MyExtensionLibrary"); key=lua_touserdata(L,-1); lua_pop(L,1); if (key==0) { key=(void*)&key; lua_pushlightuserdata(L,key); lua_setfield(L, LUA_REGISTRYINDEX, "MyExtensionLibrary"); } }

这样,我们就可以对 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

Lua 中写 C 扩展库时用到的一些技巧相关推荐

  1. 安装python扩展库时只能使用pip_安装 Python 扩展库时只能使用 pip 工具在线安装,如果安装不成功就没有别的办法了。_学小易找答案...

    [单选题]关于Python中的复数,下列说法错误的是_________________. [填空题]在Python程序中,导入sys模块后,可以通过列表________________访问命令行参数. ...

  2. Lua中的字符串函数库

    Lua解释器对字符串的支持很有限.一个程序可以创建字符串并连接字符串,但不能截取子串,检查字符串的大小,检测字符串的内容.在Lua中操纵字符串的功能基本来自于string库. 字符串库中的一些函数是非 ...

  3. Qt中调用OpenCV函数库时Crashed问题的解决。

    这几天想在虚拟机上搭建Ubuntu的开发环境,包括了Matlab和QT(C++)的开发工具安装等,同时由于做图像处理,所以还必须要安装FFMPEG和OpenCV库.下面就讲讲我们安装时出现qt中调用O ...

  4. Python中使用pip安装库时提示:远程主机强迫关闭了一个现有的连接

    场景 在cmd中使用pip install moviepy时,需要安装一些依赖库,很长时间后提示: 远程主机中断了一个现有的连接. 原因是默认镜像源下载过慢,将其修改为国内或者设置安装时的源. 这里以 ...

  5. Python中使用pip安装库时指定镜像源为豆瓣镜像源

    场景 在使用pip进行安装库时,使用默认的库会很慢,甚至有时会出现远程主机中断了一个现有连接. 怎样在使用pip install 时指定镜像源为豆瓣镜像源. 实现 pip install moviep ...

  6. SDL教程4——在VS2010中设置SDL扩展库

    前几节我们了解到,SDL基本库只能加载普通的BMP图像,如果我们还想加载其它格式的图片,我们就需要用到SDL的扩展库,它可以帮助我们加载BMP, PNM, XPM, LBM, PCX, GIF, JP ...

  7. 【cocos2d-x】Lua中的table函数库

    一部分的table函数只对其数组部分产生影响, 而另一部分则对整个table均产生影响. 下面会分开说明. table.concat(table, sep,  start, end) concat是c ...

  8. 安装python扩展库时只能使用pip_使用pip安装Python扩展库的方法

    本文节选自作者的<Python编程基础及应用>视频教程.Python编程基础及应用_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​www.bilibili.com 使用pip进行安装 ...

  9. 在C#中写pock pc 2003时,怎样控制发出声音

    在普通的程序中可以,但在PPC中提示无法找到PInvoke DLL "wimm.dll"这个标准的mci我已经在项目中用的,不会提示找不到的. 我的用在window mobile ...

最新文章

  1. python 字符串拼接_面试官让用 3 种 python 方法实现字符串拼接 ?对不起我有8种……...
  2. ubuntu14.04,安装JDK1.8(JAVA程序需要的开发、运行环境)
  3. 用java实现二分搜索算法分析
  4. Linux系统中df与du命令查看分区大小
  5. 代做html网页多少钱,代做排名网站有吗,做排名帮你实现财富自由
  6. LetCode-算法-整数反转
  7. http header 设置编码_【译】http.client
  8. python机器学习应用mooc_(1)KNN
  9. 福利福利!20行代码教大家抓取斗鱼美女主播封面
  10. java 单例模式(饿汉模式和懒汉模式)
  11. Linux的capability深入分析
  12. 【P5385】【Cnoi2019】须臾幻境(LCT)
  13. 计算机打字测速,电脑的打字测速软件分享
  14. 百度直达号,一场自high的喜剧
  15. 软件工程课程第二次任务——需求分析与原型设计
  16. 射频识别系统及WMS仓库管理系统功能介绍
  17. 朱光潜给青年的十二封信 之 谈读书
  18. linux vim 编辑 保存 退出
  19. 小米 无线 linux 鼠标,实测小米便携式鼠标2:小巧精致 支持无线双模连接
  20. java xmap_XML和Java Bean的互相转换攻略 【XMAP】

热门文章

  1. linux 时区异常修正
  2. IEEE 物联网相关的标准
  3. NSNumber的使用
  4. 3.9-分区表fstab
  5. visualvm连接服务器jvm进行监控
  6. Sitemap Celebration(使用嵌套列表的树形导航)
  7. Linux 命令(47)—— file 命令
  8. C++ 引用计数技术简介(1)
  9. C++ 构造函数体内赋值与初始化列表的区别
  10. 【转】VC++计算当前时间点间隔N天的时间(不使用CTimeSpan类)