在突破它时如何清除通用的迭代器函数

时间:2017-02-27 11:06:45

标签: c lua

我在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->databreak?在这种情况下,我的iteratorfunction无法进行清理,因为它并未被完全调用。相反,泛型for循环在我的迭代器函数完成之前退出。

准确地说,如果脚本使用fclose()退出,我如何确保在s->file上调用free()并在s->data上调用break在我完成之前我的通用for循环?

我已经查看了Lua源代码,而io.lines似乎使用元数据和垃圾收集器来确保文件句柄已关闭,但我真的不明白这是如何工作的。它看起来很复杂,我不确定我是否应该以类似的方式这样做,或者是否有更简单的解决方案。

请注意,我仍然使用Lua 5.0,因此任何有关解决方案的建议都应牢记这一点。谢谢!

1 个答案:

答案 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;
}

经过测试,这确实很好。问题解决了。不知道为什么人们试图关闭这个问题。