例えばC++で次のように列挙体を定義して、そのままの名前でLuaスクリプト側でも使いたいわけです。
enum Enum1 {
E1 = 0x200,
E2,
E3,
E4,
};
↓↓↓こう書きたい↓↓↓
if foo == E2 then
bar()
end
でもそれをやるには、定義した列挙体を全部Luaのグローバル変数として登録しなければなりません。
luabind::object g = luabind::globals(L); g["E1"] = E1; g["E2"] = E2; g["E3"] = E2; …
enum定義した後で、Luaの方にも登録しなければなりません。
列挙体を1個増やすと、ソースを2箇所直さないといけない…。
これをいちいちやるのは、列挙体がたくさんあるととても面倒なので、なんとかならないものかと思っていたのですが、Boost.Preprocessorを使って楽になりそうだったので試してみました。
で、こんなふうになりました。
なにをやってるかというと、列挙体として定義したいものをシーケンスに入れておいて、
・SEQ_TO_ENUMマクロで、シーケンスの内容から列挙体を定義するコードをつくる
・SEQ_TO_LUAGLOBALSマクロで、シーケンスの内容からLuaグローバル変数を登録するコードをつくる
という感じです。
#include <iostream>
#include <boost/preprocessor.hpp>
#include <lua.hpp>
#include <luabind/luabind.hpp>
// シーケンスを渡してenum定義をつくる
// seq: 定義したい列挙体の要素名を並べたシーケンス
// name: 列挙体の名前
// value: 先頭の要素の値
#define SEQ_TO_ENUM(seq, name, value) \
enum name { \
BOOST_PP_SEQ_HEAD(seq) = value, \
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TAIL(seq)) \
};
#define SEQ_TO_LUAGLOBALS_ELEM(r, data, elem) data[BOOST_PP_STRINGIZE(elem)]=elem;
// シーケンスからLuaグローバル変数への登録
// seq: 定義したい列挙体の要素名を並べたシーケンス
// L: Lua VM
#define SEQ_TO_LUAGLOBALS(seq, L) { \
luabind::object o=luabind::globals(L); \
BOOST_PP_SEQ_FOR_EACH(SEQ_TO_LUAGLOBALS_ELEM, o, seq) \
}
// 列挙体を定義する。0x200からはじまるEnum1
// あとはここをいじるだけで良い!
#define ENUM1 (E1)(E2)(E3)(E4)
SEQ_TO_ENUM(ENUM1, Enum1, 0x200)
int main()
{
lua_State* L = lua_open();
luaL_openlibs(L);
luabind::open(L);
// 列挙体Enum1をLuaに登録する
SEQ_TO_LUAGLOBALS(ENUM1, L);
// ためしに表示してみる
luaL_dostring(L, "print(E1,E2,E3,E4)");
return 0;
}
こうすると、列挙体の数を増やしたいと思ったら、上記のソースで言えばENUM1のところを変更するだけで完了、となって、最初の例よりもかなり楽になりました。
と、本当はluabindのソースコードを読み解いている途中だったんですが、脇道にそれてしまいました。
boostを使ったコードを読むのは、ほんと難しいですね…。