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