lua-users home
lua-l archive

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


An even simpler solution to this would be to export (as part of the API) the
macro Lua uses for exception-handling. I understand some other packages
do this.

This would make it much simpler to deal with what seems to me a fairly
standard embedding pattern which is probably quite familiar to people:

int FOO_callback(foo_State *F, const char *some_argument, void *udata)
  int status = 0;
  lua_State *L = (lua_State *)udata;
  // Get our Lua handler
  lua_getglobal(L, "callback");  // *** could throw exception
  // set up arguments
  // code to push the Lua userdata corresponding to F
  lua_pushstring(L, some_argument); // *** could throw exception
  // protectively call
  if (lua_pcall(L, 0, 2, 0)) {
    // gracefully deal with error, perhaps by logging the error string
    lua_pop(L, 1);
    status = FOO_NOT_HANDLED;
  }
  return status;
}

Unfortunately, this can blow up if the section "set up arguments"
encounters, for example, a memory allocation error, which can happen
in something as innocuous as a lua_pushstring(). So to be bullet-proof,
it is necessary to wrap that piece of code in a lua_cpcall, which means
that every callback needs:

-- a callback function which uses cpcall to call:
-- a protected callback function which actually does the work; and:
-- a struct to carry arguments between the first and the second.

Which is about twice as many lines of code.

It would be a lot easier to be able to write something like:

int foo_callback(...) {
  int status = 0;
  LUA_TRY {
    // get callback
    // push arguments
    lua_call(L, 0, 2); // OK, because in a try block
  }
  LUA_CATCH {
    // gracefully deal with error
    status = FOO_NOT_HANDLED;
  }
  return status;
}

Just a thought... but I believe these macros exist anyway (or something
very much like them).

On 20-Aug-04, at 6:33 PM, Mark Hamburg wrote:

Speaking of worrying about optimizing storage allocations to gain improved speed, how complicated would it be for lua_cpcall to not create a function
closure? For example, could one introduce a new primitive type for C
functions without up values that would be much like light userdata?

Or one could use a trampoline that got allocated once and registered
somewhere and which would use the userdata to point to a block containing
the C function to call and the parameter to pass to it. For example:

    int cpcaller( lua_State *L ) {
        struct CCallS *c = cast(struct CCallS *, ud);
        lua_pop( L, 1 );
        lua_pushlightuserdata( L, c->ud );
        return (*c->func)( L );
    }

    static void f_Ccall (lua_State *L, void *ud) {
        lua_pushlightuserdata( L, &cpcaller );
        lua_rawget( L, LUA_REGISTRYINDEX );
        if( lua_isnil( L, -1 ) ) {
            lua_pop( L, 1 );
            lua_pushcfunction( L, &cpcaller );
            lua_pushlightuserdata( L, &cpcaller );
            lua_pushvalue( L, -2 );
            lua_rawset( L, LUA_REGISTRYINDEX );
        }
        setpvalue(L->top, ud);  /* push only argument */
        incr_top(L);
        luaD_call(L, L->top - 2, 0);
    }

One could even improve the efficiency a bit by avoiding pushing the void* as a light userdata for the eventual function if the function were defined to
take a void* parameter. For example:

    int cpcaller( lua_State *L ) {
        struct CCallS *c = cast(struct CCallS *, ud);
        lua_pop( L, 1 );
        return (*c->func)( L, c->ud );
    }

Pushing this further, we might get:

    int lua_cvpcall(
lua_State *L, int (*func)( lua_State *L, va_list *args ), ... );

That would allow one to write:

    lua_cvpcall( L, &myFunc, arg1, arg2, arg3 );

This would call myFunc with the state L and the supplied arguments packaged
up as a va_list.

Pushing in a different direction, it might be attractive to support a
non-protected version as well.

I could also see it being attractive to allow cpcall to return results if it
doesn't fail. The stack can then be restored with lua_settop.

Mark