模块和包基础概念
Lua对模块的支持仅定义了两个函数,用require来使用模块,用module来定义模块。模块的概念就是一个包含了各种函数和常量的table,模块中所有的接口(interface)都定义在这个表中,而require返回的也是这样一个表。使用table来表达模块的概念,使得模块可以统一到Lua语言的其它部分中去,并且使用Lua的所有其它特性。对于require函数来说,一个模块就是定义了一些值的一个Chunk,require函数的返回值就是一个包含模块函数的table,并且包含了一个全局变量指向这个table(到了5.2版本好像不支持全局变量了)。
require函数
推荐的require函数的用法是: local m = require “mod”,将模块置入一个局部变量中去。Lua搜索文件首先是Lua文件,然后才是C库文件。Lua载入一个lua文件使用loadfile方法,载入一个C库使用loadlib方法,这两个方法都仅仅是载入代码而没有运行代码。运行可以使用pcall方法。
require
函数用来搜索Lua文件的模式(pattern)保存在package.path
中。Lua使用LUA_PATH来初始化package.path,如果LUA_PATH不存在,那么就使用一个编译时内置的默认路径。并且在定义LUA_PATH时,使用;;来代表这个默认路径。如果Lua找不到对应的lua文件,那么它就从package.cpath中查找C库,此变量是用LUA_CPATH初始化的,使用C库中的函数会调用C库的luaopen_modname函数,其中modname就是C库的文件名或模块名。
编译内置的package.path在luaconf.h文件中, /usr/local/share/lua/5.3/?.lua;/usr/local/share/lua/5.3/?/init.lua;/usr/local/lib/lua/5.3/?.lua;/usr/local/lib/lua/5.3/?/init.lua;./?.lua;./?/init.lua
编译内置的package.cpath在luaconf.h文件中,
/usr/local/lib/lua/5.3/?.so;/usr/local/lib/lua/5.3/loadall.so;./?.so
搜索路径如果是相对路径,相对的是应用程序的路径,如lua, skynet而不是被调用的lua文件的路径。搜索路径通常会包含当前工作目录
require函数有一个特点:当模块名中包含连字符(-)时,Lua调用的luaopen_*函数的函数名会忽略模块名直到连字符之后。如m1 = require “v1-mod”,会先找到文件名为v1-mod的模块,然后调用luaopen_mod函数。
子模块
Lua中的子模块用.号隔开。如mod.sub是mod的子模块。当搜索子模块的文件时,require会将.号转化为目录分隔符(在Unix中为/,在Windows中是)。不论是搜索lua文件还是搜索so文件,都需要从子目录中搜索。如require “a.b.c”会搜索a/b/c.lua或者a/b/c.so文件,如果这样找不到文件那么会从package.cpath路径模式中查找a.so文件,对于这种情况可以包含多个模块在a.so文件中,如a.w模块。这里a是包名(package name),对于这种情况,而a.b.c和a.w是模块名。不管是a/b/c.so文件还是a.so文件,都必须包含luaopen_a_b_c函数。特别是a/b/c.so文件不能是luaopen_c函数。
这里require也可以使用连字符特性:如require “mod.-a”,会先找到文件mod/-a.so文件,再查找luaopen_a函数。
在Lua的搜索路径中只有?和;号是预定的。?用来替换模块名,而;用来分割各个搜索路径。在require后的字符串参数中只有.和-号是预定的。.号被翻译为目录分隔符(/或),而-号用来忽略前面的字符串,当有-号时,甚至可以require “a/b/-c”,会先查找a/b/-c.so文件,再查找luaopen_c函数,因为会忽略掉前面的a/b/不放到函数名中。而如果require “a/b/c”,会先查找a/b/c.so文件,再查找luaopen_a/b/c函数。
在Lua中,子模块之间、包与模块之间没有直接的关系。导入子模块并不会导入其它模块。
创建模块
创建模块的方法就是创建一个table,把所有函数都放入table,然后返回这个table。