【问题标题】:How to do clean up in generic for iterator function when breaking out of it打破迭代器函数时如何在通用中进行清理
【发布时间】:2017-03-01 06:41:02
【问题描述】:

我正在 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->files->data 被正确释放?在这种情况下,我的iteratorfunction 无法进行清理,因为它并没有一直被调用。相反,通用 for 循环在我的迭代器函数完成之前退出。

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

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

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

【问题讨论】:

  • 恐怕除了使用终结器之外没有其他解决方案(__gcmetamethod)
  • 谢谢,我刚刚根据您的建议发布了完整的答案。

标签: c lua


【解决方案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);

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

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

【讨论】:

    猜你喜欢
    • 2020-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-12
    • 2011-10-20
    • 2020-12-02
    相关资源
    最近更新 更多