lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

The only reason I can think of to transparently allow tables to be passed as functions is for the case where you have a table similar to the following:

foo = setmetatable({}, {__call = function()
   -- Do something
__metatable = "Stay away!"});

I think it is reasonable to argue that such a case could arise, where someone wants to grant access to a table along with a restricted-access function and wants to ensure that the table will never be changed. The same could be (probably even more definitively) argued for userdata.

The problem with the above is that this function can't be accessed. (getmetatable won't allow you to get to the __call field, and we're going to assume the function is otherwise inaccessible.) 

Do I think such a feature should be transparent/automatic, though? Probably not. It would degrade both code readability and cases where both tables and functions may want to have been passed, but having different purposes.

I think a good solution would be to have a function that could be called to grab a reference to the function that would have otherwise been used. Something similar to the following (warning: uncompiled, untested, and written in an email client!)

/* Converts a given argument to a closure.
 * Accepts a single argument. Returns nil
 * if the conversion cannot be made.
static int lua_toclosure(lua_State *L)
   int mt = 0;

   luaL_checkany(L, 1);
   switch(lua_type(L, 1))
   case LUA_TNIL:
      lua_pop(1);       /* Clean the stack */
      lua_pushnil(L);   /* nil can never be converted */
      break;            /* Already on top of stack, return self */
      /* Look for a __call metamethod */
      if (luaL_getmetafield(L, 1))
         lua_remove(L, 1); /* Clean stack, move __call to pos 1 */
         /* If we need to, try to convert __call into a function */
         if (lua_type(L, 1) != LUA_TFUNCTION)
            lua_getglobal(L, "toclosure");
            /* ensure it exists; if not, give up */
            if (lua_type(L, -1) == LUA_TNIL)
               lua_remove(L, 1);
               break; /* nil is still at top of stack */
            /* Move func -> 1, orig. __call -> 2 */
            lua_call(L, 1, 1); /* Leaves return on top of stack */
         lua_pop(L, 1);

   return 1;

This way, users of Lua can choose if they want to grab the function call and use that as a parameter for something, while authors can ensure if they protect their metatables that it can't be modified.

Thoughts on this solution?
-- Matthew P. Del Buono