将枚举绑定到 Lua |
|
以下是一个非常简单(且不完整)的示例。目标是创建一个类似 cron 的工具,具有非常简单的接口
-- add a task to be run on a given day of the week cron.add("Monday", task) -- return an iterator of tasks for a given day of the week cron.tasks("Wednesday")
以下代码实际上并没有实现这些函数;它只是概述了一种引入星期几枚举的方法。大部分代码是样板代码。
/* The auxiliary function to convert an argument to an enum This function assumes that the weekday table is the first upvalue, which in this case it is. */ static int get_weekday (lua_State *L, int argno) { int weekday; lua_pushvalue(L, argno); lua_gettable(L, lua_upvalueindex(1)); weekday = lua_tonumber(L, -1); lua_pop(L, 1); if (weekday == 0) { /* This works because there is no 0 in the weekday table and lua_tonumber returns 0 for any non-number. We could have used a lua_isnil() test instead, had 0 been a possible return. */ luaL_typerror(L, argno, "weekday"); } return weekday; } /* These functions actually need to be implemented. All that is illustrated here is how to get the enum value. */ static int cron_add (lua_State *L) { int weekday = get_weekday(L, 1); // ... } static int cron_tasks (lua_State *L) { int weekday = get_weekday(L, 1); // ... } /* As usual, a luaL_reg of function names and functions */ static const luaL_reg cron_funcs[] = { {"add", cron_add}, {"tasks", cron_tasks} {NULL, NULL} }; /* The function which creates the library; it must start by creating the enum table. This function needs to be added to the list of open functions called when the lua_State is created (unless the library is going to be loaded dynamically, of course, but that requires the same function.) */ int luaopen_cron (lua_State *L) { /* make the weekday table */ lua_newtable(L); lua_pushliteral(L, "Monday"); lua_pushnumber(L, 1); lua_settable(L, -3); lua_pushliteral(L, "Tuesday"); lua_pushnumber(L, 2); lua_settable(L, -3); lua_pushliteral(L, "Wednesday"); lua_pushnumber(L, 3); lua_settable(L, -3); lua_pushliteral(L, "Thursday"); lua_pushnumber(L, 4); lua_settable(L, -3); lua_pushliteral(L, "Friday"); lua_pushnumber(L, 5); lua_settable(L, -3); lua_pushliteral(L, "Saturday"); lua_pushnumber(L, 6); lua_settable(L, -3); lua_pushliteral(L, "Sunday"); lua_pushnumber(L, 7); lua_settable(L, -3); /* Register the library */ luaL_openlib(L, "cron", cron_funcs, 1); return 1; }
pushliteral、pushnumber、settable 的序列很丑陋,难以维护。幸运的是,我们可以用宏来解决这个问题。
#define LUA_ENUM(L, name, val) \ lua_pushlstring(L, #name, sizeof(#name)-1); \ lua_pushnumber(L, val); \ lua_settable(L, -3);
现在我们甚至可以这样做
lua_newtable(L); { int i = 1; LUA_ENUM(L, Monday, i++); LUA_ENUM(L, Tuesday, i++); LUA_ENUM(L, Wednesday, i++); LUA_ENUM(L, Thursday, i++); LUA_ENUM(L, Friday, i++); LUA_ENUM(L, Saturday, i++); LUA_ENUM(L, Sunday, i++); }
但我们可以做得更好,只需要一点 CPP 魔法。我们可以使用相同的数据来创建 C 枚举和 Lua 转换表。
#define C_ENUM_HELPER(cname, luaname) cname, #define LUA_ENUM_HELPER(cname, luaname) LUA_ENUM(L, luaname, cname) #define WEEKDAY \ E(WEEKDAY_MONDAY, Monday) \ E(WEEKDAY_TUESDAY, Tuesday) \ E(WEEKDAY_WEDNESDAY, Wednesday) \ E(WEEKDAY_THURSDAY, Thursday) \ E(WEEKDAY_FRIDAY, Friday) \ E(WEEKDAY_SATURDAY, Saturday) \ E(WEEKDAY_SUNDAY, Sunday) /* In the c header file: */ #define E C_ENUM_HELPER enum Weekday { WEEKDAY WEEKDAY_LAST /* Avoid the trailing comma problem */ }; #undef E /* In the Lua file: */ #define E LUA_ENUM_HELPER lua_newtable(L); WEEKDAY #undef E