Lua 中模块概念

模块和包基础概念

Lua 对模块的支持仅定义了两个函数,用 require 来使用模块,用 module 来定义模块(5.2 之后已经不支持使用 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 文件将会执行此文件从而构建一个 Lua 表作为模块,载入一个 C 库将调用其 luaopen_* 函数打开相应的 C 函数。载入 Lua 文件或者 C 库都只会执行一次, 一旦载入之后, 值将保存为 package.loaded[modname] 以后 require 将返回此值。

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 中,子模块之间、包与模块之间没有直接的关系。导入子模块并不会导入其它模块。