自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(94)
  • 收藏
  • 关注

原创 Lua程序设计(九十)

一个通用的调用函数下例是一个更高级的示例,我们将编写一个调用 Lua 函数的包装程序,其中用到了 C 语言的 stdarg 的机制。这个包装函数名为 call_va, 它接受一个待调用的全局函数的名字,一个描述参数类型和结果类型的字符串、参数列表、以及存放结果的一组指向变量的指针。函数 call_va 会处理有关 API 的所有细节。#include <stdarg.h>voi...

2019-05-16 22:47:42 196 1

原创 Lua程序设计(八十九)

调用 Lua 函数Lua 语言的一大优势在于允许在一个配置文件中定义应用所调用的函数。例如,我们可以用 C 语言编写一个应用来绘制某个函数的图形,并用 Lua 定义要绘制的函数。调用 Lua 函数的 API 规范很简单:首先,将待调用的函数压入栈中;然后,压入函数的参数;接着用 lua_pcal 进行实际的调用;最后,从栈中取出结果。举一个例子来讲,假设配置文件中有如下的函数:functi...

2019-05-14 21:35:27 218 1

原创 Lua程序设计(八十八)

尽管 Lua 语言的 C API 追求简洁性,但 Lua 也没有做的过于激进。因此, C API 为一些常用的操作提供了一些便利的方法。由于通过字符串类型来检索表是很常见的操作,因此 Lua 语言针对这种情况提供了一个特定版本的 lua_gettable 函数:lua_getfield。使用这个函数,可以将 getcolorfield 中的如下两行代码:lua_pushstring(L,key...

2019-05-13 22:42:12 289 1

原创 Lua程序设计(八十七)

继续之前的拓展上一个示例,为用户引入颜色的名字。用户除了可以使用颜色表,还可以使用更多常用颜色的预定义名字。要实现这个功能,就需要在 C 程序中就要有一张颜色表:struct ColorTable{ char *name; unsigned char red, green, blue;} colortable[] = { {"WHITE", MAX_COLOR, MAX_COLOR, M...

2019-05-06 22:52:51 172 1

原创 Lua程序设计(八十六)

操作表假设要为每个窗口配置一种背景色。最终的颜色格式是由三个数字分量组成的 RGB 颜色。它们各自都位于区间0-1之间。一种直接的方法是要求用户用不同的全局变量设置每个分量://配置文件width = 200height = 300background_red = 0.30background_green = 0.10background_blue = 0.0这个方法有两大缺点...

2019-05-05 22:47:59 182 1

原创 Lua程序设计(八十五)

扩展应用Lua 的重要用于之一就是用作配置(configuration) 语言。基础知识假设我们的 C 程序有一个窗口,并希望用户能够指定窗口的初始大小。我们可以使用环境变量或使用基于键值对的配置文件。但即便是一个简单的文本文件,我们需要对其进行进行解析。因此,我们决定使用一个 Lua 配置文件。它可以包含如下简单内容:width = 200height = 300现在,我们使用 L...

2019-04-22 22:49:16 155 1

原创 Lua程序设计(八十四)

内存分配Lua 语言核心对内存分配不进行任何假设,它既不会调用 malloc 也不会调用 realloc 来分配内存。相反,Lua 语言核心只会通过一个分配函数(allocation function)来分配和释放内存,当用户创建 Lua 状态时必须提供该函数。luaL_newstate 是一个用默认分配函数来创建 Lua 状态的辅助函数。该默认分配函数使用了来自 C 语言标准函数库的标准函数...

2019-04-17 22:53:18 217 1

原创 Lua程序设计(八十三)

处理库代码中的错误Lua 是一种安全的语言,这意味着不管用 Lua 写什么,也不管写出来的内容有多么不正确,Lua 自身所提供的机制总是能用来理解程序的行为。此外,程序中的错误(error) 也是通过 Lua 语言的机制来检测和解释的。与之相对的是,许多 C 语言代码中的错误只能从底层硬件的角度来解释(例如,把异常位置作为指令地址给出)。一旦我们向 Lua 语言中加入新的 C 函数,这种安全性...

2019-04-16 22:52:28 143 1

原创 Lua程序设计(八十二)

使用 C API 进行错误处理Lua 中所有的结构都是动态的:他们会按需扩展,并且在可能时最后重新收缩。这意味着 Lua 中内存分配失败可能无处不在,几乎所有的操作最终都可能面临内存分配失败。此外,许多操作也可能抛出异常。Lua 语言使用以异常来提示错误,而没有在 API 的每个操作使用错误码。Lua 使用了 C 语言中的 setjmp 机制来解决这个问题,setjmp 营造了一个类似异常处理...

2019-04-15 22:44:02 112 1

原创 Lua程序设计(八十二)

其他栈操作除了上述在 C 语言和栈之间交换数据的函数外,C API 还提供了下列用于通用栈操作的函数int lua_gettop(lua_State *L);void lua_settop(lua_State *L, int index);void lua_pushvalue(lua_State *L, int index);void lua_rotate(lua_State *L, i...

2019-04-11 22:54:22 413 1

原创 Lua程序设计(八十一)

查询元素C API 使用索引来引用栈中的元素。第一个被压入栈的元素索引为1,第二个为2,依此类推。我们也可以用站定作为参照,使用负数来访问栈中的元素。要检查栈中的元素是否为特定的类型,C API 提供了一系列名为 lua_is* 的函数,其中 * 可以是任意的一种 Lua 类型。int lua_is*(lua_State *L, int index);//这样的函数有lua_isnil, ...

2019-04-10 22:42:29 269 1

原创 Lua程序设计(八十)

压入元素针对每一种能用 C 语言直接表示的 Lua 类型, C API 都有一个对应的压栈函数:常量 nil 使用 lua_pushnil; 布尔值使用 lua_pushboolean;双精度浮点数使用 lua_pushnumber; 整形使用 lua_pushinteger; 任意字符串(一个指向 char 的指针,外加一个长度) 使用 lua_pushlstring; 以\0终止的字符串使用...

2019-04-08 23:02:22 161 1

原创 Lua程序设计(七十九)

栈Lua 和 C 之间通信的主要组件是无处不在的虚拟栈( stack ), 几乎所有的 API 调用都是在操作这个栈中的值, Lua 与 C 之间所有的数据交换都是通过这个栈完成的。此外,还可以使用栈来保存中间结果。在 Lua 和 C 之间交换数据时,会面对两个问题:1.动态类型和静态类型体系之间不匹配;2.自动内存管理和手动内存管理之间不匹配。在 Lua 中,t[k] = v, k 和 v...

2019-04-02 22:58:28 161 1

原创 Lua程序设计(七十八)

#include <stdio.h>#include <string.h>#include "lua.h"#include "lauxlib.h"#include "lualib.h"int main (void) { char buf[256]; int error; lua_State *L = luaL_newstate(); luaL_openL...

2019-04-01 22:48:02 195 1

原创 Lua程序设计(七十七)

C语言API总览Lua 是一种嵌入式语言( embedded language),这就意味着 Lua 并不是一个独立运行的应用。而是作为一个库,链接到其它应用程序,从而达到融入 Lua 功能的效果。同时,使用了 Lua 语言的程序也可以在 Lua 环境中注册新的函数,比如用 C 语言(或其它语言)实现的函数,从而增加一些无法直接用 Lua 语言编写的功能,因此 Lua 语言同时是一种可扩展语言...

2019-03-29 22:53:58 175 1

原创 Lua程序设计(七十六)

使用协程实现多线程协程能够实现一种协作式多线程。每个协程都等于一个线程。一对 yield-resume 可以将执行权在不同线程之间切换。不过,与普通的多线程不同的是,协程是非抢占的。当一个协程正在运行时,是无法从外部停止的。只有当协程显式要求即通过调用函数 yield, 它才会挂起执行。不过,对于非抢占式多线程来说,只要有一个线程调用了阻塞操作,整个程序在该操作完成前都会阻塞。有一个有趣的方法...

2019-03-28 23:00:17 139 1

原创 Lua程序设计(七十六)

沙盒(Sandbox)之前我们已经看到函数 load 在受限的环境中运行 Lua 代码是非常简单的。由于 Lua 语言通过库函数完成与外部世界的通信,因此一旦移除了这些函数也就排除了一个脚本能够影响外部环境的可能。尽管如此,我们仍然可能会被消耗大量CPU时间或内存的脚本进行拒绝服务(DoS)攻击。反射,以调试钩子的形式,提供了一种避免这种攻击的有趣方式。首先,我们使用 count 时间钩子来...

2019-03-27 22:57:34 250

原创 Lua程序设计(七十五)

调优(Profile)反射的另外一个常见的用法是用以调优,即程序使用资源的分析。对于事件相关的调优,最好使用 C 接口,因为每次调用钩子函数开销太大从而可能导致测试结果无效。性能调优工具的主要数据结构是两个表,其中一个表将函数和它们的调用计数关联起来,另一个表关联函数和函数名。local Counters = {}local Names = {}我们可以选择在性能分析完成后再获取函数的...

2019-03-26 23:09:06 132 2

原创 Lua程序设计(七十四)

钩子调式库中的钩子机制允许用户注册一个钩子函数,这个钩子函数会在程序运行中某个特定时间发生时被调用。有四种事件能够触发一个钩子:每当调用一个函数时产生的 call 事件。每当函数返回时产生的 return 事件。每当开始执行一行新代码时产生的 line 事件。-执行完指定数量的指令后产生的 count 事件。(这里的指令指的是内部操作码。)Lua 语言用一个描述导致钩子...

2019-03-25 22:54:32 207 1

原创 Lua程序设计(七十三)

25.1.3. 访问其他协程调式库的所有自省函数都能够接受一个可选的协程作为第一个参数, 这样就可以从外部来检查这个协程。co = coroutine.create(function () local x = 10 coroutine.yield() error("some error")end)coroutine.resume(co)print(debug.traceback(...

2019-03-20 22:46:02 104 1

原创 Lua程序设计(七十二)

访问非局部变量调试库还提供了函数 getupvalue, 该函数允许我们访问一个被 Lua 函数所使用的非局部变量。与局部变量不同,被一个函数所调用的非局部变量即使在引用它的函数已经不活跃的情况下也会一直存在(闭包的实质)。因此,函数 getupvalue 的第一个参数不是栈层次,而是一个函数(更确切地说,是一个闭包)。函数 getupvalue 的第二个参数时变量索引,Lua 语言按照函数引用...

2019-03-19 22:04:56 151 1

原创 Lua程序设计(七十一)

访问局部变量通过函数 debug.getlocal 来检查任意活跃函数的局部变量。该函数有两个参数,一个是要查询的栈层次,另一个则是变量的索引。该函数返回两个值,变量名和变量的当前值。如果变量索引大于活跃变量的数量,那么函数 getlocal 返回 nil。如果栈层次无效,则会抛出异常(可以使用函数 debug.getinfo 来检查栈层次是否有效。)Lua 语言按局部变量在函数中出现的顺序对...

2019-03-18 22:40:31 105 1

原创 Lua程序设计(七十)

反射(Reflection)反射是程序用来检查和修改其自身某些部分的能力。Lua这样的动态语言支持几种反射机制:环境允许运行时观察全局变量;诸如type和pairs这样的函数允许运行时见检查和遍历未知的数据结构。诸如load和require这样的函数允许程序在自身中追加代码或更新代码。不过仍有缺失的部分,如程序不能检查局部变量,开发人员不能跟踪代码执行,函数也不知道是被谁调用的。调试库(debu...

2019-03-14 22:52:10 177

原创 Lua程序设计(六十九)

事件驱动式编程传统的事件驱动编程伴随的典型问题就衍生自who-is-the-boss问题。在典型的事件驱动平台下,一个外部的实体向我们程序中的事件循环(event loop)或运行循环(run loop)生成事件。我们的程序变成了事件循环的附属品,使得我们的程序成为了一组无须任何显式关联的,相互独立的事件处理程序的集合。假设有一个与libuv类似的异步I/O库,该库中有四个与我们的示例有关的函...

2019-03-13 22:56:17 258

原创 Lua程序设计(六十八)

将协程用作迭代器循环迭代器可以视为生产者-消费者模式的一种特例:一个迭代器会生产由循环体消费的内容。因此,用协程来实现迭代器也是自然的一种方式。同时,协程最关键的特性就是能够颠倒调用者与被调用这之间的关系。有了这种特性,我们在编写迭代器时就无须担心如何保存连续调用之间的状态了。要编写这样一种迭代器并不容易,所以我们选择编写一个递归函数来产生所有的排列。思路也很简单,只要依次将每个数组元素放到最...

2019-03-12 22:50:20 104 1

原创 Lua程序设计(六十七)

哪个协程占据主循环协程的一个经典问题就是生产者-消费者问题。在生产者-消费者问题中涉及两个函数,一个函数不断地产生值(比如,从一个文件中读取),另一个函数不断地消费这些值(比如,将值写入另一个文件中)。function producer () while true do local x = io.read() send(x) endendfunction consumer()...

2019-03-11 22:48:45 88 1

原创 Lua程序设计(六十六)

需要注意的是,像函数pcall一样,函数resume也运行在保护模式中。因此,如果协程在执行过程中出错,Lua语言将不会显示错误信息,而是将错误信息返回给函数resume。当协程A唤醒协程B时,协程A既不是挂起状态(因此不能唤醒A),也不是运行状态(因为正在运行的协程是B)。所以,协程A此时的状态被称为正常状态。Lua 语言中一个非常有用的机制是通过一对resume-yield来交换数据。第一...

2019-03-08 20:31:40 129 1

原创 Lua程序设计(六十五)

协程(Coroutine)协程 可以颠倒调用者和被调用者的关系,而且这种灵活性解决了软件架构中的被称为“谁是老大(who-is-the-boss)” 或者“谁拥有主循环(who-has-the-main-loop)”的问题。 这是对诸如事件驱动编程\通过构造器构建迭代器和协作式多线程等几个表面不相关的问题的泛化,而协程以简单有效的方式解决了问题。从多线程角度来看,协程和线程类似:协程是一系列的...

2019-03-07 22:47:51 82

原创 Lua程序设计(六十四)

控制垃圾收集的步长通过函数collectgarbage可以对垃圾收集器进行一些额外的控制,该函数实际上是几个函数的集合体:第一个参数是可选的字符串,用来说明进行何种操作;有些选项使用一个整形作为第二个参数,称为data。第一个参数的选项有以下七个。”stop": 停止垃圾收集器,使用选项"restart"再次调用collectgarbage。“restart”: 重启垃圾收集器。”col...

2019-03-06 22:47:56 98 1

原创 Lua程序设计(六十三)

垃圾收集器在Lua5.0之前, Lua语言使用的都是一个简单的标记-清楚(mark-and-sweep)式垃圾收集器(Garbage Collector GC)。这种收集器又被称作“stop-the-world(全局暂停)式的收集器,意味着Lua语言会时不时地停止主程序的运行来执行一次完整的垃圾收集周期(garbage-collection style)。 每一个垃圾收集周期由四个阶段组成:标记...

2019-03-05 22:53:15 130 1

原创 Lua程序设计(六十二)

有关析构器的另一个微妙之处在于复苏(resurrection)。当一个析构器被调用时,它的参数是正在被析构的对象。因此,这个对象会至少在析构期间重新变成活跃的。这通常被称为临时复苏(transient resurrection)。在析构器进行期间,我们无法阻止析构器把该对象存储在全局变量中,使得该对象在析构器返回后仍可访问,这就被称为永久复苏(permanent resurrection)。复苏...

2019-03-01 22:41:48 155

原创 Lua程序设计(六十)

瞬表 (Ephemeron Table)有一种棘手的情况是,一个具有弱引用键的表中的值又引用了对应的键。这种情况比看上去的更加常见。一个典型的示例是常量函数工厂( constant-function factory)。 这种工厂的参数是一个对象,返回值则是一个被调用时返回传入对象的函数:function factory (o) return ( function () return o e...

2019-02-27 22:46:58 235 1

原创 Lua程序设计(五十九)

回顾具有默认值的表如何实现具有非nil默认值的表。我们将要看到的两种实现这个默认值的技术是对偶表示和记忆这两种通用技术的特例。第一种解决方案中,我们使用一个弱引用表来映射每一个表和它的默认值:local defaults = {}setmetatable(defaults, {__mode = "k"})local mt = {__index = function (t) return ...

2019-02-26 22:45:26 150 1

原创 Lua程序设计(五十八)

记忆技术(memorization technique)还可以用来确保某类对象的唯一性。例如,假设一个系统用具有三个相同取值范围的字段red、green和blue的表来表示颜色,一个简单的颜色工厂函数每被调用一次就生成一个新颜色:function createRGB (r, g, b) return {red = r, green = g, blue = b}end使用记忆技术,我们就...

2019-02-25 22:46:55 134 1

原创 Lua程序设计(五十七)

记忆函数一种常见的编程技巧是用空间换时间。我们可以通过一种叫做记忆函数的函数来执行结果,在后续使用相同参数再次调用该函数时直接返回之前记忆的结果,来加快函数的执行速度。假设我们有一个通用的服务器,该服务器接受的请求是以字符串形式表示的Lua语言代码。每当服务器接收到一个请求时,它就对字符串运用load函数,然后再调用编译后的函数。不过,我们知道,函数load的开销很昂贵,而且发送给服务器的某些...

2019-02-22 22:44:28 163

原创 Lua程序设计(五十六)

弱引用表(二)一个表是否为弱引用表是由其元表中的 __mode字段决定的。当这个字段存在时,其值应为一个字符串:如果这个字符串是"k", 那么这个表的键是弱引用的;如果这个字符串是"v",那么这个表的值是弱引用的;如果这个字符串是"kv",那么这个表的键和值都是弱引用的。以下是一个简单示例:a = {}mt = {__mode = "k"}setmetatable(a, mt)key ...

2019-02-21 22:39:29 103 1

原创 Lua程序设计(五十五)

垃圾收集Lua 语言使用自动管理。程序可以创建对象(表、闭包等),但却没有函数来删除对象。Lua 语言通过垃圾收集自动地删除成为垃圾的对象,从而将程序员从内存管理的绝大部分负担中解放出来。同时对于无效指针(dangling pointer) 和内存泄漏(memory leak) 等问题。弱引用表(weak table)、析构器( finalizer) 和函数 collectgarbage 是在...

2019-02-20 22:55:30 130 1

原创 Lua程序设计(五十四)

环境和模块之前我们提到过模块的缺点之一在于很容易污染全局空间,例如在私有声明中忘记 local 关键字。环境为解决这个问题提供了一种有趣的方式。一旦模块的主程序块有一个独占的环境,则不仅该模块的所有函数共享了这个环境,该模块的全局变量也进入到了这个环境中。我们可以将所有的公有函数声明为全局变量,这样它们就会自动地进入分开的环境中。模块所要做的就是将这个环境赋值给变量 _ENV。之后,当我们声明...

2019-02-19 22:41:22 102 1

原创 Lua程序设计(五十三)

使用 _ENV由于 _ENV 的使用给我们带来了许多灵活性的手段。由于 _ ENV 只是一个普通的变量,因此可以对其进行赋值或像访问其他变量一样访问它。_ENV = nil这句赋值语句将使得后续代码不能直接访问全局变量。这可以用来控制代码使用哪种变量local print, sin = print, math.sin_ENV = nilprint(13) --&gt; 13pr...

2019-02-15 22:51:37 154 1

原创 Lua程序设计(五十二)

非全局环境在Lua语言中,全局变量不一定非得是真正全局的。Lua语言中甚至都根本没有全局变量。但是Lua语言尽力让程序员有全局变量存在的幻觉。那么Lua语言是如何模拟全局变量的存在的呢?首先,让我们从自由名称的概念开始讨论。一个自由名称是指没有关联到显式声明上的名称,即它不出现在对应局部变量的范围内。local z = 10x = y + zx 和 y 是自由名称,而 z不是。接下啦...

2019-02-14 22:59:10 168 1

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除