《Programming in Lua 3》读书笔记(二十六)

日期:2014.8.12

PartⅣ The C API

30 Managing Resources

上一节中实现的自定义类型,我们并没有关注于资源管理的问题。上一节实现的数组是需要关心内存问题,而这些问题由Lua实现管理。但是很多时候事情不那么简单,有些对象不仅需要内存空间,还会需要如窗口句柄、文件描述等资源。尽管说这些也是内存开销,但是这些资源是由系统的其他组件管理的。这种情况下,当一个对象被回收了,我们也需要合适的机制来实现回收这些额外的资源。

在17.6章节中,介绍了Lua提供的finalizer,由__gc 元方法所组成的一种机制。为了能在C和API中也能正常的进行资源管理,本章将会从Lua中扩展两个机制。第一个例子是一个函数遍历一个目录的实现;第二个例子是Expat的绑定,一个打开XML资源的解析器。

30.1 A Directory Iterator

之前已经实现过一个类似的dir函数,函数返回一个包含给定目录下所有文件的table。在这里我们要实现每次调用返回一个迭代器,代表一个条目(相当于之前table中的一个元素),这样我们就可以用以下的方式实现遍历一个目录:

e.g.

for fname in dir.open(".") do

print(fname)

end

在C中要实现遍历一个目录,此时需要一个DIR的数据结构。而且数据结构DIR的实例需要由opendir创建,而且必须在调用closedir的时候立即释放该实例。之前介绍的实现dir函数是将DIR的实例作为一个局部变量存储,然后在得到最后一个文件名字后释放该实例。然后在这里,不能将DIR的实例以局部变量来存储,这里考虑的是将会有多处的访问会访问该实例。不仅如此,也不能仅是在得到最后一个文件名字的时候做释放工作,因为假如程序中途退出了循环,那么就会不会再有释放了。所以,这里将DIR的实例地址以一个userdataum的形式存储,然后使用__gc
元方法来管理这个userdataum,用来释放这个资源。

需要借助三个C函数来实现我们的需求:1,需用dir.open 函数,Lua用来创建迭代器,这个函数用来打开一个DIR 结构,然后创建一个将这个结构以upvalue的形式的closure;2,一个iterator 函数;3,__gc 元方法,用来释放资源。当然也需要一个额外的函数用来做参数的初始化工作。

首先,dir.open 函数。这里要注意的是,一定要在打开目录之前创建好userdatum,因为如果顺序反了的话,会引发内存错误。在顺序正确的基础上,一创建好DIR 结构,就立马与userdatum链接起来了,之后__gc 元方法就会接管对其的释放工作。

然后,dir_iter函数,函数从upvalue中得到DIR结构的地址,然后调用readdir读取下一个条目。

接着,函数dir_gc 代表 __gc 元方法。这个方法关掉目录,但是这里要注意一点:因为是在打开目录之前先创建好了userdatum,所以无论opendidr的结果是如何,都会对userdatum进行回收。

最后,luaopen_dir 函数,是打开这个库的函数。

都是C函数形式实现的。

30.2 An XML Parser

这里介绍的是简单的实现Lua和Expat的绑定,叫做lxp。Expat是一个开源的用C写的XML1.0解析器。该解析器实现为SAX(the Simple API for XML)。SAX是一个基于API的事件分发器,即SAX解析器读取一行XML文档,然后通过回调来通知程序读取到的信息。如我们使用Expat解析一个字符串"<tag cap="5">hi</tag>",将会生成三个事件:当读取子串"<tag
cap-"5>"生成一个 start-element 事件;读取到"hi"的时候生成text event 事件;读取到"</tag>"的时候生成endelement event 事件。每个事件在程序中对应一个相应的回调事件。

Expat有非常多的事件,在这里主要是介绍上面提到的三个事件。

首先,介绍创建和清除Expat解析器的函数:

e.g.
XML_Parser XML_ParserCreate(const char *encoding);     /* 创建 */
void XML_ParserFree(XML_Parser p);                              /* 清除 */

第一个函数中的参数 encoding 是可选的,这里使用NULL。

得到了解析器之后,就是需要注册回调句柄了:

void XML_SetElementHandler(XML_Parser p,
                                             XML_StartElementHandler start,     /* start 事件 */
                                             XML_EndElementHandler end);      /* end 事件 */
void XML_SetCharacterDataHandler(XML_Parser p, XML_CharacterDataHandler hndl); /* text 事件 */

所有的回调句柄接受user data 作为第一个参数。而且start-element 句柄也接受tag的名字和tag的属性作为其参数。

typedef void (*XML_StartElementHandler) (void *uData,const char *name,const char **atts);

而end-element句柄则只接受一个额外的参数,tag的名字:

typedef void(*XML_EndElementHandler)(void *uData,const char *name);

text 句柄则接受的是text作为其参数加上这个text的长度:

typedef void(*XML_CharacterDataHandler)(void *uData,const char *s,int len);

传递要解析的信息给解析器,用以下这个函数:

int XML_Parse(XML_Parser p,const char *s,int len,int isLast);

Expat通过连续调用XML_Parse,以块来接受需要解析的文档。最后一个参数isLast表示每次解析的块是否是文档中最后的一块。我们这里使用了每个块的实际长度作为参数,所以我们不需要给每个块做0终止符标记。该函数在遇到解析错误的时候将会返回0。

最后的函数便是,设置user data,传递给每个句柄用的:

void XML_SetUserData(XML_Parser p ,void *uData);
时间: 2024-10-15 10:01:47

《Programming in Lua 3》读书笔记(二十六)的相关文章

《Programming in Lua 3》读书笔记(二十二)

日期:2014.8.6 PartⅣ The C API 26 Extending Your Application 使用Lua很重要的一点是用来做配置语言.配合主语言做一些功能的配置. 26.1 The Basics 有的时候程序需要配置一些功能信息,很多时候可能有许多别的方法比用lua做配置要更简单:如使用环境变量或者读取文件,读取文件涉及到文件的解析.如果使用Lua进行配置的话,相当于用lua文件替代了要读取的如csv.txt文件等. 使用Lua进行配置的时候,就需要使用Lua API去控制

【Unity 3D】学习笔记二十六:unity游戏脚本(六)

在3D游戏世界中,任何一个游戏对象在创建的时候都会附带Transform(变换)组件,并且该组件是无法删除的,也不应该删除.在unity中,Transform面板一共有3个属性: Position  (位置) Rotation(旋转) Scale(缩放) 这三个值都是用来调整游戏对象在游戏界面中的位置,状态等相关参数. Position  (位置) 任何一个游戏对象的三维坐标都保存在Vector3容器中,该容器记录对象在X轴,Y轴,Z轴的坐标.一旦Vector33容器中的坐标发生变化,那么Sce

angular学习笔记(二十六)-$http(4)-设置请求超时

本篇主要讲解$http(config)的config中的timeout项: $http({ timeout: number }) 数值,从发出请求开始计算,等待的毫秒数,超过这个数还没有响应,则返回错误 demo: html: <!DOCTYPE html> <html ng-app = 'HttpGet'> <head> <title>18.4 $http(2)</title> <meta charset="utf-8"

马哥学习笔记二十六——MySQL主从复制

配置MySQL复制基本步骤: 一.master 1.启用二进制日志 log-bin = master-bin log-bin-index = master-bin.index 2.选择一个惟一server-id server-id = {0-2^32} 3.创建具有复制权限的用户 REPLICATION SLAVE REPLICATION CLIENT 二.slave 1.启用中继日志 relay-log = relay-log relay-log-index = 2.选择一个惟一的server

《Programming in Lua 3》读书笔记(二十五)

日期:2014.8.11 PartⅣ The C API 29 User-Defined Types in C 在之前的例子里,已经介绍过如果通过用C写函数来扩展Lua.在本章,将会介绍通过用C写新的类型来扩展Lua,将会使用到元方法等特性来实现这个功能. 以一个例子来介绍本章将要介绍的,例子实现的功能是实现了一个简单的类型:boolean arrays.实现这个功能主要是这种方法不需要太复杂的算法,因此可以将精力放在API的讨论上.当然我们可以在Lua中用一个table来实现,但是用一个C来实

《Programming in Lua 3》读书笔记(二十八)

日期:2014.8.15 PartⅣ The C API 32 Memory Management Lua是动态的对其数据结构进行管理的.所有数据结构按需求进行增长,最终进行释放.Lua对其内存的使用控制较为严格,当我们关闭了一个Lua state的时候,Lua会立即释放其内存占用.不仅如此,Lua中的所有对象都被垃圾回收器管理,包括tables,strings,和functions,threads,和modules. Lua进行内存管理的方式对多数程序来说都是挺合适的.但是还是有一些特殊的程序

《Distributed Programming With Ruby》读书笔记二 Security and ID Conversion (Part1.1-2)

Security Although DRb provide some security, they fall short of a full, comprehensive solution. This can make DRb less than desirable in a lot of real world situations. However, in situations where security is a lesser concern,such as prototyping and

Android笔记二十六.Android异步任务处理(AsyncTask)

转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 一.引言 我们知道Android的UI线程主要负责处理用户的按键事件.用户触屏事件及屏幕绘图事件等,对于其他的操作尽量不要在UI线程中实现,因为这些操作很有可能会阻塞UI线程,比如一些耗时操作,会导致UI界面停止响应,从而降低了用户的体验.所以,为了避免UI线程失去响应的问题,Android建议将耗时操作放在新线程中完成,但新线程也可能需要动态更新UI组件:比如需要从网上获取一个网页,然后在Te

《C++ Primer》读书笔记—第十六章 模板与泛型编程

---恢复内容开始--- 声明: 文中内容收集整理自<C++ Primer 中文版 (第5版)>,版权归原书所有. 学习一门程序设计语言最好的方法就是练习编程 1.面向对象编程和泛型编程都是处理在编写程序时不知道类型的情况,不同之处在于,OOP能处理类型在程序运行之前都未知的情况,而在泛型编程中,在编译时就能获知类型了. 2.泛型编程与面向对象编程一样,都依赖于某种形式的多态性.面向对象编程中的多态性在运行时应用于存在继承关系的类. 3.在泛型编程中,我们所编写的类和函数能够多态地用于跨越编译