我在C中实现了迭代器函数的泛型。返回泛型for循环的迭代器函数的函数首先使用lua_newuserdata()
为迭代器函数分配状态信息,如下所示:
struct mystate *s = (struct mystate *) lua_newuserdata(L, sizeof(struct mystate));
s->firstcall = 1;
然后我将指针作为up值推到我的C闭包中,如下所示:
lua_pushvalue(L, lua_gettop(L));
lua_pushcclosure(L, iteratorfunction, 1);
我的迭代器函数然后从第一个upvalue中检索指针并对其进行一些分配,如下所示:
static int iteratorfunction(lua_State *L)
{
struct mystate *s = (struct mystate *) lua_touserdata(L, lua_upvalueindex(1));
if(s->firstcall) {
s->file = fopen(...);
s->data = malloc(...);
...
s->firstcall = 0;
}
...
}
现在我的问题是:如果脚本在完成之前使用s->file
退出泛型for循环,我应该如何确保正确释放s->data
和break
?在这种情况下,我的iteratorfunction
无法进行清理,因为它并未被完全调用。相反,泛型for循环在我的迭代器函数完成之前退出。
准确地说,如果脚本使用fclose()
退出,我如何确保在s->file
上调用free()
并在s->data
上调用break
在我完成之前我的通用for循环?
我已经查看了Lua源代码,而io.lines
似乎使用元数据和垃圾收集器来确保文件句柄已关闭,但我真的不明白这是如何工作的。它看起来很复杂,我不确定我是否应该以类似的方式这样做,或者是否有更简单的解决方案。
请注意,我仍然使用Lua 5.0,因此任何有关解决方案的建议都应牢记这一点。谢谢!
答案 0 :(得分:1)
要回答我自己的问题,我现在正在使用Egor建议的终结器(__gc
元方法)。在代码中,这看起来像这样:
首先我们需要创建一个我们可以用__gc
进行清理的元表:
#define PRIVATEHANDLE "PRIVATE*"
luaL_newmetatable(L, PRIVATEHANDLE);
lua_pushliteral(L, "__index");
lua_pushvalue(L, -2);
lua_rawset(L, -3);
lua_pushstring(L, "__gc");
lua_pushcclosure(L, iteratorfunction_gc, 0);
lua_settable(L, -3);
lua_pop(h, 1);
然后我们需要将用户数据与metatable相关联,以便在Lua决定删除我们的用户数据时调用我们的__gc
方法,因此我们这样做:
struct mystate *s = (struct mystate *) lua_newuserdata(L, sizeof(struct mystate));
memset(s, 0, sizeof(struct mystate));
luaL_getmetatable(L, PRIVATEHANDLE);
lua_setmetatable(L, -2);
最后,我们需要实施iteratorfunction_gc
来进行实际清理。这看起来像这样:
static int iteratorfunction_gc(lua_State *L)
{
struct mystate *s = (struct mystate *) luaL_checkudata(L, 1, FILEHANDLE);
if(s->file) fclose(s->file);
if(s->data) free(s->data);
...additional cleanup here...
return 0;
}
经过测试,这确实很好。问题解决了。不知道为什么人们试图关闭这个问题。