例えば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を使ったコードを読むのは、ほんと難しいですね…。