Lua 中写 C 扩展库时用到的一些技巧
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 扩展库时用到的一些技巧相关推荐
- 安装python扩展库时只能使用pip_安装 Python 扩展库时只能使用 pip 工具在线安装,如果安装不成功就没有别的办法了。_学小易找答案...
[单选题]关于Python中的复数,下列说法错误的是_________________. [填空题]在Python程序中,导入sys模块后,可以通过列表________________访问命令行参数. ...
- Lua中的字符串函数库
Lua解释器对字符串的支持很有限.一个程序可以创建字符串并连接字符串,但不能截取子串,检查字符串的大小,检测字符串的内容.在Lua中操纵字符串的功能基本来自于string库. 字符串库中的一些函数是非 ...
- Qt中调用OpenCV函数库时Crashed问题的解决。
这几天想在虚拟机上搭建Ubuntu的开发环境,包括了Matlab和QT(C++)的开发工具安装等,同时由于做图像处理,所以还必须要安装FFMPEG和OpenCV库.下面就讲讲我们安装时出现qt中调用O ...
- Python中使用pip安装库时提示:远程主机强迫关闭了一个现有的连接
场景 在cmd中使用pip install moviepy时,需要安装一些依赖库,很长时间后提示: 远程主机中断了一个现有的连接. 原因是默认镜像源下载过慢,将其修改为国内或者设置安装时的源. 这里以 ...
- Python中使用pip安装库时指定镜像源为豆瓣镜像源
场景 在使用pip进行安装库时,使用默认的库会很慢,甚至有时会出现远程主机中断了一个现有连接. 怎样在使用pip install 时指定镜像源为豆瓣镜像源. 实现 pip install moviep ...
- SDL教程4——在VS2010中设置SDL扩展库
前几节我们了解到,SDL基本库只能加载普通的BMP图像,如果我们还想加载其它格式的图片,我们就需要用到SDL的扩展库,它可以帮助我们加载BMP, PNM, XPM, LBM, PCX, GIF, JP ...
- 【cocos2d-x】Lua中的table函数库
一部分的table函数只对其数组部分产生影响, 而另一部分则对整个table均产生影响. 下面会分开说明. table.concat(table, sep, start, end) concat是c ...
- 安装python扩展库时只能使用pip_使用pip安装Python扩展库的方法
本文节选自作者的<Python编程基础及应用>视频教程.Python编程基础及应用_哔哩哔哩 (゜-゜)つロ 干杯~-bilibiliwww.bilibili.com 使用pip进行安装 ...
- 在C#中写pock pc 2003时,怎样控制发出声音
在普通的程序中可以,但在PPC中提示无法找到PInvoke DLL "wimm.dll"这个标准的mci我已经在项目中用的,不会提示找不到的. 我的用在window mobile ...
最新文章
- python 字符串拼接_面试官让用 3 种 python 方法实现字符串拼接 ?对不起我有8种……...
- ubuntu14.04,安装JDK1.8(JAVA程序需要的开发、运行环境)
- 用java实现二分搜索算法分析
- Linux系统中df与du命令查看分区大小
- 代做html网页多少钱,代做排名网站有吗,做排名帮你实现财富自由
- LetCode-算法-整数反转
- http header 设置编码_【译】http.client
- python机器学习应用mooc_(1)KNN
- 福利福利!20行代码教大家抓取斗鱼美女主播封面
- java 单例模式(饿汉模式和懒汉模式)
- Linux的capability深入分析
- 【P5385】【Cnoi2019】须臾幻境(LCT)
- 计算机打字测速,电脑的打字测速软件分享
- 百度直达号,一场自high的喜剧
- 软件工程课程第二次任务——需求分析与原型设计
- 射频识别系统及WMS仓库管理系统功能介绍
- 朱光潜给青年的十二封信 之 谈读书
- linux vim 编辑 保存 退出
- 小米 无线 linux 鼠标,实测小米便携式鼠标2:小巧精致 支持无线双模连接
- java xmap_XML和Java Bean的互相转换攻略 【XMAP】