我在一个资源有限的系统中使用Lua,与C一起使用。
我正在使用的库创建了一些对它创建的对象的引用(作为指针),这些对于稍后访问这些对象很有用。为了将此库的功能公开给Lua,在创建此类对象时,此引用将返回到Lua脚本。
用户可以根据自己的喜好存储此引用,因为这使得以后的调用非常方便。
示例案例:
ref = MyLib.createObject("some", arguments)
local ref = MyLib.createObject("some", arguments)
table_of_refs[45] = MyLib.createObject("some", arguments)
-- etc...
不幸的是,这些引用可能会在Lua脚本的范围之外被破坏(从C内部)。因此,这些引用可能变得无效。
当时,我的代码可以毫无问题地处理这些无效的引用。所有这些指针在库中实际使用之前都经过验证,因此代码是安全的。
然而,在Lua用户的观点上似乎有点令人困惑,因为没有办法判断这个引用是否仍然有效(这不是很重要,但我仍然希望改进它)。
我想要的是以下内容。我想从C迭代Lua存储的所有lightuserdata。如果lightuserdatum不再有效,请将其设置为nil。这样,在下次使用时,变量将有效或为零,从而为用户提供更好的API。
有没有办法实现这个目标?我可以从C迭代所有Lua知道的lightuserdata(无论它们存储在何处,本地/全局/表等),并修改它们?
无法从C访问本地lua变量。因此,即使您迭代了某种类型的lua对象,也无法访问至少一个区域(局部变量)。
您可以遍历全局表中的所有内容,如Max的答案所示:Loop through all Lua global variables in C++
如果要对lua_islightuserdata进行测试,然后将lightuserdata的值与要删除的指针进行比较,则应该能够识别所需的全局值。
lua_pushglobaltable(L);
lua_pushnil(L);
while (lua_next(L,-2) != 0)
{
if (lua_islightuserdata(L, -1))
{
//TODO: compare against the lightuserdata pointer value you are looking for
void* mypointer = lua_touserdata(state, -1);
name = lua_tostring(L,-2); // Get key(-2) name
//TODO: do whatever with the value; probably set to nil by name in the global table
}
lua_pop(L,1);
}
lua_pop(L,1);
但是,如果要在表中搜索它,则必须包含对lua_istable的附加检查,然后以相同的方式递归迭代表的值。
...
else if (lua_istable(L, -1)
{
//TODO: iterate each value in the table searching for lightuserdata
// or tables with further levels of recursion
}
...
但是,我认为,在MyLib上提供一个名为“isValid”的方法会更直接,它在使用之前返回值是否仍然有效。
local ref = MyLib.createObject("some", arguments)
...
local isvalid = MyLib.isValid(ref)
那么您不受本地范围的限制。另外,假设您在lua代码中无论如何都要进行零检查(以查看值是否从您的下方更改),这在代码方面基本上没有任何成本。
我认为你已经在概念上走错路了。
为什么创建的对象可以从C中销毁?正是Lua脚本触发了创建,所以它也应该是Lua脚本,它会触发破坏。
您可以只设置一个指示对象无效状态的标志,而不是销毁该对象。您可以另外实现一个回调机制,以便C库可以通知Lua脚本有关被无效的对象。
如果要维护大量数据并且您希望能够尽快重用内存,那么Lua对象可能只是指向真实数据的指针的包装器。然后你可以独立删除数据,并将指针设置为NULL
,这将同时作为无效的标志。
请注意,仅通过指针值本身检查指针有效性可能会严重失败:
SomeStruct* ptr = malloc(sizeof(SomeStruct));
// don't forget if(!ptr) error handling
SomeStruct* copy = ptr; // here C only, but might be stored in Lua!
SomeStruct* newPtr = malloc(sizeof(SomeStruct));
// by accident same address re-used as ptr once had!!!
if(isValid(copy))
{
// but the struct originally referenced died long ago...
}