Lua のモジュールとパッケージ


モジュールはパッケージ ライブラリに似ています。Lua 5.1 以降、Lua には、いくつかの共通コードをファイルに配置し、API インターフェイスの形式で他の場所で呼び出すことができるようになりました。コードを再利用し、コードの結合を減らします。

Lua のモジュールは変数や関数などの既知の要素で構成されるテーブルなので、モジュールの作成は非常に簡単で、テーブルを作成し、そこにエクスポートする必要がある定数と関数を入れて、最後にテーブルを返すだけです。以下はカスタムモジュール module.lua の作成です。 ファイルのコード形式は以下の通りです:

-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}
 
-- 定义一个常量
module.constant = "这是一个常量"
 
-- 定义一个函数
function module.func1()
    io.write("这是一个公有函数!\n")
end
 
local function func2()
    print("这是一个私有函数!")
end
 
function module.func3()
    func2()
end
 
return module

上記からもわかるように、モジュールの構造はテーブル構造になっているので、定数の操作や呼び出しが可能です。または、テーブル内の要素と同じようにモジュール内の関数を呼び出します。

上記の func2 はプログラム ブロックのローカル変数として宣言されており、プライベート関数を表します。したがって、モジュール内のこのプライベート関数は外部からアクセスできず、モジュール内のパブリック関数を通じて呼び出す必要があります。


require関数

Lua モジュールをロードするためにrequireという関数が用意されています。モジュールをロードするには、それを呼び出すだけです。例:

require("<模块名>")

または

require "<模块名>"

require を実行すると、モジュール定数または関数で構成されるテーブルが返され、そのテーブルを含むグローバル変数も定義されます。

-- test_module.lua 文件
-- module 模块为上文提到到 module.lua
require("module")
 
print(module.constant)
 
module.func3()

上記のコードの実行結果は次のとおりです:

这是一个常量
这是一个私有函数!

または、簡単に呼び出せるようにロードされたモジュールのエイリアス変数を定義します:

-- test_module2.lua 文件
-- module 模块为上文提到到 module.lua
-- 别名变量 m
local m = require("module")
 
print(m.constant)
 
m.func3()

上記のコードの実行結果は次のとおりです:

这是一个常量
这是一个私有函数!

ロードメカニズム

カスタマイズされたモジュールの場合、モジュール ファイルは、どのファイル ディレクトリに配置されるかは関係ありません。関数 require には、独自のファイル パス読み込み戦略があり、Lua ファイルまたは C ライブラリからモジュールをロードしようとします。

require Lua ファイルの検索に使用されるパスは、グローバル変数 package.path に保存されます。Lua が起動すると、この環境変数は環境変数 LUA_PATH の値で初期化されます。環境変数が見つからない場合は、コンパイル時に定義されたデフォルトのパスが初期化に使用されます。

もちろん、LUA_PATH 環境変数がない場合は、設定をカスタマイズして、現在のユーザーのルート ディレクトリにある .profile ファイルを開くこともできます (存在しない場合は作成するか、.bashrc ファイルを開きます)。 LUA_PATH 環境変数に「~/lua/」パスを追加します:

#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"

ファイル パスは「;」で区切られ、最後の 2 つの「;;」は、新しいパスの後に元のデフォルト パスが続くことを意味します。

次に、環境変数パラメーターを更新して、すぐに有効になるようにします。

source ~/.profile

package.path の値が次であると仮定します:

/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua

そして、require("module") が呼び出されると、ターゲットを検索するために次のファイル ディレクトリを開こうとします。

/Users/dengjoe/lua/module.lua;
./module.lua
/usr/local/share/lua/5.1/module.lua
/usr/local/share/lua/5.1/module/init.lua
/usr/local/lib/lua/5.1/module.lua
/usr/local/lib/lua/5.1/module/init.lua

目的のファイルが見つかった場合、package.loadfile が呼び出されてモジュールがロードされます。それ以外の場合は、C ライブラリに移動します。

検索されたファイル パスはグローバル変数 package.cpath から取得され、この変数は環境変数 LUA_CPATH を通じて初期化されます。

検索戦略は上記と同じですが、検索対象が so または dll タイプのファイルである点が異なります。見つかった場合、require は package.loadlib 経由でそれをロードします。


C パッケージ

Lua と C は簡単に組み合わせることができます。C を使用して Lua 用のパッケージを作成します。

Lua で書かれたパッケージとは異なり、C パッケージは使用する前に最初にロードして接続する必要があります。これを実装する最も簡単な方法は、ダイナミック リンク ライブラリ メカニズムを使用することです。

Lua は、すべてのダイナミックリンク機能を、loadlib と呼ばれる関数で提供します。この関数は、ライブラリへの絶対パスと初期化関数の 2 つのパラメータを取ります。したがって、典型的な呼び出しの例は次のとおりです:

local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")

loadlib 関数は、指定されたライブラリをロードして Lua に接続しますが、ライブラリを開きません (つまり、初期化関数を呼び出しません)。初期化関数を Lua の関数として使用できるため、Lua で直接呼び出すことができます。

動的ライブラリのロード時または初期化関数の検索時にエラーが発生した場合、loadlib は nil とエラーメッセージを返します。前のコードを変更してエラーを検出し、初期化関数を呼び出すことができます:

local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\windows\luasocket.dll",这是 Window 平台下
local f = assert(loadlib(path, "luaopen_socket"))
f()  -- 真正打开库

一般に、バイナリ リリース ライブラリには、前のコード セグメントと同様のスタブ ファイルが含まれていることが予想されます。バイナリ ライブラリをインストールするときに、それをどこかに配置できます。ディレクトリを変更する場合は、スタブ ファイルに対応するバイナリ ライブラリの実際のパスを変更するだけで済みます。

スタブファイルが配置されているディレクトリをLUA_PATHに追加すると、require関数を使用してCライブラリをロードできます。