如何在跳出循环时清理通用迭代器函数中的内容

我正在C语言中实现一个通用的迭代器函数。返回通用循环的迭代器函数的函数首先使用 lua_newuserdata() 来分配迭代器函数的状态信息,如下所示:

struct mystate *s = (struct mystate *) lua_newuserdata(L, sizeof(struct mystate));
s->firstcall = 1;

然后,我将该指针作为闭包的upvalue推入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;
    }

    ...
}

现在我的问题是:如果脚本在完成之前使用“break”退出通用for循环,如何确保在s->files->data上正确释放内存?在这种情况下,我的iteratorfunction不能执行清理,因为它没有完全被调用时,通用for循环就已经退出了。

具体来说,如果脚本在完成之前使用“break”退出我的通用for循环,怎样才能确保在s->file 上调用fclose(),在s->data 上调用free()

我查看了Lua的源代码和io.lines似乎使用元表和垃圾回收器确保文件句柄被关闭,但我不太理解这是如何工作的。看起来相当复杂,我不确定我是否应该以类似的方式进行操作,还是是否有更简单的解决方案适用于我的情况。

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

点赞
用户1197719
用户1197719

回答我的问题,我现在使用了终结器(__gc元方法)建议使用Egor。在代码中它看起来像这样:

首先我们需要创建一个元表,其中我们可以使用__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);

然后我们需要将用户数据与元表相关联,以便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);

    ...这里可以补充其他的清理操作...

    return 0;
}

经过测试,这能够非常好地工作。问题解决了。不知道为什么有人想关闭这个问题。

2017-02-28 22:41:02