ModulePackage.lua
------------------------------------ 模块与包 ------------------------------------
------- 模块加载
-- Lua提供了一个名为require的函数来加载模块;
-- require("<模块名>") 或者 require "<模块名>"
-- require查找成功会返回加载器,否则会抛出错误;
--- 加载lua模块(这里myModule.lua放在本xx.lua同目录的module文件夹里)
-- 模块路径:LUA_PATH环境变量
local myMdl = require('module.myModule')
print(myModule.mysin(3.14 / 2)) --> 0.99999968293183
print(myMdl.name) --> math
say() --> 你好! (调用了myModule.lua中的sayHello()函数)
--- 加载C包(mylib.dll放在本xx.lua同目录下)
-- C库路径:LUA_CPATH环境变量;
local myLib = require ("mylib")
-- 结果与上面的例子相同,但是这里是通过调用C库中的函数实现;
print(myLib.mysin(3.14 / 2)) --> 0.99999968293183
------- require( modname )加载机制
-- 按顺序执行以下步骤,直到某步骤成功为止;
-- (1) 先查找 package.loaded 表,若 modname 被加载过(即表中存在 modname),则返回
-- package.loaded[modname]中保存的值(即模块);
-- (2) 按照 package.searchers 查找器序列的执行来查找,如下:
-- 1. 查找 package.preload 表,若存在 package.preload[modname],则返回这个加载器(这个值是这个函数);
-- 2. 查找Lua库的加载库;(按照 package.path 的路径作查找)
-- 3. 查找C库的加载库;(按照 package.cpath 的路径作查找)
-- 4. 一体化加载器;(按照 package.cpath 路径,如查找模块中的子模块)
-- (3) 调用返回的加载器;(参数1:modname ; 参数2:获取加载器过程中得到的参数)
-- (4) 加载器返回非空值,则将其赋值给 package.loaded[modname];
-- 加载器返回空值,则将 package.loaded[modname] 赋值为 true;
-- (5) 加载、运行模块时有错误,或无法为模块找到加载器,require都会抛出错误;
------- package包API解析
-- Lua包管理库帮助从Lua中加载模块,除了require是放在全局环境中,其他的部分都在表package中;
--- package.config:string
-- 一个描述有一些为包管理准备的编译期配置信息的串;
-- 行一:目录分割串;(Windows默认是 '\' ,其他系统是 '/' )
-- 行二:路径分隔符;(默认是 ';' )
-- 行三:模板字符串替代符;(默认是 '?' )
-- 行四:执行程序所在目录的替换符;(Windows默认是 '!' )
-- 行五:忽略记号;(默认是 '-' )(使用loadlib构建luaopen_函数名时,忽略掉'-'后的文本,require支持得不友好)
do
local str = string.gsub(package.config,"\n"," ")
print(str) --> \ ; ? ! -
end
--- package.preload:table
-- 保存有一些特殊模块的加载器;
-- 这个变量仅仅是对真正那张表的引用,改变这个值,并不会改变require使用的表;
print(package.preload) --> table: 0114FD30
--- package.loaded:table
-- 用于require控制哪些模块已经被加载的表;
-- 这个变量仅仅是对真正那张表的引用,改变这个值,并不会改变require使用的表;
do
local str = 'package.loaded:'
local fmt = "'%s' "
for k, v in pairs(package.loaded) do
str = str .. fmt:format(k) --> package.loaded:'io' 'math' 'module.myModule' 'string' 'table' 'os' '_G' 'mylib' 'debug' 'utf8' 'package' 'coroutine'
end
print(str)
end
--- package.path:string
-- 这个路径被require在Lua加载器中做搜索时用到;
-- 启动时,Lua用环境变量 LUA_PATH 或 LUA_PATH_5_3 来初始化这个变量;(或者采用luaconf.h中默认的路径)
-- 环境变量中出现的所有';;'都会被替代成默认的路径;
do
local str = string.gsub(package.path,';',';|')
print(str) --> C:\Program Files (x86)\Lua\5.1\lua\?.luac;|C:\Program Files (x86)\Lua\5.3\?.luac;|.\?.lua;|
end
--- package.cpath:string
-- 这个路径被require在C加载器中做搜索时用到;
-- 启动时,Lua用环境变量 LUA_CPATH 或 LUA_CPATH_5_3 来初始化这个变量;(或者采用luaconf.h中默认的路径)
-- 环境变量中出现的所有';;'都会被替代成默认的路径;
do
local str = string.gsub(package.cpath,';',';|')
print(str) --> C:\Program Files (x86)\Lua\5.3\?.dll;|C:\Program Files (x86)\Lua\5.3\..\lib\lua\5.3\?.dll;|C:\Program Files (x86)\Lua\5.3\loadall.dll;|.\?.dll
end
--- package.searchers:table
-- 用于require控制如何加载模块的表;
-- 表内每一项都是一个 "查找器函数";(require按次序调用这些 "查找器")
-- 参数: 传入模块名(即require的参数)作为唯一的一个参数;
-- 返回值:
-- 1. 查找成功:此函数返回:1个模块的 "加载器" 和 1个传递给这个加载器的参数;
-- 2. 查找失败:返回描述失败原因的字符串(或者nil);
-- 查找器序列,如下:
-- 1. 查找 package.preload 表(无返回值)
-- a. 过程:若存在 package.preload[modname],这个值就是对应的加载器(这个值是这个函数);
-- 2. 查找Lua库的加载库;(返回模块的文件名)
-- a. 路径:package.path;
-- b. 过程:同package.searchpath描述一致,如:
-- myMod.lua在c:\lua\module,环境变量LUA_PATH有路径"c:\lua\?.lua",则参数 modname
-- 可以是"module.myMod",会将"."替换成"\",再以"module\myMod"替代"?",即最终路径:
-- "c:\lua\module\myMod.lua";
-- 3. 查找C库的加载库;(返回模块的文件名)
-- a. 路径:package.cpath;
-- b. 过程:同package.searchpath描述一致,如:
-- a.b.c-v2.1.dll在c:\lua\clib,环境变量LUA_CPATH有路径"c:\lua\clib\?.dll",则参
-- 数 modname可以是"a.b.c-v2.1",将以"a.b.c-v2.1"替代c路径的"?",即最终路径:
-- "c:\lua\clib\a.b.c-v2.1.dll";
-- 找到该C库后,首先使用动态链接机制连接该库,然后尝试在该库中寻找可用作加载器的C函数;
-- 则个C函数名字必须是"luaopen_"紧接模块名的字符串,即将modname中"-"后面的串去掉,同
-- 时,将"."替换成"_",即:"a_b_c",最终函数名:"luaopen_a_b_c"
-- 4. 一体化加载器;(返回模块的文件名,如查找模块中的子模块)
-- a. 路径:package.cpath;
-- b. 过程:查找根名字,如:
-- a-v2.1.dll在c:\lua\clib,环境变量LUA_CPATH有路径"c:\lua\clib\?.dll",则参
-- 数 modname如果是"a.b.c",将以"a"替代c路径的"?",即最终路径:"c:\lua\clib\a.dll";
-- 找到该C库后,接着会在里面找子模块的加载函数,本例子中的加载函数就是"luaopen_a_b_c",
-- 当然,里面也会存在"luaopen_a"、"luaopen_a_b"等C函数;
-- c. 特点:可以把若干C子模块打包进单个库,每个子模块都可以有原本的加载函数名;
do
local str = ''
local fmt = "k='%s','%s'| "
for k, v in pairs(package.searchers) do
str = str .. fmt:format(k,v) --> k='1','function: 0133BA58'| k='2','function: 013412C8'| k='3','function: 01340CD8'| k='4','function: 013411D8'|
end
print(str)
end
--- package.searchpath(name,path[,sep[,rep]])
-- 在指定 path 中搜索指定的 name;
-- 路径:一个包含有一系列以分号分割的 模板 构成的字符串;
-- 模板:即问号("?"),都会用name去替代;
-- sep、rep:将name中的sep符号替换成rep符号;(sep默认为"." rep默认为"\")
-- 返回值:
-- 成功:第一个可以用读模式打开(并立即关闭)的文件的名字;
-- 失败:nil + 错误消息
do
local path = ".\\module\\?.lua;.\\?.lua;"
print(package.searchpath("module.myModule",path)) --> .\module\myModule.lua
print(package.searchpath("mymodule",path)) --> .\module\myModule.lua (不区分大小写)
print(package.searchpath("myXXX",path)) --> nil / no file '.\module\myXXX.lua' / no file '.\myXXX.lua'
end
--- package.loadlib(libname,funcname)
-- 让宿主程序动态链接 C 库 libname;
-- 当funcname为 "*",它仅仅连接该库,并将库中符号导出给其他动态链接库使用;
-- 当funcname为库中函数名,则查找库中对应函数,以C函数形式返回;(funcname要遵循原型lua_CFunction)
-- 相比于require(modname):
-- 1. 只加载 C 库;
-- 2. 不做任何路径查询;(即不使用 package.searchers 的4个加载器)
-- 3. 不会自动加扩展名;(如:"mylib-v2.1.dll"改成"mylib-v2.1",会搜索失败)
-- 4. 对忽略记号支持好;(如:在require中传入"mylib-v2.1.dll",会搜索失败)
-- (1) 用法一 :获取库中的模块
-- cpp中函数原型“extern "C" __declspec(dllexport) int luaopen_mylib(lua_State* L)”
openMyLib = package.loadlib("mylib-v2.1.dll","luaopen_mylib")
mylibTable = openMyLib()
print(mylibTable.mysin(3.14/2)) --> 0.99999968293183
--(2)用法二 :获取库中的某个函数
-- cpp中函数原型“extern "C" __declspec(dllexport) int l_sin(lua_State *L)”
mysin = package.loadlib("mylib.dll","l_sin")
print(mysin(3.14/2)) --> 0.99999968293183
myModule.lua
--[=[
1. Lua的模块是由变量、函数等已知的元素组成的table;
2. 要创建一个模块,只需要创建一个table,然后把需要到处的变量、函数等放入其中,最后返回这个table就行;
]=]
-- 定义一个模块
myModule = {}
-- 定义一个模块的函数
function myModule.mysin(num)
return math.sin(num)
end
-- 定义一个私有函数(仅模块内可以访问)
local function sayHello()
print("你好!")
end
-- 定义一个公有的函数,用以调用私有函数
function say()
sayHello()
end
-- 定义一个变量
myModule['name'] = 'math'
-- 结尾处返回这个模块的table
return myModule