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 ...
随机推荐
- ABBYY FineReader 与尚书七号OCR的对比
ABBYY FineReader 与尚书七号OCR都是帮助我们识别文字的工具,使用的都是OCR技术,如今文字识别工具是我们学习和工作经常会使用的,它们的功能是否实用和好用?现在通过对比的方式来探讨. ...
- 如何使用ABBYY FineReader 的用户模式?
在运用ABBYY FineReader 15(Windows系统)进行文档识别时,用户可能会遇到识别的文档包含一些特殊字符或者其他软件无法识别的字体等情况,容易造成识别出现乱码的结果.在这种情况下,用 ...
- 模拟赛38 B. T形覆盖 大模拟
题目描述 如果玩过俄罗斯方块,应该见过如下图形: 我们称它为一个 \(T\) 形四格拼板 .其中心被标记为\(×\). 小苗画了一个 \(m\) 行 \(n\) 列的长方形网格.行从 \(0\) 至 ...
- 聊聊kafka-client的源码
一,感想 kafka 客户端代码很早以前 我就想研究借鉴一下,我前前后后至少阅读过三遍源码,我发现我看不下去,不知道为啥这么写,在次期间,我也参考了很多的网上的源码分析,我发现自己依然一知半解的, 慢 ...
- GraphicsLab 之 Atmospheric Scattering (一)
作者:i_dovelemon 日期:2020-10-11 主题:Atmospheric Scattering, Volume Scattering, Rayleigh Scattering, Mie ...
- 使用Python切片赋值
解释器下运行以下代码:list1 = [4,5,6,7,8,9] list1[2:5] = ['a','b','c']结果是:[4, 5, 'a', 'b', 'c', 9]
- 在VMware下创建windows server 2008虚拟机
1.创建新的虚拟机 打开VMware软件,点击主页内创建新的虚拟机 2.进入新建虚拟机向导 点击典型,点击下一步 3.在下一步中单击稍后安装操作系统 点击下一步 4.选择操作系统类型 客户机操作系统选 ...
- 2014.04.28基于CPLD的LCOS场序彩色视频控制器设计
基于CPLD的LCOS场序彩色视频控制器设计 作者:宋丹娜,代永平,刘艳艳,商广辉 发表刊物:液晶与显示,2009 学习时间:2014.04.28 文章讲述了-- (和上一篇论文有些相似之处) 1. ...
- CentOS7.X 挂载磁盘 与Mysql 自动备份
1.前言 搞了好几天终于弄了出来,简单记录一下.注意以下教程是在测试服务器基础上编写 如数据盘含有数据切勿使用此教程,如对命令行不熟悉的 不建议搞哈 .本文中的磁盘/dev/sdb为笔者测试服务器上的 ...
- 【PSMA】Progressive Sample Mining and Representation Learning for One-Shot Re-ID
目录 主要挑战 主要的贡献和创新点 提出的方法 总体框架与算法 Vanilla pseudo label sampling (PLS) PLS with adversarial learning Tr ...