エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

Lua 5.1とLua 5.2の非互換について

Io話を続けるつもりだったのだけれども。ネタを仕込んでいる間にわけあってLuaに手を出すことになり。そういや昨年末に新しいバージョン、Lua 5.2がリリースされたっけ、と思って調べてみたら。気をつけないとならない互換性のない変更があるのを見つけたので、それのめも。

ライブラリの登録の方法が変更になりました

バージョン5.2とそれ以前のバージョンとの非互換の部分について、リファレンスマニュアルの中に「Incompatibilities with the Previous Version」という項目があります。そこに「Function luaL_register is deprecated.」と、さらっと書かれています。
この[http://www.lua.org/manual/5.1/manual.html#luaL_register:title=luaL_register]という関数はライブラリを登録するのに使う関数なので、最低この1カ所とはいえ既存のライブラリで必ず書き換える必要がある変更です。

マニュアルでは[http://www.lua.org/manual/5.1/manual.html#luaL_register:title=luaL_register]の代わりに[http://www.lua.org/manual/5.2/manual.html#luaL_setfuncs:title=luaL_setfuncs]を利用するように記載されています。あるいはもう少し簡単に扱えるようにした[http://www.lua.org/manual/5.2/manual.html#luaL_newlib:title=luaL_newlib]マクロを使うことになります。

変更の理由ですが。
[http://www.lua.org/manual/5.1/manual.html#luaL_register:title=luaL_register]の書式を見てもらうとわかるのですが、この関数はグローバル環境に名前を登録してしまいます。結果としてライブラリを追加で読み込むたびにグローバル環境を「よごして」しまうことになります。これを嫌っての変更のようです。全体的にもグローバル環境に影響を与えない方向に変更が加わっているようです。

具体的にライブラリを書いてみる

まずC++あるいはCでライブラリを書いてビルドします。

extern "C"
{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}

#include <sstream>

namespace
{

int Hoge_hoge(lua_State* L)
{
    const char* s = luaL_checkstring(L, 1);
    std::stringstream ss;
    ss << "<hoge>" << s << "</hoge>";
    lua_pushstring(L, ss.str().c_str());
    return 1;
}

const luaL_Reg hogelib[] =
{
    { "hoge", Hoge_hoge },
    { NULL,   NULL      }
};

} // namespace

extern "C" int luaopen_hoge(lua_State* L)
{
    luaL_register(L, "Hoge", hogelib);
    return 1;
}

shared libraryを作ります。

g++ -shared -o hoge.dll hoge.cpp -llua

Macな人のばあい。

g++ -dynamiclib -o hoge.so hoge.cpp -llua

MS-Windowsな人のばあい。

g++ -shared -o hoge.dll hoge.cpp -llua


ライブラリを使うサンプル。

require "hoge"

print(Hoge.hoge("ほげ"))


hoge.soがあるフォルダで実行してください。

$ lua sample.lua 
ほげ

オブジェクトHogeLuaスクリプト中で宣言されていませんが利用できます。ライブラリの中で名前付けされているためです。


Lua 5.2ではライブラリのエントリポイントluaopen_hogeを次のように書き換えます。

extern "C" int luaopen_hoge(lua_State* L)
{
    luaL_newlib(L, hogelib);
    return 1;
}


見てのとおりHogeという名前は登録されません。
なので先ほどのスクリプトをそのまま実行すると、こうなります。

$ lua sample.lua 
lua: sample.lua:3: attempt to index global 'Hoge' (a nil value)
stack traceback:
	sample.lua:3: in main chunk
	[C]: in ?


requireは読み込んだライブラリのオブジェクトを返すので、スクリプトを次のように書き換えます。

Hoge = require "hoge"

print(Hoge.hoge("ほげ"))

これで期待どおりに動作するようになりました。このスクリプトLua 5.1でも動作するので、当面5.2を使う予定がなというばあいでもLua側はこのように書いておくのがよさそうです。


この例ではグローバル環境でHogeを宣言しているので結果的には同じですが、特定のスコープでだけライブラリを使うばあいにはグローバル環境をよごさずにすみます。


5.1用、5.2用のどちらのライブラリも作成したいばあい、LUA_VERSION_NUMというマクロにバージョン番号が定義されているのでそれを見て登録関数を使い分けることになるかと。

extern "C" int luaopen_hoge(lua_State* L)
{
#if LUA_VERSION_NUM >= 502
    luaL_newlib(L, hogelib);
#else
    luaL_register(L, "Hoge", hogelib);
#endif
    return 1;
}