技術ノート 11このLTNはLua 5.0で導入された"loadfile"に依存します。
Lua 4.1では、既にロードされていない場合にファイルをロードして実行する"require"関数が導入されました。Lua 5.0では、requireは基本ライブラリに組み込まれた関数として提供されています。requireコマンドは、LTN 7 "モジュールとパッケージ"と共に、Luaにおけるシンプルなモジュールサポートの基礎を提供します。この技術ノートでは、requireの改良版である"import"を提案します。提案されたimportスキームは、グローバル変数への直接アクセスを回避し、グローバル変数に関連するセキュリティ上の抜け穴を修正し、循環的なモジュール依存関係を適切に処理します。
import関数は、次のLua 5.0コードで実装できます。
local imported = {}
local function package_stub(name)
local stub = {}
local stub_meta = {
__index = function(_, index)
error(string.format("member `%s' is accessed before package `%s' is fully imported", index, name))
end,
__newindex = function(_, index, _)
error(string.format("member `%s' is assigned a value before package `%s' is fully imported", index, name))
end,
}
setmetatable(stub, stub_meta)
return stub
end
local function locate(name)
local path = LUA_PATH
if type(path) ~= "string" then
path = os.getenv "LUA_PATH" or "./?.lua"
end
for path in string.gfind(path, "[^;]+") do
path = string.gsub(path, "?", name)
local chunk = loadfile(path)
if chunk then return chunk, path end
end
return nil, path
end
function import(name)
local package = imported[name]
if package then return package end
local chunk, path = locate(name)
if not chunk then
error(string.format("could not locate package `%s' in `%s'", name, path))
end
package = package_stub(name)
imported[name] = package
setglobals(chunk, getglobals(2))
chunk = chunk()
setmetatable(package, nil)
if type(chunk) == "function" then
chunk(package, name, path)
end
return package
end
importの典型的な使用法は次のとおりです。
-- import the complex package local complex = import "complex" -- complex now holds the public interface local x = 5 + 3*complex.I
パッケージは次のように構成する必要があります。
-- first import all other required packages.
local a = import "a"
local b = import "b"
-- then define the package install function.
-- the PIF more or less contains the code of a
-- LTN 7 package.
local function pif(Public, path)
local Private = {}
function Public.fun()
-- public function
end
-- etc.
end
-- return the package install function
return pif
Importは、requireとほぼ後方互換性があります。ただし、Importはロード中に_REQUIREDNAMEグローバルを定義しません。PIFを返さない「旧スタイル」のパッケージは、ロードされて実行されますが、importは空のパブリックインターフェースを返します。requireには戻り値がないため、これは旧スタイルのコードには影響しません。
これは、互いにインポートし合う2つのパッケージの例です。インポート中に実際にお互いを使用していないため、これは問題になりません。
パッケージ "a.lua"
local b = import "b"
local function pif(pub, name, path)
function pub.show()
-- use a message from package b
print("in " .. name .. ": " .. b.message)
end
pub.message = "this is package " .. name .. " at " .. path
end
return pif
パッケージ "b.lua"
local a = import "a"
local function pif(pub, name, path)
function pub.show()
-- use a message from package a
print("in " .. name .. ": " .. a.message)
end
pub.message = "this is package " .. name .. " at " .. path
end
return pif
そして、両方をインポートして実行するコード
local a = import "a" local b = import "b" a.show() -- prints "in a: this is package b at ./b.lua" b.show() -- prints "in b: this is package a at ./a.lua"