Lua:调用_索引函数工作,然后抛出错误

Lua:调用_索引函数工作,然后抛出错误,lua,Lua,我正在尝试编写Lua绑定,以便可以在userdata上调用任意函数。下面是我一直在研究的一个MCV示例 总之:我们将C函数newarray推送到Lua globals中的一个表中,这样就可以创建一个新的数组对象。假设数组是数据库记录。在使用newarray生成它之后,我想对它执行两种操作(对于这个糟糕的示例):访问元素和销毁对象 由于我不知道会有多少元素(在现实世界的例子中),我决定将\uu index作为一个函数,并使用if语句来确定该函数是“destroy”还是其他任何东西(即“给我这个元素

我正在尝试编写Lua绑定,以便可以在userdata上调用任意函数。下面是我一直在研究的一个MCV示例

总之:我们将C函数
newarray
推送到Lua globals中的一个表中,这样就可以创建一个新的数组对象。假设数组是数据库记录。在使用
newarray
生成它之后,我想对它执行两种操作(对于这个糟糕的示例):访问元素和销毁对象

由于我不知道会有多少元素(在现实世界的例子中),我决定将
\uu index
作为一个函数,并使用if语句来确定该函数是“destroy”还是其他任何东西(即“给我这个元素”)。如果是“销毁”,则删除该对象;否则,返回请求的元素

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

#define TEST_METATABLE "_test_mt"

typedef struct
{
    int* array;
} array_t;

int newArray(lua_State* L)
{
    assert(lua_gettop(L) == 0);
    array_t* array = lua_newuserdata(L, sizeof(array_t));
    array->array = malloc(sizeof(int) * 10);

    for (int i = 0; i < 10; i++)
        array->array[i] = i;

    /* Set metatable */

    lua_getfield(L, LUA_REGISTRYINDEX, TEST_METATABLE);
    lua_setmetatable(L, -2);

    return 1;
}

int indexFunc(lua_State* L)
{
    int argc = lua_gettop(L);
    array_t* array = luaL_checkudata(L, 1, TEST_METATABLE);
    const char* key = luaL_checkstring(L, 2);
    int ret = 0;

    if (!strcmp(key, "destroy"))
    {
        if (argc != 2)
        {
            lua_settop(L, 0);
            luaL_error(L, "Invalid arguments");
        }

        if (array->array)
        {
            free(array->array);
            array->array = NULL;
        }

        printf("Finished destroy\n");

        lua_settop(L, 0);
    }
    else
    {
        if (argc != 2)
        {
            lua_settop(L, 0);
            luaL_error(L, "Invalid arguments");
        }

        if (lua_tointeger(L, 2))
        {
            lua_pushinteger(L, array->array[lua_tointeger(L, 2)]);
        }
        else
        {
            lua_settop(L, 0);
            luaL_error(L, "Bad index supplied");
        }

        lua_remove(L, 2);
        lua_remove(L, 1);
        ret = 1;
    }

    return ret;
}

int luaopen_TestArray(lua_State* L)
{
    /* Set up metatable */

    lua_newtable(L);

    lua_pushliteral(L, "__index");
    lua_pushcfunction(L, indexFunc);
    lua_settable(L, -3);

    lua_setfield(L, LUA_REGISTRYINDEX, TEST_METATABLE);

    /* Set up 'static' stuff */

    lua_newtable(L);

    lua_pushliteral(L, "newarray");
    lua_pushcfunction(L, newArray);
    lua_settable(L, -3);

    lua_setglobal(L, "TestArray");

    return 0;
}
Lua测试程序如下所示:

require("TestArray")

a = TestArray.newarray()

print(a[5])

a:destroy()
输出:

$ lua test.lua
5
Finished destroy
lua: test.lua:7: attempt to call method 'destroy' (a nil value)
stack traceback:
        test.lua:7: in main chunk
        [C]: ?
$

因此,Lua通过检索第6个元素的值(用C表示)并打印它(正如它通过
indexFunc
所做的那样),完成了它应该做的事情。然后它继续执行
indexFunc
中的销毁特定代码,然后尝试查找名为
destroy
的函数,我不知道为什么。它找到了
\u索引
元方法,所以我不明白为什么之后它会在别处查找。为什么会这样,我做错了什么


Lua版本:5.1.4。

\u索引应返回一个值。你的没有

具体来说,当你写这篇文章时:

a:destroy()
这相当于:

getmetatable(a).__index(a, "destroy")(a)
i、 e.调用_index元方法,然后调用它返回的任何内容,并将其作为参数传递给
a

但是,如果我们看一下您的_索引实现,它不尊重该契约:

int indexFunc(lua_State* L)
{
  int argc = lua_gettop(L);
  array_t* array = luaL_checkudata(L, 1, TEST_METATABLE);
  const char* key = luaL_checkstring(L, 2);
  int ret = 0;

  if (!strcmp(key, "destroy"))
  {
    /* ... delete the array ... */
    lua_settop(L, 0);
  }
  else
  {
    /* ... push the value ... */
  }

  return ret; /* since key == "destroy", ret == 0 here */
}
如果键是
“destroy”
,则不返回函数;相反,它会立即销毁数组并不返回任何内容,这在本例中相当于返回nil。然后lua代码尝试调用返回的nil并分解

相反,您需要创建一个单独的函数来进行销毁,例如

int destroyFunc(lua_State * L) {
  array_t array = luaL_checkudata(L, 1, TEST_METATABLE);
  free(array->array);
  array->array = NULL;
  return 0;
}
然后让您的_索引返回该函数,而不是调用它:

lua_pushcfunction(L, destroyFunc);
return 1;

此时Lua代码将能够调用该函数。

\uu index
元方法应仅检索键“destroy”的值,也就是说,
indexFunc
必须仅返回值(函数“destroy”),而不执行此函数。析构函数应该作为单独的函数
intdestrofunc(lua\u State*L)
实现。快速解决方案:只需将
a:destroy()
替换为
local=a.destroy
:-)@EgorSkriptunoff Ohh,我明白了,这是因为
destroy
后面的括号基本上意味着“调用检索“destroy”的结果作为函数”?如果您想将其作为答复提交,我将接受,因为您没有从评论中得到任何当之无愧的代表。:)
lua_pushcfunction(L, destroyFunc);
return 1;