Lua的require小结
在游戏开发中会经常使用到lua作为游戏逻辑层的脚本语言,各种优势就不说了,虽然平时用的比较多,但对lua语言本身和内部的一些实现并不是很了解,让我们先从lua的require入手来一探require的各种用法吧。
require其实类似与C/C++中的#include,就是加载一个指定名称的模块进来,该模块可以来自于lua,也可能来自于C/C++,在lua虚拟机启动时,默认会给我们预先加载好一些模块,保存在package.loaded
中,我们可以实际打印一波看看:
for k, v in pairs(package.loaded) do
print(k, v)
end
可以看到预先加载好的模块名称,一目了然。那么,lua又是从哪些地方去加载模块呢?加载模块又有什么规则呢?这个就是由package.path
指定,同样可以实际打印一波看看:
如果我们希望修改lua加载模块的路径,只要修改这个package.path
就可以了。
让我们回到前面打印的package.loaded
结果,我们发现这个table的value都是table,这不禁让人好奇:require的返回值是什么呢?我们可以自己写一个简单的自定义模块去验证下:
--mypackage.lua
print("hello world")
然后,执行:
require("mypackage")
for k, v in pairs(package.loaded) do
print(k, v)
end
诶,发现自定义模块返回的值是true。这是为什么呢?明明我们的代码里没有任何一句return语句。难道是在没有写返回值的情况下,默认给我们返回true了?那既然如此,手动显式加一句return试试:
--mypackage.lua
print("hello world")
return nil
然后require,我们发现结果还是一样,返回值为true:
其实,lua之所以这么做,是为了避免重复加载同一个模块,每加载一个模块,就将模块的name作为key,模块的返回值(如果有且不为nil)作为value插入到package.loaded
中去。这样下次再去加载这个模块时,就无需加载再去执行该模块的代码,直接返回package.loaded
对应key的value即可。怎么验证呢?我们可以尝试require一个模块两次试试:
require("mypackage")
require("mypackage")
注意到,hello world只被打印了一次,说明第二次require的时候并没有执行mypackage中的代码。require内部实际上是调用了loadfile接口来进行模块加载,loadfile的返回值是一个函数,执行该函数,相当于执行该模块的代码:
f = loadfile("D:/lua/mypackage.lua")
f()
那么,有没有办法让重复require时都去执行模块的代码呢?答案是显而易见的,只要将package.loaded
中对应的key删掉就可以了:
require("mypackage")
package.loaded.mypackage = nil
require("mypackage")
有意思的是,如果我们的模块返回值为false,或者我们设置package.loaded.mypackage = false
时,无论require多少次,都会触发模块的加载执行。不过根据我们之前的验证,这也是符合情理的hhh。说到这里,其实我们就可以自己写一个简单的require了:
function require_ex(module)
if package.loaded[module] then
return package.loaded[module]
end
for pattern in string.gmatch(package.path, '[^;]+%?[^;]+') do
local path = string.gsub(pattern, '%?', module)
local fp = loadfile(path)
if fp then
local ret = fp()
if ret ~= nil then
package.loaded[module] = ret
else
package.loaded[module] = true
end
return package.loaded[module]
end
end
end
有时候,我们希望require进来的模块是不允许定义全局变量的,因为全局变量会污染我们整个环境,并可能造成意想不到的后果,在lua 5.1,我们可以使用setfenv
函数来设置函数环境,而在lua 5.2以上版本,则可以通过修改env参数来解决,loadfile
的第三个参数就是函数环境:
local env = {}
setmetatable(env, {__index = _G, __newindex = function(t, k, v) print("forbidden global var ", k) end})
local fp = loadfile(path, nil, env)
如果lua在package.path
中找不到对应的lua模块,那么接下来它会尝试从C++模块中加载,类似地,C++路径是由package.cpath
指定的:
针对dll,require内部是使用package.loadlib
方法实现的,它接受两个参数,一是模块的路径,二是给lua调用的函数名称(lua_openxxx)。其他的就基本和前面加载lua模块一致了,完整的require_ex
代码如下:
function require_ex(module)
if package.loaded[module] then
return package.loaded[module]
end
for pattern in string.gmatch(package.path, '[^;]+%?[^;]+') do
local path = string.gsub(pattern, '%?', module)
local env = {}
setmetatable(env, {__index = _G, __newindex = function(t, k, v) print("forbidden global var ", k) end})
local fp = loadfile(path, nil, env)
if fp then
local ret = fp()
if ret ~= nil then
package.loaded[module] = ret
else
package.loaded[module] = true
end
return package.loaded[module]
end
end
for pattern in string.gmatch(package.cpath, '[^;]+%?[^;]+') do
local path = string.gsub(pattern, '%?', module)
local fp = package.loadlib(path, "luaopen_" .. module)
if fp then
local ret = fp()
if ret ~= nil then
package.loaded[module] = ret
else
package.loaded[module] = true
end
return package.loaded[module]
end
end
end
如果你觉得我的文章有帮助,欢迎关注我的微信公众号(大龄社畜的游戏开发之路)-
Lua的require小结的更多相关文章
- Lua的require和module小结
Lua的require和module小结 module特性是lua5.1中新增的,用于设置Lua文件自己的模块,最常用的方式是module(name,package.seeall),有时候lua文件 ...
- Lua 字符串函数小结
1.求字符串长度 string.len(str) 2.大小写转换 string.upper(str) string.lower(str) 3.字符串查找(非全局) --func_string.lua ...
- Lua脚本认知小结
0.前言 Lua是一种脚本语言,笔者在学习cocos2dx的时候认识了这个脚本语言. 据个人了解的脚本语言最大的优势是无需编译,使用其内核可以使其跨平台运行. JavaScript,Python,Pe ...
- Lua中的require
lua中的require机制 为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来.现在看看lua的require的处理流程.1.require机制相关 ...
- Lua Require函数
转自:http://www.cppblog.com/cslover/archive/2013/12/21/204934.html Lua提供高级的require函数来加载运行库.粗略的说require ...
- 【转载】lua中的require机制
[转载自]http://blog.chinaunix.net/uid-552961-id-2736410.html lua中的require机制 为了方便代码管理,通常会把lua代码分成不同的模块,然 ...
- Lua中的require(转)
lua中的require机制 为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来.现在看看lua的require的处理流程.1.require机制相关 ...
- lua中的require机制
lua中的require机制 为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来.现在看看lua的require的处理流程.1.require机制相关的数据 ...
- Lua require 相对路径
lua require 加载方式与我们现在熟知的路径系统不太一样,想要知道lua require 方法的工作原理也很简单 随便写一个错误的require 代码即可: 1 require("l ...
随机推荐
- CorelDRAW快速去除图片背景颜色
当我们需要从网上借助一些素材图片在CorelDRAW中运用时,往往需要去掉图片的背景颜色.本文小编分享CDR中如何快速去除图片背景颜色的方法,通过此方法可以做简单的照片抠图.合成. 1. 打开Core ...
- Camtasia中对给录制的视频添加视觉效果
视频创作和后期剪辑对很多人来说是一件很头痛的事,对着屏幕一段一段.一帧一帧的进行调整会让人十分的心烦,有时花费了大量时间剪出来的视频质量却不高,让人有一种想砸键盘的冲动. 这种问题,除非是原视频素材质 ...
- css3系列之text的常用属性 和 Multi-column(多列)
text(文本) white-space: word-break word-wrap/overflow-wrap text-align: word-spacing letter-spacing tex ...
- 检查字符串结尾 判断一个字符串(str)是否以指定的字符串(target)结尾。
function confirmEnding(str, target) { var arr = str.replace(/\s+/g, ""); var bb = arr.subs ...
- 【初等数论】裴蜀定理&扩展欧几里得算法
裴蜀定理: 对于\(a,b\in N^*, x, y\in Z\),方程\(ax+by=k\)当且仅当\(gcd(a, b)|k\)时有解. 证明: 必要性显然. 充分性:只需证明当\(k=gcd(a ...
- 如何有效恢复误删的HDFS文件
HDFS是大数据领域比较知名的分布式存储系统,作为大数据相关从业人员,每天处理HDFS上的文件数据是常规操作.这就容易带来一个问题,实际操作中对重要数据文件的误删,那么如何恢复这些文件,就显得尤为重要 ...
- AlanShan数据库课程设计报告
目 录 1.绪论.... 2 1.1前言... 2 1.2社会背景... 2 1.3超市背景... 3 2.系统可行性研究.... 4 2.1 技术可行性研究... 4 2.2 经济可行性研究. ...
- 注意当cin.getline、和cin 合用的时候
1. getline有两种,一个是string的(getline(cin,s)),一个是istream的(cin.getline(s,maxsize,delim)). 2. 当用cin输入了数据后,在 ...
- 第8.12节 Python类中使用__dict__定义实例变量和方法
上节介绍了使用实例的__dict__查看实例的自定义属性,其实还可以直接使用__dict__定义实例变量和实例方法. 一. 使用__dict__定义实例变量 语法: 对象名. dict[属性名] = ...
- 转:【Python3网络爬虫开发实战】6.4-分析Ajax爬取今日头条街拍美图
[摘要] 本节中,我们以今日头条为例来尝试通过分析Ajax请求来抓取网页数据的方法.这次要抓取的目标是今日头条的街拍美图,抓取完成之后,将每组图片分文件夹下载到本地并保存下来. 1. 准备工作 在本节 ...