wrapper for lua
考虑使用已经有的dll,要写wrapper,使得在lua中能调用dll里的函数,嗯,参考《Programming in lua》,然后仿写luars232。
一、函数定义
先分析一个函数的写法,其它函数类似:
/*
* error, written_len = port:write(data [, timeout_ms])
*/
static int lua_port_write(lua_State *L)
{
int ret = ;
int argc = ;
unsigned int timeout = ;
unsigned int wlen = ;
size_t len = ;
const char *data;
struct rs232_port_t *p = NULL; p = *(struct rs232_port_t**) luaL_checkudata(L, , MODULE_NAMESPACE);
lua_remove(L, ); if (p == NULL || !rs232_port_open(p)) {
lua_pushinteger(L, RS232_ERR_PORT_CLOSED);
lua_pushinteger(L, );
return ;
} argc = lua_gettop(L);
switch (argc) {
case : {
data = luaL_checklstring(L, , &len);
ret = rs232_write(p, (unsigned char*) data, (unsigned int) len, &wlen);
break;
}
case :
data = luaL_checklstring(L, , &len);
timeout = (unsigned int) luaL_checknumber(L, );
ret = rs232_write_timeout(p, (unsigned char*) data, (unsigned int) len, &wlen, timeout);
break;
default:
lua_pushinteger(L, RS232_ERR_CONFIG);
lua_pushinteger(L, );
return ;
} lua_pushinteger(L, ret);
lua_pushinteger(L, wlen);
return ;
}
1、传给函数的参数是lua_State *L。用PIL的说法,lua是使用一个virtual stack来和C之间交换数据的。这一stack里的每一个element都是lua中的一个value,比如number、string、nil这些。为了方便,我们是可以用index来取用这些element的。取用element时,正的index表示它的绝对位置(1开始),即直接使用正1,我们就可以取到最早压入的数;负的index表示它相对堆顶的偏移量(offset),注意,stack的top是我们最后压入stack的那个element。如果有n个element,那么,用-1取到的就是最后被压入stack的element,和使用n取到的是同一个element。另外需留意,这里是从index1开始,根据PIL的说法,lua每次调用一个C函数,都会给他分配一个新的堆,第一个压入的是函数名,然后是第一个参数,依次类推。。。
2、我们看到的是一个void *luaL_checkudata (lua_State *L, int narg, const char *tname)的调用,在PLI 28.2找到它的描述。它是和metable密切相关的函数,我们可以检查给定index位置的element是不是tname类型的。如果是,它会返回这个element的地址,所以这里的赋值是赋给指针的;如果不是,它会抛出一个错误(注,据说以前是返回NULL,具体不详)。关于这里的tname,后面会介绍到,这里只需要知道我们使用这个tname可以找到某个metatable,这个关联是在我们注册C函数到lua时产生的。
3、接下来是void lua_remove (lua_State *L, int index)调用,因为我们已经拿到了port句柄的地址,所以,我们可以把这一个element抛掉了。这个element为最早压入,所以用index=1.
4、下面有个判断,P==NULL或者是使用rs232_open来打开端口无效,则执行这里的操作,调用的是void lua_pushinteger (lua_State *L, lua_Integer n)。调用push来压一个integer到堆顶,是为了返回参数给lua。这个和后面的return不同,return是给lua解释器看的,而我们调用lua函数的返回值是从堆L中取的,或者说,压进去的element就是我们在lua中看得到的返回值。
5、接下来的int lua_gettop (lua_State *L),是查看L中还有几个element。如果是有0个,那么这个stack为empety。这里为了检查调用这个函数时传过来几个参数。
6、这里的switch根据剩余几个element来做出选择,事实上,这里就是我们要在lua中传给这个函数几个参数。从下面的判断来看,可以是1个或2个。如果是1个参数,这个参数就是data;如果是2个参数,第一个是data,第二个是timeout。这里调用const char *luaL_checkstring (lua_State *L, int narg),取出L中第几个element(第几个被压入stack,lua中调用函数时参数的顺序),取之前要先判断这个element是不是一个string,并以string的形式把这个element拿出来。
7、另外,留意到,这个函数里都是renturn 2。不知道为什么,翻PIL再找答案。
二、声明各函数的注册关系
这里主要是从下面的struct luaL_reg 型变量声明开始的:
static luaL_reg port_methods[] = {
{ "__tostring", lua_port_tostring },
{ "__gc", lua_port_close },
{ "read", lua_port_read },
{ "write", lua_port_write },
{ "close", lua_port_close },
{ "flush", lua_port_flush },
{ "device", lua_port_device },
{ "fd", lua_port_fd },
/* baud */
{ "baud_rate", lua_port_get_baud },
{ "baud_rate_tostring", lua_port_get_strbaud },
{ "set_baud_rate", lua_port_set_baud },
/* data */
{ "data_bits", lua_port_get_data },
{ "data_bits_tostring", lua_port_get_strdata },
{ "set_data_bits", lua_port_set_data },
/* stop */
{ "stop_bits", lua_port_get_stop },
{ "stop_bits_tostring", lua_port_get_strstop },
{ "set_stop_bits", lua_port_set_stop },
/* parity */
{ "parity", lua_port_get_parity },
{ "parity_tostring", lua_port_get_strparity },
{ "set_parity", lua_port_set_parity },
/* flow */
{ "flow_control", lua_port_get_flow },
{ "flow_control_tostring", lua_port_get_strflow },
{ "set_flow_control", lua_port_set_flow },
/* dtr */
{ "dtr", lua_port_get_dtr },
{ "dtr_tostring", lua_port_get_strdtr },
{ "set_dtr", lua_port_set_dtr },
/* rts */
{ "rts", lua_port_get_rts },
{ "rts_tostring", lua_port_get_strrts },
{ "set_rts", lua_port_set_rts },
{ NULL, NULL }
};
PIL里面提到,已经有定义一个变量类型如下:
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
所以,这里是定义了一个luaL_reg类型的数组,该数组的内容有巴拉巴拉个函数以及他们对应的在lua中的函数名,这些函数就是我们前面定义的如lua_port_write()之类的。应该叫注册关系还是什么?
还提到,其实,这个数组是给下面这个函数用的:
void luaL_register (lua_State *L,
const char *libname,
const luaL_Reg *l);
这个函数的执行是分情况的,这里只是简单翻译文档,我也没明白:
1、这个函数打开了一个library。
2、如果libname是NULL,那么,这个函数就按照数组l中的对应关系,把所有函数注册到堆L顶部的table中。
3、如果libname不为NULL,那么,这个函数就会先创建一个以libname那么为变量名的table,这个table会是一个全局变量,并且把这个table当做是package.loaded[libname]的值,然后,他会按照数组l中的对应关系,把所有函数注册到堆L顶部的table中。
4、如果libname变量本身就是一个table,或者package.loaded[libname]中有一个table,那么它会复用这个table,而不是重新创建一个。
这里还有定义另一个数组,也是给后面用的:
static luaL_reg port_functions[] = {
{ "open", lua_port_open },
{ "error_tostring", lua_port_strerror },
{ NULL, NULL }
};
不过,理解luaL_register的动作的话,还得靠分析后面的代码。
三、注册到lua
下面这句是创建一个metatable:
static void create_metatables(lua_State *L, const char *name, const luaL_reg *methods)
{
luaL_newmetatable(L, name);
lua_pushvalue(L, -);
lua_setfield(L, -, "__index");
luaL_register(L, NULL, methods);
}
1、调用int luaL_newmetatable (lua_State *L, const char *tname),PIL讲,如果在L的注册关系中已经有了一个以tname为名的key,那么直接return 0;否则的话,创建一个表作为userdata的metatable,把它添加到注册关系中,然后返回1。不管怎么样,它都会把最后那个以tname为key的注册项的内容push到L顶上。
2、调用void lua_pushvalue (lua_State *L, int index),这个调用是copy了指定index处element的value到堆顶。所以,这里是做了一个刚才我们创建的metatable的copy。
3、调用void lua_setfield (lua_State *L, int index, const char *k),手册说这个函数是执行了类似于t[k]=value的操作,这里的table t是位置index处的table,v就是在堆顶的那个element的value。而且lua_setfield会先把堆顶的element给pop出来。那么,在这里,它做的事就是给我们刚创建的metatable添加了一个member,member的key是“__index”,value是它自己的地址?
4、这个呢,就是我们前面遇到过的luaL_register了,因为libname的位置为NULL,而且我们的堆顶位置现在是一个table,即刚创建的metatable,所以,这句在这里就是按照methods对应的注册关系,把所有函数注册到metatable。
然后完了是定义了一个函数,内容如下:
RS232_LIB int luaopen_luars232(lua_State *L);
RS232_LIB int luaopen_luars232(lua_State *L)
{
int i;
create_metatables(L, MODULE_NAMESPACE, port_methods);
luaL_register(L, MODULE_NAMESPACE, port_functions); for (i = ; luars232_ulong_consts[i].name != NULL; i++) {
lua_pushstring(L, luars232_ulong_consts[i].name);
lua_pushnumber(L, luars232_ulong_consts[i].value);
lua_settable(L, -);
} lua_pushstring(L, MODULE_VERSION);
lua_setfield(L, -, "_VERSION"); lua_pushstring(L, MODULE_BUILD);
lua_setfield(L, -, "_BUILD"); lua_pushstring(L, MODULE_TIMESTAMP);
lua_setfield(L, -, "_TIMESTAMP"); lua_pushstring(L, MODULE_COPYRIGHT);
lua_setfield(L, -, "_COPYRIGHT"); DBG("[*] luaopen_luars232(Version: '%s' Build: '%s' TimeStamp: '%s')\n",
MODULE_VERSION, MODULE_BUILD, MODULE_TIMESTAMP); return ;
}
这个函数是唯一一个需要外部访问的函数,RS232_LIB事实上是一个宏定义,需要给生成dll的编译器用的:
#define RS232_LIB __declspec(dllexport)
那来看看这个函数做了些什么:
1、create_metatables,也就是我们上面分析的那个函数做的事情。MODULE_NAMESPACE的定义是"luars232",即在表luars232中创建了一个metatable,然后按照port_methods中对应关系,把那些函数注册到metatable中。
2、然后,直接按照port_functions的对应关系,把两个函数注册到表“luars232”中,这两个函数是不依赖于port存在的,而上面metatable中定义的是和port相关的操作。
3、接下来是lua_pushstring、lua_pushnumber和void lua_settable (lua_State *L, int index)。前两个好理解,往堆里压入一个string和一个number。lua_settable的话,手册里说,它会执行t[k]=v的操作,t是index指向的element,v是堆上index为-1的element,k是index为-2的element;执行这个调用的话,会把k和v都先从堆上pop出来,也就是执行完这个调用,原来index=-3的element成为了堆顶。在这里,index=-3的element是那边的metatable。
这句留着自己看的:比如,RS232_BAUND_300,我们在lua中使用这个变量的时候,我们传给dll的是"RS232_BAUD_300"这个字符串,然后dll会去处理好这个字符串。
=======================get in===================
看书看到这里,做了个很头疼的决定,11点了,是去睡觉还是继续看?因为明天就是周末了,所以我决定继续把它看完。嗯!
因为lua_setfiled涉及另一个问题,所以在这里展开下。这里的field是什么?其实就是指某个table的一个register项,我们前面定义过的luaL_reg型数组里的一项。
还是理解不能,所以了解了另一个用得比较多的调用void lua_getfield (lua_State *L, int index, const char *k)。一个例子是,lua_getfield(L, LUA_GLOBALSINDEX, "f"),它需要在LUA_GLOBALSINDEX定位的table中,找到名为f的注册项,然后把f放在堆的顶部;这句可以用来找注册到全局变量里的某个function。这个怎么理解?比如我们在lua中调用了某个函数 :
a = f("how", t.x, )
这一句调用实际上等于下面的C语言过程:
lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* function to be called */
lua_pushstring(L, "how"); /* 1st argument */
lua_getfield(L, LUA_GLOBALSINDEX, "t"); /* table to be indexed */
lua_getfield(L, -, "x"); /* push result of t.x (2nd arg) */
lua_remove(L, -); /* remove 't' from the stack */
lua_pushinteger(L, ); /* 3rd argument */
lua_call(L, , ); /* call 'f' with arguments and result */
lua_setfield(L, LUA_GLOBALSINDEX, "a"); /* set global 'a' */
这里的第一句是调用lua_getfield在全局变量里找到f,然后把function f的地址放到堆顶,接着把第一个参数也就是string “how”压到堆顶,继续再调用lua_getfield找到table t并放到堆顶,再用lua_getfield从table t中找到name是x的注册项并把它的value放到堆顶,取出t.x之后应该是把刚拿出来的table t给移走,接着再压入下一个参数“14”。自此,函数,以及参数都被压进了stack,然后调用lua_call(L, 3, 1)来执行函数。嗯,我们的重点在最后一句,lua_setfield,new一个变量a出来,把堆顶的element给push出来并把它的value给a,然后再把变量a注册到LUA_GLOBALSINDEX去。
====================get out==================================
wrapper for lua的更多相关文章
- delphi调用LUA函数来处理一些逻辑
替同事做了个洛奇英雄传自动染色程序,关于屏幕取色的. 因为里面他对颜色的要求比较复杂,改动也比较大,于是我让他把逻辑写在 lua 脚本里面. uses LUA, LUALIB; function lu ...
- GO语言的开源库
Indexes and search engines These sites provide indexes and search engines for Go packages: godoc.org ...
- Go语言(golang)开源项目大全
转http://www.open-open.com/lib/view/open1396063913278.html内容目录Astronomy构建工具缓存云计算命令行选项解析器命令行工具压缩配置文件解析 ...
- [转]Go语言(golang)开源项目大全
内容目录 Astronomy 构建工具 缓存 云计算 命令行选项解析器 命令行工具 压缩 配置文件解析器 控制台用户界面 加密 数据处理 数据结构 数据库和存储 开发工具 分布式/网格计算 文档 编辑 ...
- cryptdb中wrapper.lua的分析
因为cryptDB是在mysql-proxy的基础上来实现了,可以看成是为mysql-proxy添加了新的.为mysql-proxy已经为开发人员提供了相应的接口.如果开发人员只需要通过lua脚本语言 ...
- lua C++ wrapper
背景 最近在研究lua的c++绑定库,使用过一下几个 luabind 问题:没人维护 https://github.com/vinniefalco/LuaBridge https://github.c ...
- luars232库中用到的一些C API for lua
代码就不贴了,这里只是梳理一下前两篇里面忽略的一些东西,作为读代码的记录吧. 1.头文件 #include <lauxlib.h> #include <lua.h> All A ...
- lua中的table、stack和registery
ok,前面准备给一个dll写wrapper,写了篇日志,看似写的比较明白了,但是其实有很多米有弄明白的.比如PIL中使用的element,key,tname,field这些,还是比较容易混淆的.今天正 ...
- Lua知识备忘录
最近对Lua很感兴趣,以下是本阶段学习的总结,包含三部分,一部分是基础语法,一部分是扩展和解释器嵌入,最后一部分是Lua小练习. 知识涉及:Lua语言编程基础:Lua&C++:Lua扩展.嵌入 ...
随机推荐
- 丰富Easyui 的插件 - lookup
插件用途: 主要用于表单中,某字段的内容是用其他表里的记录ID.当然你可以使用combobox.combotree.combogrid等,但有时这些表现方式并不是很好,希望弹出个层,然后在去做一些查询 ...
- C语言复习(1)
test.c #include <stdio.h> int main(){ printf("hello\n"); return 0; } 1.预处理阶段 由于在test ...
- git的安装以及遇到的问题
git安装以及遇到的问题 之前没有学会如何在Ubuntu下使用git,国庆放假回来后,完成了git的安装,补回来了之前没有学会的东西. 以下是我安装的过程以及遇到问题.解决问题的过程. 这次安装git ...
- 我的Logo设计简史
近日,日本东京奥运会会微因涉嫌抄袭而被弃用的新闻引起设计界的一翻热论.在此我想到自己的LOGO设计,虽说并一定不好看甚至自己看回来都觉得略丑,但 几乎没有过抄袭的念头.有句话说,不想当设计师的程序猿不 ...
- 拥抱HTML5 — Page Visibility(页面可见性) API介绍
H5 提供了很多简单实用的 API,Page Visibility API 就是其中之一. 不知道用户是不是在与页面交互,这是困扰广大 Web 开发人员的一个主要问题.如果 页面最小化了 或者 隐藏在 ...
- <实训|第六天>偷偷让新手的Linux无限重启附linux主机名称不是随便乱改的!
先说个事情:这几天我正在忙一个项目的设计,8月1号之前要弄出来,所以每天都要弄到很晚,可能更新就有点跟不上了,不过我如果有时间的话,我就更新,没时间的话,我会在8月1号之后统一更新出来,希望大家谅解! ...
- Bootstrap系列 -- 7. 列表排版方式
一. 去点列表 1. 使用class=list-unstyled <ul > <li>无序列表项目</li> <li>无序列表项目</li> ...
- (404) 未找到 获取StatusCode状态码
异常代码: (HttpWebResponse)req.GetResponse(); 当执行这段代码出现异常 解决问题 那如果我们想获得错误发生时候服务器段错误页面的源代码该如何做呢? 其实非常非常简单 ...
- WinForm程序执行JS代码的多种方法以及使用WebBrowser与JS交互
方法一 使用微软官方组件Interop.MSScriptControl 1.msscript.ocx下载的地址 http://www.microsoft.com/downloads/details ...
- C# EventHandler and Delegate(委托的使用)
委托的声明 public delegate void MyDelegate(string str); 注 1.委托的定义和方法的定义类似,只是在前面加了一个delegate,但委托不是方法,它是一种特 ...