C中调用LUA回调,LUA注册表

实现原理:

通过将LUA中得回调函数存入LUA注册表中来保存LUA函数,然后在需要回调时从LUA注册表中取出LUA函数进行调用

下面是一些预备知识:(学习两个重要的函数)

原汁原味的英文解释的最透彻,翻译的话就会加入自己的理解


LUA_GLOBALSINDEX

LUA_ENVIRONINDEX

LUA_REGISTRYINDEX

Lua provides a registry, a pre-defined table that can be used by any C code to store whatever Lua value it needs to store. This table is always located at pseudo-index LUA_REGISTRYINDEX. Any C library can store data into this table, but it should take care to choose keys different from those used by other libraries, to avoid collisions. Typically, you should use as key a string containing your library name or a light userdata with the address of a C object in your code


int luaL_ref (lua_State *L, int t);

Creates and returns a reference, in the table at index t, for the object at the top of the stack (and pops the object).

A reference is a unique integer key. As long as you do not manually add integer keys into table t, luaL_ref ensures the uniqueness of the key it returns. You can retrieve an object referred by reference r by calling lua_rawgeti(L, t, r). Function luaL_unref frees a reference and its associated object.

If the object at the top of the stack is nil, luaL_ref returns the constant LUA_REFNIL. The constant LUA_NOREF is guaranteed to be different from any reference returned by luaL_ref.


void luaL_unref (lua_State *L, int t, int ref);

Releases reference ref from the table at index t (see luaL_ref). The entry is removed from the table, so that the referred object can be collected. The reference ref is also freed to be used again.

If ref is LUA_NOREF or LUA_REFNIL, luaL_unref does nothing.

这样使用

将lua函数保存到这个注册表。lua提供了在表里增加一个条目的API,luaL_ref,返回值是新条目的key,一个整数。

int handler = luaL_ref( L , LUA_REGISTRYINDEX );

下面是简化后的代码,是保存lua中传入的一个function,并将其作为回掉函数调用

代码如下:

 int Test_callback(lua_State* luaState)
{
    int topindex = lua_gettop(luaState);
    assert(topindex == 1);
    /** check stack top is lua function */
    if (!lua_isfunction(luaState, topindex))
      generate_error(luaState, "param #1 is not function\n");

    /** register lua function to register table and we can use with func id
        luaL_ref will pops the stack top */
    int luafuncid = luaL_ref(luaState, LUA_REGISTRYINDEX);

    { /** Wait a moment */
        /** get function by ref id */
        lua_rawgeti(luaState, LUA_REGISTRYINDEX, luafuncid);
        if (!lua_isfunction(luaState, -1))
            generate_error(luaState, "callback is not a lua function\n");

        /** push params */ 
        
        /** call lua function */
        if (lua_pcall(luaState, 0, 0, 0) != 0) {
            /** error message on the stack top, lua_error will jump */
            lua_error(luaState);
        }
        luaL_unref(luaState, LUA_REGISTRYINDEX, luafuncid);
    }
    return 0;

}

 
extern int luaopen_Test_luabinding(lua_State* luaState)
{
    static const struct luaL_Reg funcs[] = {
        {"callback", Test_callback},
        {NULL, NULL}
    };
    luaL_register(luaState, "test", funcs);
    return 1;
}