本实例实现一种很简单的类型------布尔数组。C语言可以实现将每个布尔值存储在一个bit中,从而减少内存用量。

必须的一些宏

Code Snippet
  1. #defineBITS_PER_WORD (CHAR_BIT * sizeof(unsignedint))  //bit
  2. #defineI_WORD(i) ((unsignedint) i / BITS_PER_WORD)    //bitword
  3. #defineI_BIT(i) 1<<((unsignedint) (i) % BITS_PER_WORD) //word bit

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

以下函数用lua_newuserdata创建一个新的布尔数组

以下函数用lua_newuserdata创建了一个新的布尔数组

Code Snippet
  1. typedefstructNumArray{
  2. intsize;
  3. unsignedintvalues[1];
  4. }NumArray;
  5. staticintnewarray(lua_State * L)
  6. {
  7. size_tnBytes;
  8. NumArray * a;
  9. intn = luaL_checkint(L,1);   //
  10. luaL_argcheck(L,n>=1,1,"invalid size");  //>1
  11. nBytes = sizeof(NumArray) + I_WORD(n-1)*sizeof(unsignedint);
  12. a = (NumArray * ) lua_newuserdata(L,nBytes);
  13. a->size = n;
  14. for(inti = 0;i<I_WORD(n-1);i++)
  15. a->values[i] = 0;//
  16. return 1;
  17. }

只要lua注册好newarray就可以通过 a = array.new(1000) 来创建一个数组。array.set(array,index,value)在数组中存储元素。

Code Snippet
  1. staticintsetarray(lua_State * L)
  2. {
  3. NumArray * a = (NumArray * ) lua_touserdata(L,1);
  4. intindex = luaL_checkint(L,2);
  5. luaL_checkany(L,3);
  6. if(lua_toboolean(L,3))
  7. a->values[I_WORD(index)] |= I_BIT(index);
  8. else
  9. a->values[I_WORD(index)] |= I_BIT(index);
  10. return 0;
  11. }

对应的有getarray

Code Snippet
  1. staticintgetarray(lua_State * L)
  2. {
  3. NumArray * a= (NumArray * ) lua_touserdata(L,1);
  4. intindex = luaL_checkint(L,2);
  5. lua_pushboolean(L,a->values[I_WORD(index)] & I_BIT(index));
  6. return 1;
  7. }

下面还定义了一个函数用于检索一个数组大小

Code Snippet
  1. staticintgetsize(lua_State * L)
  2. {
  3. NumArray * a = (NumArray*) lua_touserdata(L,1);
  4. lua_pushinteger(L,a->size);
  5. return 1;
  6. }

最后需要代码来初始化这个库

Code Snippet
  1. staticconststructluaL_Regarraylib[] =
  2. {
  3. {"new",newarray},
  4. {"set",setarray},
  5. {"get",getarray},
  6. {"size",getsize},
  7. {NULL,NULL}
  8. };
  9. intluaopen_array(lua_State * L)
  10. {
  11. luaL_register(L,"array",arraylib);
  12. return 1;
  13. }

28.2 元表

一种辨别不 同类型的userdata的方法是,为每种类型创建一个唯一的过元表,每当创建一个userdata后,就用相应的元表来标记它。每当和到一个userdata之后,就检查它是否拥有正确元表。由于lua代码不能改变usedata的元表,因此也无法欺骗代码。

通常辅助库提供了一些函数来实现这些内容,可以使用的辅助库函数有

int luaL_newmetatable(lua_State * L,const char * name)  创建一个新的table用作元表,并将其压栈,然后将这个table与注册表中的指定名称关联起来

int luaL_getmetatable(lua_State * L ,const char * name)  可以在注册表中检测与name关联的元表

void * luaL_checkudata(lua_State * L ,int index,const char * name) 可以检查栈中指定位置上是否有一个userdata,或者它不具有正确名称,就会引发一个错误。否则就返回这个usrdata的地址。

下面开始修改代码 :

1.必须创建一元表

Code Snippet
  1. intluaopen_array(lua_State * L)
  2. {
  3. luaL_newmetatable(L,"LuaBook.array");
  4. luaL_register(L,"array",arraylib);
  5. return 1;
  6. }

2.修改newarray 使其为所有新建的数组设置这个元表

Code Snippet
  1. staticintnewarray(lua_State * L)
  2. {
  3. size_tnBytes;
  4. NumArray * a;
  5. intn = luaL_checkint(L,1);   //
  6. luaL_argcheck(L,n>=1,1,"invalid size");  //>1
  7. nBytes = sizeof(NumArray) + I_WORD(n-1)*sizeof(unsignedint);
  8. a = (NumArray * ) lua_newuserdata(L,nBytes);
  9. a->size = n;
  10. for(inti = 0;i<I_WORD(n-1);i++)
  11. a->values[i] = 0;//
  12. luaL_getmetatable(L,"LuaBook.array");
  13. lua_setmetatable(L,-2);
  14. return 1;
  15. }

3.最后,setarray getarray getzie 必有检其第一个参数是不回为一个合法的数组。所以定义了如下宏

Code Snippet
  1. #definecheckarray(L) \\par     (NumArray*) luaL_checkudata(L,1,"LuaBook.array")

28.3 面向对象的访问

下一步就是这种新类型变换为一个对象,然后就可以用普面向对象的语法来使用它们了如

a = array.new(1000)
print(a:size())  --1000
a:set(10,true) 
print(a:get(10)) –true

注意:a: size() 等价于a.size(a)  因此,必须使用表达式a.size返回前面定义的函数getsize。实现这一点的关键是使用__index元方法。对于table而言Lua会在找不到指定key时调用 这个,对于userdata,则会在每次访问都调用它。假设运行如下代码

local metaarray = getmetatable(array.new(1)) --创建一个数组
metaarray.__index = metaarray 
metaarray.set = array.set
metaarray.get = array.get
metaarray.size = array.size

第一行创建了数组,并将它的元表赋予metaarray。然后将metaarray.__index设置为metaarray。当对size求值时,lua会尝试通过a的元表__index字段来查这个值最后就找到了array.size()

其实,在c中也可以达到相同的效果,甚至还可以做的更好现在数组是一种具有操作的对象,可以无须在table array中保存这些操作。程序库只要导出一个用于创建新数组函数new就可以了。所有其它操作都可以作为对象的方法。c代码同样可以直接注册这些方法。操作getsize,getarray和setarray无须作任何改变 唯一改变的是注册它们的方式 。首先需要设置两个独立的函数列表,一个用于常规函数,一个用于方法

Code Snippet
  1. staticconststructluaL_Regarraylib_f[] =
  2. {
  3. {"new",newarray},
  4. {NULL,NULL}
  5. };
  6. staticconststructluaL_Regarraylib_m[] =
  7. {
  8. {"set",setarray},
  9. {"get",getarray},
  10. {"size",getsize},
  11. {NULL,NULL}
  12. };

新的打开函数luaopen_array必须创建过元表,并将它赋予__index字段,然后在元表中注册所有方法,最后创建并填充array table。

Code Snippet
  1. intluaopen_array(lua_State * L)
  2. {
  3. luaL_newmetatable(L,"LuaBook.array");
  4. /*.__index = */
  5. lua_pushvalue(L,-1); //
  6. lua_setfield(L,-2,"__index");
  7. luaL_register(L,NULL,arraylib_m);
  8. luaL_register(L,"array",arraylib_f);
  9. return 1;
  10. }

其中用到了 luaL_register的另一个特性。在第一次调用中,以NULL作为库名,luaL_register不会创建任何用于存储函数的table,而是以栈顶的table作用存储函数的table。在本本例中栈顶table就是元表本身,因此lua_register会将所有方法放入其中,第二次调用luaL-register则提供了一个表名,它就根据此名倒地 一个新的table,并将指定的函数注册在这个table中。

28.4 数组的访问

另一种写法是常规的数组写法 a[i]  。由于函数setarray和getarray所接受的参数次序暗剑相应元方法的次序。因此在lua代码中可以快速的将这些元方法定义为。

local metaarray = getmetatable(array.new(1))
metaarray.__index = array.get
metaarray.__newindex=array.set
metaarray.__len=array.size

如果还要更完美,可以在c代码中注册这些方法,为此需要再次修改初始化函数

Code Snippet
  1. staticconststructluaL_Regarraylib_f[] =
  2. {
  3. {"new",newarray},
  4. {NULL,NULL}
  5. };
  6. staticconststructluaL_Regarraylib_m[] =
  7. {
  8. {"__newindex",setarray},
  9. {"__index",getarray},
  10. {"__len",getsize},
  11. {NULL,NULL}
  12. };

Code Snippet
  1. intluaopen_array(lua_State * L)
  2. {
  3. luaL_newmetatable(L,"LuaBook.array");
  4. luaL_register(L,NULL,arraylib_m);
  5. luaL_register(L,"array",arraylib_f);
  6. return 1;
  7. }

28.5 轻量级userdata (light userdata)

之前介绍的是full userdata,Lua还提供了另一种轻量级userdata(light userdata)。事实上,轻量级userdata仅仅表示的是C指针的值,即(void*)。由于它只是一个值,所以不用创建。如果需要将一个轻量级userdata放入栈中,调用lua_pushlightuserdata即可。full userdata和light userdata之间最大的区别来自于相等性判断,对于一个full userdata,它只是与自身相等,而light userdata则表示为一个C指针,因此,它与所有表示同一指针的light userdata相等。再有就是light userdata不会受到垃圾收集器的管理,使用时就像一个普通的整型数字一样

用户自定义类型《lua程序设计》 28章 笔记的更多相关文章

  1. lua程序设计 第一章习题答案

    练习1.1:运行阶乘的示例并观察,如果输入负数,程序会出现什么问题?试着修改代码来解决问题. 答:当输入负数时,循环无法终止,因为原本程序中的终止条件为n==0,而在输入为负数情况下,无法达成此终止条 ...

  2. 关于Lua程序设计{读书笔记}

    1.lua中的标识符可以是由任意字母.数字和下划线构成的字符串,但不能以数字开头.2.lua将通常类似"_VALUE"的标识符作为保留标识符3.lua的保留字 and break ...

  3. Java程序设计(2021春)——第二章笔记与思考

    Java程序设计(2021春)--第二章笔记与思考 本章概览: 面向对象方法的特征 抽象:从同类型对象中抽象出共同属性 封装:把数据和处理数据的方法封到一个类中 继承:在已有的类的基础上开发新的类 多 ...

  4. lua程序设计(一)

    摘要:lua程序设计第二版学习笔记 脚本语言的基础语法大都比较简单,这里只列举一些lua独有,或者需要特别注意的语法点. 书中前三章的内容是一些惯常的引言,基础数据类型,运算符等内容,相对简单,这里就 ...

  5. 【RL-TCPnet网络教程】第28章 RL-TCPnet之DNS应用

    第28章      RL-TCPnet之DNS应用 本章节为大家讲解RL-TCPnet的DNS应用,学习本章节前,务必要优先学习第27章的DNS基础知识.有了这些基础知识之后,再搞本章节会有事半功倍的 ...

  6. Lua 程序设计 (Roberto,Ierusalimschy 著)

    1 开始 2 类型与值 3 表达式 4 语句 5 函数 6 深入函数 7 迭代器与泛型for 8 编译,执行与错误 9 协同程序(coroutine) 10 完整的示例 11 数据结构 12 数据文件 ...

  7. 《APUE》第四章笔记(1)

    1.引言 本章介绍文件系统的特征和文件的性质.从stat函数开始,逐个说明stat结构的每一个成员以了解文件的所有属性.在此过程中,还将会说明修改这些属性的各个函数,并更详细地查看UNIX文件系统的结 ...

  8. 《APUE》第三章笔记(3)

    文件共享 UNIX系统支持在不同进程中共享打开的文件,首先先用一幅apue的图来介绍一下内核用于I/O文件的数据结构: 如图所见,一个进程都会有一个记录项,记录项中包含有一张打开文件描述符表,每个描述 ...

  9. Lua编程入门-学习笔记1

    第1章:起点 Chunks: 语句块 每个语句结尾的分号是可选的,如果同一行有多个语句最好使用分号分隔: dofile("lib1.lua")  -- 执行lua文件 全局变量:局 ...

随机推荐

  1. ASP.NET中怎样将页面设为首页,加入收藏

    1.文字js脚本事件:<span onClick="var strHref=window.location.href;this.style.behavior=’url(#default ...

  2. String.Format,DateTime日期时间格式化

    DateTime dt = DateTime.Now;//2010年10月4日 17点05分            string str = "";            //st ...

  3. 洛谷 P1598 垂直柱状图【字符串】

    题目描述 写一个程序从输入文件中去读取四行大写字母(全都是大写的,每行不超过72个字符),然后用柱状图输出每个字符在输入文件中出现的次数.严格地按照输出样例来安排你的输出格式. 输入输出格式 输入格式 ...

  4. 模板—算法—整体二分(区间k小值)

    模板—算法—整体二分(区间k小值) Code: #include <cstdio> #include <algorithm> using namespace std; #def ...

  5. Kattis - boxes (dfn序)

    Boxes There are N boxes, indexed by a number from 1 to N . Each box may (or not may not) be put into ...

  6. 1357:车厢调度(train)

    [题目描述] 有一个火车站,铁路如图所示,每辆火车从A驶入,再从B方向驶出,同时它的车厢可以重新组合.假设从A方向驶来的火车有n节(n≤1000),分别按照顺序编号为1,2,3,…,n.假定在进入车站 ...

  7. struts2 action 字段问题

    struts2最多只能解释两级字段,比如user.username,像user.info.age在类中属性类的三段字符不能识别,只能先用user,info 然后在user.setInfo(info);

  8. [BZOJ 1789] Necklace

    Link: BZOJ 1789 传送门 Solution: 感觉$n\le 50$可以随便乱搞啊…… 这里我是先找到3条链的失配位置,再找到这之后其中2条链最远的失配位置,统计即可 Code: #in ...

  9. CodeForces - 981D Bookshelves

    Discription Mr Keks is a typical white-collar in Byteland. He has a bookshelf in his office with som ...

  10. 【强联通分量缩点】【最短路】【spfa】bzoj1179 [Apio2009]Atm

    缩点后转化成 DAG图上的单源最长路问题.spfa/dp随便. #include<cstdio> #include<queue> #include<algorithm&g ...