我在C中实现了一个泛型for迭代器函数。返回泛型for循环的迭代器函数的函数首先使用lua_newuserdata()
为迭代器函数分配状态信息,如下所示:
struct mystate *s = (struct mystate *) lua_newuserdata(L, sizeof(struct mystate));
s->firstcall = 1;
然后我把指针作为一个向上值推到我的C闭包,像这样:
lua_pushvalue(L, lua_gettop(L));
lua_pushcclosure(L, iteratorfunction, 1);
我的迭代器函数然后从第一个上值检索指针,并对其进行一些分配,如下所示:
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;
}
...
}
我的问题是如果脚本使用break
在通用for循环完成之前退出它,我应该如何确保s->file
和s->data
被正确释放?在这种情况下,我的iteratorfunction
不能做清理,因为它没有被调用所有的方式。相反,泛型for循环在迭代器函数完成之前就退出了。
准确地说,如果脚本使用break
在完成之前退出我的泛型for循环,我如何确保fclose()
在s->file
上被调用,free()
在s->data
上被调用?
我看过Lua源代码,io.lines
似乎使用了元表和垃圾收集器来确保文件句柄被关闭,但我真的不明白这是如何工作的。它看起来相当复杂,我不确定我是否应该以类似的方式这样做,或者是否有一个更容易的解决方案。
请注意,我仍然在Lua 5.0上,所以任何解决方案的建议都应该记住这一点。谢谢你,谢谢
2条答案
按热度按时间jjhzyzn01#
为了回答我自己的问题,我现在使用Egor建议的终结器(
__gc
元方法)。在代码中,它看起来像这样:首先,我们需要创建一个元表,我们可以使用它的
__gc
来进行清理:然后我们需要将用户数据与元表关联起来,这样一旦Lua决定删除用户数据,我们的
__gc
方法就会被调用,因此我们这样做:最后,我们需要实现
iteratorfunction_gc
来进行实际的清理。这可能看起来像这样:测试了它,这做的工作真的很好。问题解决了不知道为什么人们试图关闭这个问题。
bz4sfanl2#
不幸的是,Lua没有提供一种在自定义迭代器中进行清理的方法。然而,有一个非常简单的替代方案:请改用函数样式的循环。
例如,而不是这个结构:
你可以这样实现它:
在这种情况下,
mylines()
可以很容易地检测到函数何时返回true
,从而知道何时取消循环并执行清理。实际上我更喜欢函数式的循环,因为它们也允许返回其他值来指示循环内部的状态,所以这是一种有效的双向通信方式。对于自定义迭代器,您需要采取一些更简单的方法,例如传递状态表或调用终结器函数。
函数式循环的唯一缺点是,你不能在循环上下文中从 parent 函数返回。但根据我的经验,这是很少需要的。