15.1 The require Function
函数require
Lua offers a high-level function to load modules, called require. This function tries to keep to a minimum its assumptions about what a module is. For require, a module is just any chunk of code that defines some values (such as functions or tables containing functions).
Lua提供一个高级函数来加载模块,叫做require。该函数试图保持它关于模块是什么的假定为一极小数量。对require来说,模块只是定义了一些值(比如函数或包含函数的表)的代码块。
To load a module, we simply call require "modname". Typically, this call returns a table comprising the module functions, and it also defines a global variable containing this table. However, these actions are done by the module, not by require, so some modules may choose to return other values or to have different side effects.
要加载模块,我们只需调用require "modname"。典型地,该调用返回一个由模块函数组成的表,它也定义了一个包含该表的全局变量。但是,这些动作是模块而非require做的,所以某些模块选择返回其他值或具有不同的副作用。
It is a good programming practice always to require the modules you need, even if you know that they would be already loaded. You may exclude the standard libraries from this rule, because they are pre-loaded in Lua. Nevertheless, some people prefer to use an explicit require even for them:
总是require需要的模块,即使你知道它们已经加载了,这是好的编程习惯。标准库可排除在这个准则之外,因为在Lua中它们是预加载的。不过,即使是它们,有些人也更愿意使用显式地require:
local m = require "io"
m.write("hello world\n")
Listing 15.1 details the behavior of require. Its first step is to check in table package.loaded whether the module is already loaded. If so, require returns its corresponding value. Therefore, once a module is loaded, other calls to require simply return the same value, without loading the module again.
清单15.1详述了require的行为。第一步是在表package.loaded中检查模块是否已经加载了。如果是,require返回其相应的值。所以,一旦模块已经载入,其他对require的调用只是返回同一个值,不会再次加载模块。
If the module is not loaded yet, require tries to find a loader for this module. (This step is illustrated by the abstract function findloader in Listing 15.1.) Its first attempt is to query the given library name in table package.preload. If it finds a function there, it uses this function as the module loader. This preload table provides a generic method to handle some non-conventional situations (e.g., C libraries statically linked to Lua). Usually, this table does not have an entry for the module, so require will search first for a Lua file and then for a C library to load the module from.
如果模块还没载入,require尝试为该模块寻找一个加载器。(本步由清单15.1中的抽象函数findloader举例说明)首先尝试在表package.preload中查询给定的库名。如果在那儿找到一个函数,就用该函数作为模块加载器。该preload表提供一个通用方法来处理某些非常规情况(比如静态连接Lua的C库)。通常,该表没有模块的条目,所以require将首先搜索一个Lua文件然后是个C库文件来从中加载模块。
If require finds a Lua file for the given module, it loads it with loadfile; otherwise, if it finds a C library, it loads it with loadlib. Remember that both
----------------------- Page 3-----------------------139
Listing 15.1. The require function:
清单 15.1。require函数:
-----------------------------------------------------
function require (name)
if not package.loaded[name] then -- module not loaded yet? 模块还没载入?
local loader = findloader(name)
if loader == nil then
error("unable to load module " .. name)
end
package.loaded[name] = true -- mark module as loaded 标记模块为已载入
local res = loader(name) -- initialize module 初始化模块
if res ~= nil then
package.loaded[name] = res
end
end
return package.loaded[name]
end
-----------------------------------------------------
loadfile and loadlib only load some code, without running it. To run the code, require calls it with a single argument, the module name. If the loader returns any value, require returns this value and stores it in table package.loaded to return the same value in future calls for this same library. If the loader returns no value, requirereturns whatever value is in table package.loaded. As we will see later in this chapter, a module can put the value to be returned by require directly into package.loaded.
如果require为给定的模块找到一个Lua文件,就用loadfile加载它;否则,如果找到一个C库,就用loadlib加载它。记住,loadfile和loadlib都只加载某些代码,不会运行它。
An important detail of that previous code is that, before calling the loader, require marks the module as already loaded, assigning true to the respective field in package.loaded. Therefore, if the module requires another module and that in turn recursively requires the original module, this last call to require returns immediately, avoiding an infinite loop.
先前那段代码有个重要细节,就死后在调用loader以前,require(通过)给package.loaded中各自的字段赋值true把模块标记为已经载入。因此,如果该模块require其他模块而且依次递归require原始模块,这个最近对require的调用会立刻返回,避免了无线循环。
To force require into loading the same library twice, we simply erase the library entry from package.loaded. For instance, after a successful require "foo", package.loaded["foo"] will not be nil. The following code will load the library again:
要迫使require加载同一个库两次,只需要从package.loaded中擦除库条目。例如,在成功地require "foo"之后,package.loaded["foo"]经不会为nil。下面的代码将再次加载库:
package.loaded["foo"] = nil
require "foo"
When searching for a file, require uses a path that is a little different from typical paths. The path used by most programs is a list of directories wherein to search for a given file. However, ANSI C (the abstract platform where Lua runs) does not have the concept of directories. Therefore, the path used by require is a list of patterns, each of them specifying an alternative way to transform a module name (the argument to require) into a file name. More specifically, each component in the path is a file name containing optional question marks. For each component, require replaces the module name for each '?' and checks whether there is a file with the resulting name; if not, it goes to the next
----------------------- Page 4-----------------------140
component. The components in a path are separated by semicolons (a character seldom used for file names in most operating systems). For instance, if the path is
当搜索一个文件时,require使用与典型路径有些不同的路径。多数程序用的路径是个目录列表,在其中搜索给定的文件。但是,ANSI C(Lua运行于其中的抽象平台)没有目录的概念。因此,require用的路径是个模式列表,它们中的每个都规定了一个可选的路径来把模块名(require的参数)转化为文件名。对于每个组件,require把每个'?'都替换为模块名并检查合成的名字是否文件;如果不是,就转到下一个组件。路径中的组件由分号(在多数操作系统中都极少被用作文件名的字符)分隔。例如,如果路径是
?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua
then the call require "sql" will try to open the following files:
则对require "sql"的调用将试图打开下列文件:
sql
sql.lua
c:\windows\sql
/usr/local/lua/sql/sql.lua
The require function assumes only the semicolon (as the component separator) and the question mark; everything else, such as directory separators or file extensions, is defined by the path itself.
函数require只假定了分号(作为组件分隔符)和问号标记;其他的所有东西,比如目录分隔符或文件扩张名,由路径自身定义。
The path that require uses to search for Lua files is always the current value of the variable package.path. When Lua starts, it initializes this variable with the value of the environment variable LUA_PATH or with a compiled-defined default path, if this environment variable is not defined. When using LUA_PATH, Lua substitutes the default path for any substring ";;". For instance, if you set LUA_PATH to "mydir/?.lua;;", the final path will be the component "mydir/?.lua" followed by the default path.
require用来搜索Lua文件的路径始终是变量package.path的当前值。当Lua启动时,它用环境变量LUA_PATH初始化该变量,或者,如果该环境变量未定义就用一个编译时定义的缺省路径。当使用LUA_PATH时,Lua用缺省路径替换所有的子字符串";;"。例如,如果你设置LUA_PATH为"mydir/?.lua;;",最终的路径将是组件"mydir/?.lua"后跟缺省路径。
If require cannot find a Lua file compatible with the module name, it looks for a C library. For this search, it gets the path from variable package.cpath (instead of package.path). This variable gets its initial value from the environment variable LUA_CPATH (instead of LUA_PATH). A typical value for this variable in Unix is like this:
如果require找不到与模块名一致的Lua文件,则查找C库。该搜索从变量package.cpath(而不是package.path)中获取路径。这个变量从环境变量LUA_CPATH(而非LUA_PATH)中获得其初始值。在Unix中该变量的典型值是这样的:
./?.so;/usr/local/lib/lua/5.1/?.so
Note that the file extension is defined by the path (e.g., the previous example uses .so for all templates). In Windows, a typical path is more like this one:
注意,文件扩展名由路径定义(举例来说,前一个例子用.so作为所有的模板)。在Windows中,典型路径大概是这个:
.\?.dll;C:\Program Files\Lua501\dll\?.dll
Once it finds a C library, require loads it with package.loadlib, which we discussed in Section 8.2. Unlike Lua chunks, C libraries do not define one single main function. Instead, they can export several C functions. Well-behaved C libraries should export one function called luaopen_modname, which is the function that require tries to call after linking the library. In Section 26.2 we will discuss how to write C libraries.
一旦找到C库,require用package.loadlib——我们在8.2节讨论过——加载它。与Lua程序块不同,C库不会定义一个单独的主函数。代替的做法是,它们能导出若干C函数。行为良好的C库应该导出一个名为luaopen_modname的函数,它是require在连接库以后试图调用的函数。我们将在26.2节中导论如何编写C库。
Usually, we use modules with their original names, but sometimes we must rename a module to avoid name clashes. A typical situation is when we need to load different versions of the same module, for instance for testing. For a Lua module, either it does not have its name fixed internally (as we will see later) or we can easily edit it to change its name. But we cannot edit a binary module to correct the name of its luaopen_* function. To allow for such renamings, require uses a small trick: if the module name contains a hyphen, require
----------------------- Page 5-----------------------141
strips from the name its prefix up to the hyphen when creating the luaopen_* function name. For instance, if a module is named a-b, require expects its open function to be named luaopen_b, instead of luaopen_a-b (which would not be a valid C name anyway). So, if we need to use two modules named mod, we can rename one of them to v1-mod (or -mod, or anything like that). When we call m1=require "v1-mod", require will find both the renamed file v1-mod and, inside this file, the function with the original name luaopen_mod.
通常,我们以模块的原始名字来使用它们,但有时候必须重命名模块以避免命名冲突。典型的情况是当我们需要加载同一模块的不同版本时,比如为了测试。对于Lua模块,要么它不把名字固化在内部(如同我们稍后将看到的),要么我们能轻易地编辑它改变名字。但是我们不能编辑二进制模块来改正其luaopen_*函数的名字。为了允许这种重命名,require用了一个小技巧:如果模块名含有连字符,require在创建函数名luaopen_*时会从名字中剥去直到连字符的前缀。例如,如果模块名为a-b,require期望其打开函数名为luaopen_b,而不是luaopen_a-b(无论如何它也不是有效的C命名)。所以,如果我们需要使用两个名为mod的模块,可以重命名其中一个为v1-mod(或-mod,或类似那个的任何东西)。当我们调用m1=require "v1-mod"时,require将会找到更名的文件v1-mod,而且也能用原始名字luaopen_mod找到该文件内的函数。
阅读(1469) | 评论(0) | 转发(0) |