Binding Enums To Lua

lua-users home
wiki

Although there are a lot of ways to bind enums into Lua, the most Lua-like way is to simply represent each enum as a character string. This avoids issues like name pollution, and provides the scripter with a simple and easy to understand interface.

Here is a very simple (and incomplete) example. The goal was to create a cron-like facility, with a very simple interface:


-- 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 following code does not actually implement these functions; it simply sketches a way of introducing the enums for the days of the week. Most of it is boilerplate code.




/*

   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;

}

        

The sequence of pushliteral, pushnumber, settable is a big ugly and hard to maintain. Fortunately, we can solve that with a macro.


#define LUA_ENUM(L, name, val) \

  lua_pushlstring(L, #name, sizeof(#name)-1); \

  lua_pushnumber(L, val); \

  lua_settable(L, -3);



Now we can even do this:


  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++);

  }

But we can do even better than that, with a little bit of CPP magic. We could use the same data to create both a C enum and the Lua conversion table.


#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




RecentChanges · preferences
edit · history
Last edited January 7, 2010 9:57 am GMT (diff)