Nested lua_CFunction calls

395 views Asked by At

What is the best way to deal with nested lua_CFunction calls? Assume I have two function like this:

static int function2(lua_State *L) {
   int i = luaL_checkint(L, 1);

   /* do something */

   return 1;
};

static int function1(lua_State *L) {
   struct udata *u = luaL_checkudata(L, 1, UDATA_METATABLE);
   int i = luaL_checkint(L, 2);

   /* do something */

   /* this does not work, first on call stack is udata, not int */
   return function2(L);
};

The function call as above does not work. One option is to modify function2() to use the last element (index -1) on stack, but this is not a sollution in general since function2() might be called from various places with different call stacks. Another way would be to replace the return function2(L); by

lua_pushcfunction(L, function2);
lua_pushvalue(L, 2);
lua_call(L, 1, 1);  /* need to know number of results */

I assume this gives function2() its own call stack so there is no need to modify it. But this sollution seems overly complicated for functions with more parameters since it requires duplicating all of them on the stack.

tl;dr: What is the recommended way/a good way to call a lua_CFunction from inside another one?

3

There are 3 answers

0
Timma On

In function1 you are expecting the bottom of the stack to be user data. When you call function2 directly, the LuaState has not changed and therefore the bottom is still user data.

You can successfully call function2 from function1 by ensuring an integer is at index 1.

You could do this by calling lua_insert(L, 1) which will move the top (assuming index 2), to index 1.

You could also do this by popping all values the pushing the integer back on:

lua_pop(L, lua_gettop(L));
lua_pushnumber(L, i);
return function2(L);
0
Etan Reisner On

I'd say that calling it through lua is the recommended way to do that but if you don't want to do that for some reason then Timma's suggestions are the right ones.

1
moteus On

lua_CFunction is not fully Lua function. It just a way to create Lua function/closure.

static int function1(lua_State *L) {
    ....
    int top = lua_gettop(L);
    lua_pushcfunction(L, function2);
    lua_pushvalue(L, 2);
    lua_call(L, 1, LUA_MULTRET);
    return lua_gettop(L) - 1;
}

The Lua equivalent is

function function1(arg)
  return (function(arg) --[[body of f2]] end)(arg)
end

So you create each time new function and call it. For C function it is quite ok because it no need to compile and you do not need upvalue. Also Lua 5.2 introduce light function for that. But if you want equivalent for

int i = 1
local function function2(arg)
  i = i + 1
  ...
end

function function1(arg)
  return function2(arg)
end

You need a way to find real Lua function e.g. (not tested)

int f2_ref;

static int function1(lua_State *L) {

  ...

  -- push `function2` on stack
  lua_rawgeti(L, LUA_REGISTRYINDEX, f2_ref);

  -- as above
}

static int function2(lua_State *L) {
  int my_upvalue = lua_tonumber(L, lua_upvalueindex(1));
  my_upvalue++;
  lua_pushnumber(L, my_upvalue);
  lua_replace(L, lua_upvalueindex(1));


  ...
}

int luaopen_foo(lua_State *L){
  -- Here we create instance of Lua function(closure)
  lua_pushnumber(L, 1);
  lua_pushcclosure(L, function2, 1);
  f2_ref = luaL_ref(L, LUA_REGISTRYINDEX);

  lua_pushcclosure(L, function1, 0);
  return 1;
}