热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C开发lua模块(二)操作table和调用lua函数

此篇是上篇入门篇的后续文章,上篇地址:http:blog.csdn.netzhang197093articledetails76400871C语言没有

此篇是上篇入门篇的后续文章,上篇地址:http://blog.csdn.net/zhang197093/article/details/76400871

C语言没有类似 lua_pushtable 和 lua_totable 的方法,这也很容易理解,因为C语言中没有一种数据类型可以和lua中的table相对应的,那C函数怎么操作table类型的参数或者返回table类型的结果呢,lua API中提供了一系列的操作table的函数。

void lua_createtable (lua_State *L, int narr, int nrec); //创建一个空的table并压入栈中,并预分配narr个array元素的空间和预分配nrec个非array元素的空间
void lua_newtable (lua_State *L); // lua_createtable的特例版,相当于调用 lua_createtable(L, 0, 0)


以上两个方法用于创建一个lua table并压入栈中,所以,想要返回一个table,就可以如下操作:

static int returntable(lua_State *L)
{lua_newtable(L);return 1;
}



上面代码返回的是一个空的table,如果需要向这个table中添加元素,API提供了几个用于设置table元素的方法:

void lua_settable (lua_State *L, int index);
void lua_rawset (lua_State *L, int index);void lua_setfield (lua_State *L, int index, const char *k);
void lua_rawseti (lua_State *L, int index, int n);


lua_settable在调用之前需要先压入你要设置的 key 和 value,例如你要设置table["foo"] = "bar",先把 "foo" 压入栈中,再把 "bar" 压入栈中,然后再调用lua_settable,index取你要设置的table在栈中的索引,调用完该方法后,压入的key和value会自动被弹出栈。lua_rawset除了设置时不会触发元表操作外和lua_settable基本相同,修改示例代码为table添加元素:

static int returntable(lua_State *L)
{lua_newtable(L);lua_pushstring(L, "foo");lua_pushstring(L, "bar");lua_settable(L, -3); //由于压入了key和value,所以此时的table索引为-3//lua_rawset(L, -3); 也可以使用lua_rawsetreturn 1; //设置后,会弹出 "foo"和 "bar", 所以此时table仍旧处在栈顶
}

lua_setfield简化了lua_settable的调用,key不再需要压入栈中,而是直接当作函数的第三个参数传入,所以只需要压入value然后调用方法即可,调用后同样会把value从栈中弹出,lua_rawseti 和 lua_setfield类似,但是只能设置整数类型的key,可以对于纯数字索引的数组使用。

static int returntable(lua_State *L)
{lua_newtable(L);lua_pushstring(L, "foo");lua_pushstring(L, "bar");lua_settable(L, -3); //由于压入了key和value,所以此时的table索引为-3//lua_rawset(L, -3); 也可以使用lua_rawsetlua_pushstring(L, "Tom");lua_setfield(L, -2, "username"); //由于只压入了value,所以table此时的索引为-2return 1;
}


接下来实现一个用指定字符切割字符串返回数组的函数:

static int split(lua_State *L)
{int len;const char *str = lua_tolstring(L, 1, &len);const char *sep = lua_tostring(L, 2);int lastpos = -1, i = 0, key = 1;lua_newtable(L); //创建一个table作为返回值for(;i }


上面代码使用了lua_tolstring 和 lua_pushlstring 两个方法,可以使用第三个参数,来获取或设置字符串的长度,这个方法可以在lua中像 split("aaa,bbb,ccc", ",") 这样调用,并返回一个字符串数组。

同样,C API也提供了获取table值得方法:

void lua_gettable (lua_State *L, int index); //获取索引为index的table中指定key的value,key要预先压入栈,函数调用结束key会被弹出栈,并把value压入栈中
void lua_rawget (lua_State *L, int index); // lua_gettable类似,但不涉及元表void lua_getfield (lua_State *L, int index, const char *k); //key不需要压入栈,直接当作第三个参数传入,得到的value同样会被压入栈中
void lua_rawgeti (lua_State *L, int index, int n); // 除了不涉及元表,并且key只能为整数



下面实现一个求数组所有元素和的方法:

static int array_sum(lua_State *L)
{//检查参数是否为一个table, 如果不是返回nilif (!lua_istable(L, 1)){lua_pushnil(L);return 1;}int sum &#61; 0;int len &#61; lua_objlen(L, 1); //返回数组的长度int i &#61; 1; //lua table 索引从1开始for(; i <&#61; len; i&#43;&#43;){lua_rawgeti(L, 1, i);sum &#43;&#61; lua_tointeger(L, -1);lua_pop(L, 1); //将刚刚获取的元素值从栈中弹出,其实也可以不用弹栈 因为table的索引始终是1&#xff0c;新读取的值始终在栈顶&#xff0c;也就是索引是-1&#xff0c;不过这是一个好的习惯}lua_pushinteger(L, sum);return 1;
}



lua_istable 可以检查一个值是否为table&#xff0c;是返回1&#xff0c;否则返回0&#xff0c; lua_objlen 可以获取字符串或数组的长度


以上就是C API中关于table操作的一些方法&#xff0c;接下来介绍一下在C代码中调用lua函数。


假设现在lua脚本中有这样一种需求&#xff0c;要求有一个array_map方法&#xff0c;该方法接受两个参数&#xff0c;第一个参数为一个数组&#xff0c;第二个参数为一个函数f&#xff0c;用数组里的所有元素当作参数调用函数f&#xff0c;用返回值组成一个新的数组并返回&#xff0c;大致的lua脚本像这样&#xff1a;

local function double(num)return num * 2
endlocal arr &#61; {1, 2, 3, 4, 5}local res &#61; array_map(arr, double) --期望得到 {2, 4, 6, 8, 10}


那该如何用C实现这个array_map方法呢&#xff0c;这就需要在C代码中调用lua函数&#xff0c;为此&#xff0c;C API提供了两个用来调用lua函数的方法&#xff1a;

void lua_call (lua_State *L, int nargs, int nresults);
int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);


这两个方法都相对比较复杂&#xff0c;lua_call要求首先压入要调用的lua函数&#xff0c;然后依次按顺序压入nargs个参数, 这时调用lua_call&#xff0c;之后这个函数和所有的参数都会从栈中弹出&#xff0c;最后把函数返回的nresults个结果依次压入栈中。lua_pcall在保护模式下执行lua函数&#xff0c;当执行出错时这个错误会被捕获&#xff0c;而不会直接终止程序运行&#xff0c;lua_pcall的返回值是一个错误码&#xff0c;当返回0时表示没有错误&#xff0c;此时效果和lua_call一样&#xff0c;当返回不为0时表示有错误&#xff0c;并将错误消息压入栈中&#xff0c;第四个参数还可以指定一个错误处理函数的索引&#xff0c;如果不指定可以传入0值。

下面实现前面提到的array_map方法&#xff1a;

static int array_map(lua_State *L)
{//校验参数类型if (!lua_istable(L, 1) || !lua_isfunction(L, 2)){lua_pushnil(L);return 1;}int len &#61; lua_objlen(L, 1); //数组的大小int i &#61; 1;for(; i <&#61; len; i&#43;&#43;){//由于方法调用之后会被弹出&#xff0c;所以调用之前先复制一份&#xff0c;pushvalue会将指定索引的值复制一份到栈顶lua_pushvalue(L, 2);lua_rawgeti(L, 1, i); //获取数组元素值并会压入栈中lua_call(L, 1, 1); //调用函数&#xff0c;一个参数 一个返回值,此时复制的函数和压入的参数都会弹出&#xff0c;然后压入结果lua_rawseti(L, 1, i); //再把结果替换掉数组中的原值。}lua_pushvalue(L, 1); //把数组复制一份放到栈顶 当作返回值return 1;
}


现在把本文中的所有方法放到上节中的clib.c中&#xff0c;重新编译成clib.so

#include //编写C函数 static使外部无法直接访问这个函数
static int sum(lua_State *L)
{ int a &#61; lua_tointeger(L, 1); //第一个加数&#xff0c;函数的第一个参数总是索引1int b &#61; lua_tointeger(L, 2); //第二个加数lua_pushinteger(L, a &#43; b); //压入结果 return 1; //返回1表示该方法只有一个返回值
}static int split(lua_State *L)
{ size_t len;const char *str &#61; lua_tolstring(L, 1, &len);const char *sep &#61; lua_tostring(L, 2);int lastpos &#61; -1, i &#61; 0, key &#61; 1;lua_newtable(L); //创建一个table作为返回值for(;i }static int array_sum(lua_State *L)
{//检查参数是否为一个table, 如果不是返回nilif (!lua_istable(L, 1)){lua_pushnil(L);return 1;}int sum &#61; 0;int len &#61; lua_objlen(L, 1); //返回数组的长度int i &#61; 1; //lua table 索引从1开始for(; i <&#61; len; i&#43;&#43;){lua_rawgeti(L, 1, i);sum &#43;&#61; lua_tointeger(L, -1);}lua_pushinteger(L, sum);float aver &#61; (float)sum / len; //平均值lua_pushnumber(L, aver);return 2;
}static int array_map(lua_State *L)
{//校验参数类型if (!lua_istable(L, 1) || !lua_isfunction(L, 2)){lua_pushnil(L);return 1;}int len &#61; lua_objlen(L, 1); //数组的大小int i &#61; 1;for(; i <&#61; len; i&#43;&#43;){//由于方法调用之后会被弹出&#xff0c;所以调用之前先复制一份&#xff0c;pushvalue会将指定索引的值复制一份到栈顶lua_pushvalue(L, 2);lua_rawgeti(L, 1, i); //获取数组元素值并会压入栈中lua_call(L, 1, 1); //调用函数&#xff0c;一个参数 一个返回值,此时复制的函数和压入的参数都会弹出&#xff0c;然后压入结果lua_rawseti(L, 1, i); //再把结果替换掉数组中的原值。}lua_pushvalue(L, 1); //把数组复制一份放到栈顶 当作返回值return 1;
}//声明一个luaL_Reg结构体数组
static const struct luaL_Reg funcs[] &#61; {{"sum", sum},{"split", split},{"array_sum", array_sum},{"array_map", array_map},{NULL, NULL} // 该数组最后一个元素始终是 {NULL, NULL}
};int luaopen_clib(lua_State *L)
{luaL_register(L, "clib", funcs); //第二个参数要和你的模块名一致return 1;
}


编写lua测试脚本&#xff1a;

local clib &#61; require "clib"local s &#61; clib.sum(3, 99)
print(s)print("---------------------")local str &#61; "aaa,bbbb,cccc,dddd,eeee"
local arr &#61; clib.split(str, ",")for i &#61; 1, #arr doprint(arr[i])
endprint("---------------------")local arr1 &#61; {1, 2, 3, 10}
local sum &#61; clib.array_sum(arr1)
print("sum&#61;", sum)print("---------------------")local function double(num)return num * 2
end
local arr2 &#61; {1, 2, 3, 4, 5}
local res &#61; clib.array_map(arr2, double)for i &#61; 1, #res doprint(res[i])
end


执行结果&#xff1a;

102
---------------------
aaa
bbbb
cccc
dddd
eeee
---------------------
sum&#61; 16
---------------------
2
4
6
8
10


篇幅所限&#xff0c;就写到这里&#xff0c;如果想更深入了解lua扩展的开发技术&#xff0c;欢迎关注后续文章。








推荐阅读
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文介绍了在Windows系统上使用C语言命令行参数启动程序并传递参数的方法,包括接收参数程序的代码和bat文件的编写方法,同时给出了程序运行的结果。 ... [详细]
author-avatar
bj韩式尕伙
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有