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

モジュールはカプセル化されたライブラリのようなものです。Lua 5.1 から、Lua には標準のモジュール管理メカニズムが追加され、共通のコードを1つのファイルに置き、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 ファイル

-- test_module.lua ファイル
-- module モジュールは前述の module.lua です
require("module")
 
print(module.constant)
 
module.func3()

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

これは定数です
これはプライベート関数です!

または、ロードしたモジュールにエイリアス変数を定義して、呼び出しを便利にします:

test_module2.lua ファイル

-- 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/" パスを LUA_PATH 環境変数に追加します:

#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"、これは Windows プラットフォームの場合
local f = assert(loadlib(path, "luaopen_socket"))
f()  -- 実際にライブラリを開く

一般的に、バイナリ配布ライブラリには前述のコードと似た stub ファイルが含まれていることが期待されます。バイナリライブラリをインストールするときは任意のディレクトリに置くことができ、stub ファイル内のバイナリライブラリに対応する実際のパスを変更するだけで済みます。

stub ファイルがあるディレクトリを LUA_PATH に追加します。この設定により、require 関数を使用して C ライブラリをロードできるようになります。

その他の拡張