我正在尝试编写Lua绑定,以便可以在userdata上调用任意函数。我一直在努力的MCV示例如下。
总结:我们将C函数newarray
推送到Lua全局变量中的一个表,以便可以创建一个新的数组对象。假设该数组是数据库记录。在使用newarray
生成它之后,我想对它执行两种操作(对于这个错误的示例):访问元素并销毁对象。
由于我不知道会有多少个元素(在现实世界的例子中),我决定使__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;
}
我编译:
gcc -std=c99 -Wall -fPIC -shared -o TestArray.so test.c -llua
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
的函数,我不知道为什么。它找到了__index
元方法,所以我不明白为什么它后来在其他地方。为什么会这样做,我做错了什么?
Lua版本:5.1.4。
答案 0 :(得分:2)
__ index预计会返回一个值。你没有。
具体来说,当你这样写:
a:destroy()
这相当于:
getmetatable(a).__index(a, "destroy")(a)
即。调用__index元方法,然后调用它返回的任何内容,并以a
为参数。
但是,如果我们查看您的__index实现,它并不尊重该合同:
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;
}
然后让你的__index返回该函数而不是调用它:
lua_pushcfunction(L, destroyFunc);
return 1;
此时Lua代码将能够调用该函数。