在游戏开发中会经常使用到lua作为游戏逻辑层的脚本语言,各种优势就不说了,虽然平时用的比较多,但对lua语言本身和内部的一些实现并不是很了解,让我们先从lua的require入手来一探require的各种用法吧。

require其实类似与C/C++中的#include,就是加载一个指定名称的模块进来,该模块可以来自于lua,也可能来自于C/C++,在lua虚拟机启动时,默认会给我们预先加载好一些模块,保存在package.loaded中,我们可以实际打印一波看看:

for k, v in pairs(package.loaded) doprint(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) doprint(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] thenreturn package.loaded[module]endfor pattern in string.gmatch(package.path, '[^;]+%?[^;]+') dolocal path = string.gsub(pattern, '%?', module)local fp = loadfile(path)if fp thenlocal ret = fp()if ret ~= nil thenpackage.loaded[module] = retelsepackage.loaded[module] = trueendreturn package.loaded[module]endend
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] thenreturn package.loaded[module]endfor pattern in string.gmatch(package.path, '[^;]+%?[^;]+') dolocal 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 thenlocal ret = fp()if ret ~= nil thenpackage.loaded[module] = retelsepackage.loaded[module] = trueendreturn package.loaded[module]endendfor pattern in string.gmatch(package.cpath, '[^;]+%?[^;]+') dolocal path = string.gsub(pattern, '%?', module)local fp = package.loadlib(path, "luaopen_" .. module)if fp thenlocal ret = fp()if ret ~= nil thenpackage.loaded[module] = retelsepackage.loaded[module] = trueendreturn package.loaded[module]endend
end

Lua的require小结相关推荐

  1. Lua的require使用

    一.require的作用 加载指定的模块,相当与#include作用类似,加载了该模块,那么就可已使用模块中的全局函数和全局数据(如表等等)  注:实际上require "xxx" ...

  2. Lua的require机制

    在Lua中,有模块这个定义,加载一个模块需要使用require函数,require是一个全局函数. require有两个写法require("modname")和require & ...

  3. MinGW+Lua环境配置小结

    虽说VC是Windows下C++开发的首选工具,但有时候写一些小程序,我还是更倾向于使用一些轻量级的开发工具.比如最近研究Lua嵌入 C++,有时候只是写一些很小的程序,这时候Visual Studi ...

  4. Lua require 相对路径

    lua require 加载方式与我们现在熟知的路径系统不太一样,想要知道lua require 方法的工作原理也很简单 随便写一个错误的require 代码即可: 1 require("l ...

  5. 使用ToLua插件 关于Lua脚本中 require 添加模块经常报错找不到Lua文件的问题

    Lua的require添加模块经常报错,找不到 LuaException: E:/UnityProJect/Calculator/Assets/Script/Lua/NpcManage.lua:4: ...

  6. lua/require函数

    Lua提供高级的require函数来加载运行库,lua中的require函数功能主要有: 1.require函数会搜索目录加载文件 2.require会判断是否文件已经加载避免重复加载同一文件. 由于 ...

  7. sleep函数c语言实现,C语言拓展实现Lua sleep函数

    这几天在做一个小项目,其中用到了一些基本的API, 例如sleep,获取当前目录等等,lua标准库中没有提供这些接口,虽然所第三方库中也都有实现,但是要用的就那么几个函数,在一个嵌入式系统中安装那么多 ...

  8. lua延时函数c语言,C语言拓展实现Lua sleep函数

    这几天在做一个小项目,其中用到了一些基本的API,例如sleep,获取当前目录等等,lua标准库中没有提供这些接口,虽然所第三方库中也都有实现,但是要用的就那么几个函数,在一个嵌入式系统中安装那么多第 ...

  9. lua 多条件_【LUA】只需花费你半天时间

    前言:有一段时间使用OpenResty写Waf防护模块的时候使用到了Lua.Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵 ...

最新文章

  1. pom配置之:distributionManagementsnapshot快照库和release发布库
  2. 帝国cms栏目忘记设置为终极栏目怎么办?
  3. mybaits十七:使用foreach标签
  4. poj3666(DP+离散化)
  5. Facebook开源计算机视觉目标检测平台Detectron
  6. jenkins war包_Jenkins的安装和部署(jenkins教程)
  7. iOS:iOS开发系列–打造自己的“美图秀秀”(中)
  8. 阿里小程序云应用上线了,有哪些看点?
  9. ACM-ICPC 2018 徐州赛区网络预赛G (单调队列)
  10. sqlmap md5怎么解密_UC浏览器代理流量解密
  11. PeopleRank从社交网络中发现个体价值
  12. cxGrid使用笔记
  13. “暖心”腊八节开启中国年 全民喝粥“讨彩头”
  14. PCL计算点到直线距离
  15. No certificate for team ‘‘ matching ‘iPhone Distribution: VOVA TECH LIMITED ()‘ Select a different s
  16. python假设检验
  17. 跨站脚本攻击(XSS)及防范措施
  18. 改进ur_modern_driver包,提供ur_driver/URScript_srv服务
  19. 将Ubuntu装入移动硬盘
  20. “梦中行千里不如现实行一步。”创业亦是如此丨国仁网络资讯

热门文章

  1. java resultmap_mybatis: resultMap 结果集映射和多表查询
  2. 基于MATLAB面部特征识别的疲劳检测系统
  3. mac使用office工具卡顿问题
  4. L298N 直流电机驱动模块与 Arduino
  5. 四元数——为何使用四元数
  6. python多变量同时赋值
  7. JavaWeb(引用-->狂神学习笔记)2021-08
  8. Pyhon在振动信号处理中的高级应用(十一):监督式特征的选择方法(F_Score、Chi-square Score、mRMR)
  9. 基于android的网络选课系统
  10. 适女化科技(二):让女性更安全的两条技术路径:软件硬件化与硬件软件化...