话从这里说起

在我发表《Lua中的类型与值》这篇文章时,就有读者给我留言了,说:你应该好好总结一下Lua中的function和userdata类型。现在是时候总结了。对于function,我在《Lua中的函数》这篇文章中进行了总结,而这篇文章将会对Lua中的userdata进行仔细的总结。对于文章,大家如果有任何疑议,都可以在文章的下方给我留言,也可以关注我的新浪微博与我互动。学习,就要分享,我期待你的加入。

userdata是啥?

userdata是啥?简单直译就是用户数据,如果再文艺一点,就叫做用户自定义数据。要这货有什么好处呢?首先,让我们来想象一个场景,你可以在 C中定义struct,当你在C中定义了一个struct,你有么有想过,如何让Lua表示这个struct,也就是说,Lua和C要进行沟通,如何让 Lua也能正确的访问这个struct呢?这是一个符合实际且实用的需求。遇到这种需求,怎么办?这个时候,实用userdata就能大展身手了,因此 Lua为此提供了一种基本的类型——userdata。userdata提供了一块原始的内存区域,可以用来存储任何东西。并且,在Lua中 userdata没有任何预定义的操作。先来看看怎么使用userdata。

函数lua_newuserdata会根据指定的大小分配一块内存,并将对应的userdata压入栈中,最后返回这个内存块的地址:

void*lua_newuserdata(lua_State *L,size_tsize);

下面,就通过一简单的实例来说说userdata的使用。

staticstructStudentTag{char*strName;// 学生姓名char*strNum;// 学号intiSex;// 学生性别intiAge;// 学生年龄};

定义一个学生结构体,之后的操作,都在这个学生结构体上进行,包括设置学生姓名,学号,性别和年龄。

staticintStudent(lua_State *L){size_tiBytes =sizeof(structStudentTag);structStudentTag*pStudent;pStudent =(structStudentTag*)lua_newuserdata(L,iBytes);return1;// 新的userdata已经在栈上了}

创建一个新的学生结构体,使用的lua_newuserdata函数,创建完成以后,这个新的userdata就在栈上,可以直接返回给Lua。下面就以设置姓名和获取姓名为例子。

staticintGetName(lua_State *L){structStudentTag*pStudent =(structStudentTag*)lua_touserdata(L,1);luaL_argcheck(L,pStudent !=NULL,1,"Wrong Parameter");lua_pushstring(L,pStudent->strName);return1;}staticintSetName(lua_State *L){// 第一个参数是userdatastructStudentTag*pStudent =(structStudentTag*)lua_touserdata(L,1);luaL_argcheck(L,pStudent !=NULL,1,"Wrong Parameter");// 第二个参数是一个字符串constchar*pName =luaL_checkstring(L,2);luaL_argcheck(L,pName !=NULL &&pName !="",2,"Wrong Parameter");pStudent->strName =pName;return0;}

在GetName函数中,只有一个参数,那就是使用Student函数创建的userdata,然后使用C语言的方式,从中取出名字,放到栈中,返回到Lua中。

在SetName函数中,需要传入两个参数,第一个参数是userdata,第二个参数是需要设置的值,然后直接赋值就好了,使用起来比较简单,没有很复杂的步骤,你觉的呢?单击这里下载完整工程项目userdatademo1.zip。

元表

上述的代码有一个很严重的问题,为什么这么说呢?我先把上一个例子的Lua代码贴出来:

require"userdatademo1"localobjStudent =Student.new()Student.setName(objStudent,"果冻想")Student.setAge(objStudent,15)localstrName =Student.getName(objStudent)localiAge =Student.getAge(objStudent)print(strName)print(iAge)

调用Student的new得到一个Student实例以后,以后调用Student的其它函数时,第一个参数都是使用Student函数得到的 userdata,也就是上面代码中的objStudent。在C模块侧,我们只是简单的判断了一下传进来的userdata是否为NULL,并没有办法 判断传进来的userdata参数是使用Student函数得到的;如果我传一个错误的userdata进去,程序也会继续运行,但有可能使内存遭到破 坏。那如何确定我们传入的userdata正是我们需要的userdata呢?我们需要一种这样的机制来确保参数的合法性。

一种辨别不同类型的userdata的方法是,为每种类型创建一个唯一的元表(什么是元表?)。每当创建了一个userdata后,就用相应的元表来标记它。而每当得到一个userdata后,就检查它是否拥有正确的元表。由于Lua代码不能改变userdata的元表,因此也就无法欺骗代码了。

为每个userdata都创建一个元表,那就需要有个地方来存储这个新的元表。在Lua中,通常习惯是将所有新的C类型注册到注册表中,以一个类型名作为key,元表作为value。由于注册表中还有其它的内容,所以必须小心地选择类型名,以避免与key冲突。

Lua的辅助库中提供了一些函数来帮助实现上面说的内容,可以使用的辅助库函数有:

intluaL_newmetatable(lua_State *L,constchar*tname);voidluaL_getmetatable(lua_State *L,constchar*tname);void*luaL_checkudata(lua_State *L,intindex,constchar*tname);

luaL_newmetatable函数会创建一个新的table用作元表,并将其压入栈顶,然后将这个table与注册表中的指定名称关联起来。 luaL_getmetatable函数可以在注册表中检索与tname关联的元表。luaL_checkudata可以检查栈中指定位置上是否为一个 userdata,并且是否具有与给定名称相匹配的元表,如果该对象不是一个userdata,或者它不具有正确的元表,就会引发一个错误;否则它就会返 回这个userdata的地址。现在来重写上面的那个例子:

intluaopen_userdatademo2(lua_State *L){// 创建一个新的元表luaL_newmetatable(L,"Student");luaL_register(L,"Student",arrayFunc);return1;}

创建一个新元表,作为该userdata的唯一标识。

staticintStudent(lua_State *L){size_tiBytes =sizeof(structStudentTag);structStudentTag*pStudent;pStudent =(structStudentTag*)lua_newuserdata(L,iBytes);// 设置元表luaL_getmetatable(L,"Student");lua_setmetatable(L,-2);return1;// 新的userdata已经在栈上了}

在创建userdata的时候,设置该userdata的元表。在使用的时候,我们就可以调用luaL_checkudata对参数进行检查。

staticintGetName(lua_State *L){structStudentTag*pStudent =(structStudentTag*)luaL_checkudata(L,1,"Student");lua_pushstring(L,pStudent->strName);return1;}

当写下一下Lua语句时:

Student.getAge(io.stdin)

就会抛出这样的异常错误:

bad argument #1 to 'getAge' (Student expected, got userdata)

现在,我想你应该懂得了如何去简单的使用userdata了吧。接下来,上点难的东西。单击这里下载完整项目工程userdatademo2.zip。

面向对象的访问

关于Lua的面向对象对象编程,我在《Lua中的面向对象编程》这篇文章中进行了总结,如果你对Lua中的面向对象编程还不是很熟悉,可以再去阅读一下《Lua中的面向对象编程》。

在上面的Lua代码中,可以看到,我都是使用以下方式调用函数的:

localstrName =Student.getName(objStudent)

这种调用方式无可厚非,但是从面向对象的角度来说,我new了一个对象,这就是一个独立的对象,我应该这样调用,才能更好理解啊。

localstrName =objStudent:getName()

是吧。这又回到了《Lua中的面向对象编程》 一文中说到的问题,由于getName、setName等这些函数都是在Student中定义的,而在objStudent对象中,并没有这些函数的定 义,怎么办?还是老办法,我们需要设置objStudent的元表,设置__index字段,当在objStudent中找不到对应的函数时,就去 Student中查找,之前在《Lua中的面向对象编程》一文中,也是介绍的这个办法。来吧,实现一下吧。

intluaopen_userdatademo3(lua_State *L){// 创建一个新的元表luaL_newmetatable(L,"Student_Metatable");// 元表.__index = 元表lua_pushvalue(L,-1);lua_setfield(L,-2,"__index");luaL_register(L,NULL,arrayFunc_meta);luaL_register(L,"Student",arrayFunc);return1;}

上述代码中,有两个地方要特别注意。首先要设置Student.__index = Student,使用上面代码中的第7、8行实现的。还有一个需要注意的地方是luaL_register的特殊用法。在第一次调用 luaL_register时,它的第二个参数是NULL,这样的话,luaL_register不会创建任何用于存储函数的table,而是以栈顶的 table作为存储函数的table,而现在栈顶的table就是luaL_newmetatable创建的元表。代码中,两个 luaL_register的第三个参数是不一样的,它们的定义如下:

staticstructluaL_reg arrayFunc[]={{"new",Student},{NULL,NULL }};staticstructluaL_reg arrayFunc_meta[]={{"getName",GetName},{"setName",SetName},{"getAge",GetAge},{"setAge",SetAge},{"getSex",GetSex},{"setSex",SetSex},{"getNum",GetNum},{"setNum",SetNum},{NULL,NULL}};

最终调用luaL_register(L, “Student”, arrayFunc);得到的Student表中,就只有一个函数new;而元表Student_Metatable中则有arrayFunc_meta 数组中包含的所有方法。我在把Lua代码贴上来,然后再详细的分析一下流程。

require"userdatademo3"localobjStudent =Student.new()objStudent:setName("果冻想")objStudent:setAge(15)localstrName =objStudent:getName()localiAge =objStudent:getAge()print(strName)print(iAge)

调用require “userdatademo3″将得到一个Student表,在该表中,只有一个函数——new;

调用Student.new()将得到一个Student结构体的userdata,该userdata的元表为Student_Metatable;

由于在userdata中本身就没有key,所以在userdata中没有table中那样的key的概念;当调用objStudent:setName时,就会去元表Student_Metatable中找setName,然后完成调用;

由于Student本身没有被设置元表Student_Metatable;当调用Student.setName(objStudent, “果冻想”)时,就会出错。这样也好,Student本身就不是一个实际的“学生”对象,只是一个模板,使用Student直接调用setName出错, 完全符合语义。

当然了,除了__index可以重新被定义以外,其它预定义的元方法也可以被重新定义。在完整的项目工程中提供了__tostring元方法的实现,单击这里下载完整工程userdatademo3.zip。

轻量级userdata

怎么又有了一个轻量级userdata了?这货又是什么?专业点,叫做“light userdata”。我在前面总结的userdata叫做“full userdata”。

轻量级userdata是一种表示C指针的值(即void *)。由于它是一个值,所以不用创建它。要将一个轻量级userdata放入栈中,只需要调用lua_pushlightuserdata即可。

voidlua_pushlightuserdata(lua_State *L,void*p);

尽管两种userdata在名称上差不多,但它们之间还是存在很大不同的。轻量级userdata不是缓冲,只是一个指针而已。它也没有元表,就像数字一样,轻量级userdata不受到垃圾收集器的管理。

轻量级userdata的真正用途是相等性判断。一个完全userdata是一个对象,它只与自身相等。而一个轻量级userdata则表示了一个 C指针的值。因此,它与所有表示同一个指针的轻量级userdata相等。可以将轻量级userdata用于查找Lua中的C对象。

现在就来说一种轻量级userdata的使用,还记的我在《再说C模块的编写(2)》中总结的注册表么?谈及注册表的key的时候,说使用UUID是一种不错的方案,现在就使用轻量级的userdata结合static来实现无冲突的key。

// 压入轻量级userdata,一个static变量的地址staticcharkey ='k';lua_pushlightuserdata(L,(void*)&key);lua_pushstring(L,"JellyThink");lua_settable(L,LUA_REGISTRYINDEX);

由于静态变量的地址在一个进程中具有唯一性,所以绝对不会出现重复key的问题。

// 从注册表中取对应的值lua_pushlightuserdata(L,(void*)&key);lua_gettable(L,LUA_REGISTRYINDEX);

lua把userdata写入mysql_Lua中的userdata相关推荐

  1. lua把userdata写入mysql_Lua教程(十九):userdata

    在Lua中可以通过自定义类型的方式与C语言代码更高效.更灵活的交互.这里我们通过一个简单完整的示例来学习一下Lua中userdata的使用方式.需要说明的是,该示例完全来自于Programming i ...

  2. lua把userdata写入mysql_Lua 之 userdata

    Lua 之 userdata 在Lua中可以通过自定义类型(user data)与C语言代码更高效.更灵活的交互,从而扩展Lua能够表达的类型. full userdata full userdata ...

  3. lua中给userdata绑定元表示例

    前言 这篇博客,我估计写不好.一方面是内容挺绕,一方面是我没有看过书,是照葫芦画瓢写代码. 前置要求: Lua调用C代码 lua中表与元表 Lua操作C语言用户自定义类型数据Userdata 上面第三 ...

  4. python把坐标写入文本_Python实现将数据写入netCDF4中的方法示例

    本文实例讲述了Python实现将数据写入netCDF4中的方法.分享给大家供大家参考,具体如下: nc文件为处理气象数据文件.用户可以去https://www.lfd.uci.edu/~gohlke/ ...

  5. Python 解决写入csv中间隔一行空行问题

    转载解决写入csv中间隔一行空行问题 写入csv: with open(birth_weight_file,'w') as f:writer=csv.writer(f)writer.writerow( ...

  6. RediSQL 0.8.0 发布,将查询结果写入流中

    百度智能云 云生态狂欢季 热门云产品1折起>>>   RediSQL 0.8.0 发布了,RediSQL 是一个 Redis 模块,为 Redis 提供完整的 SQL 功能.Redi ...

  7. LUA和C#关于字符串中\0的处理

    LUA和C#关于字符串中\0的处理 LUA中: local s = "hello\0\0dddddddd" print(s) --hello C#中: string s = &qu ...

  8. python读取excel送到网页_python怎么读取excel!怎么用python将excel数据写入网页中

    怎么用python将excel数据写入网页中 # 装 xlrd-0.9.2 xlutils-1.7.1 这两个模 from xlwt import Workbook, Formula import x ...

  9. python mysql写入速度加快_解决python写入mysql中datetime类型遇到的问题

    解决python写入mysql中datetime类型遇到的问题 发布时间:2020-08-31 16:46:47 来源:脚本之家 阅读:89 作者:WilliamDescant 刚开始使用python ...

  10. 通过 crontab将linux中的top信息写入文件中

    通过 crontab将linux中的top信息写入文件中. 问题:1.直接执行命令可以正常,放到shell脚本里面执行不行.            2.直接执行shell脚本正常输出,放到cronta ...

最新文章

  1. Linux 群组管理
  2. OSPF——虚链路详解(含配置命令)
  3. mysql分组后再分组_全网最全的mysql分组后取topN的解答
  4. vue 萤石云视频监控对接
  5. window 软件 C盘 文件 搬家(配置文件搬家)
  6. 关于高校房产管理系统中主要管理模块都有哪些
  7. 参考文献格式字号字体_论文字体字号格式要求是什么?
  8. antd charts实现地图下钻
  9. 201226元件_看电阻型号
  10. 国外免费电子书资源下载
  11. 记录一下自己爬虎牙LOL主播的爬虫思路
  12. 性能测试连载 (8)-jmeter 实战分析并发、RPS、RT 公式换算
  13. gamess下载安装
  14. java md5 签名_java md5签名
  15. AutoCAD.net-错误消息大全
  16. 你想知道的2022年广东PMP考试报名时间来咯!
  17. vb6.0 类没有注册,查找具有 CLSID 的对象:{7EBDAAE0-8120-11CF-899F-00AA00688B10} 7EBDAAE1 7EBDAAE2
  18. Android Build类
  19. python画图设置字体_【转】matplotlib画图时的中文设置
  20. 哪些win软件linux没有,Linux中替代Windows的软件有哪些?

热门文章

  1. java 向后台传两个json数据类型_前台向后台传递JSON字符串,处理JSON字符串类型的方法...
  2. 【路径规划】基于matlab人工蜂群优化粒子群算法求解最短路径规划问题【含Matlab源码 124期】
  3. 【路径规划】基于matlab A_star算法多机器人路径规划【含Matlab源码 1251期】
  4. 网页游戏怎么修改数据_2014一周网页游戏数据报告(8.18—8.24)
  5. 数据应用apply练习
  6. Linux在文件中查找the字样,Linux文件查找
  7. Vmware里安装Ubuntu时由于分辨率问题,界面显示不全解决办法
  8. 内网分享文件html源码,vue项目分享html页面(服务器只能内网访问)
  9. 为什么用虚拟机做服务器,虚拟机的优势:保留虚拟机的4个理由
  10. 解密新一代 Java JIT 编译器 Graal