將列舉綁定至 Lua

lua-users home
wiki

雖然有許多方法可以將列舉綁定至 Lua,但最類似 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


近期變更 · 喜好設定
編輯 · 記錄
上次編輯於 2010 年 1 月 7 日上午 10:57 GMT (差異)