我一直在使用LuaPlus来使用脚本语言公开模块的功能。为此,LuaPlus真的很棒但是我坚持清理暴露的对象,因为我不知道如何处理删除代表我的c ++对象的lua对象,所以我可以正确地释放c ++资源。
我正在使用lua表和metatable来表示cpp对象,将指针传递给cpp对象作为表的lightuserdata参数“__object”,所以我可以做类似的事情
function foo()
local p = MyCppObject:new() //Create a cpp object and bind it to a table+metatable
p:do_something() //Correctly calls the cpp member function do_something
....
(exits scope) //No more references to p, so p is deleted.
end
退出函数后(或稍后一段时间),我希望得到metatable method "__gc"的调用,我调用内部cpp对象的删除,但我没有看到我的cpp回调被召唤。我尝试使用lua的函数collectgarbage强制使用垃圾收集,称为我的函数很多次以强制lua收集我的对象,我看不到我的回调执行。最重要的是,我看到调用collectgarbage(“count”)的结果有时会减少,所以某些东西会被删除,但我不知道是什么。我检查了lua文档,但我没看到我做错了什么:(
任何评论表示赞赏!谢谢!
更新:添加了c ++代码端+添加了本地,因为Mud指出+我的测试样本
我创建了我的程序的这个小样本。 LuaShell对象只是状态的包装器+读取命令行并执行从std :: cin读取的字符串的循环
#include <iostream>
#include "LuaPlus.h"
class Point
{
private:
int x_,y_;
public:
Point(): x_(0), y_(0){}
Point(int a, int b): x_(a), y_(b){}
~Point() {std::cout << "Point "<< x_ << ","
<< y_ << "being deleted" << std::endl;}
int x() const { return x_;}
int y() const { return y_;}
};
LuaPlus::LuaObject metatable;
int new_point( LuaPlus::LuaState* state)
{
LuaPlus::LuaStack args(state);
//std::cout << "Creating point!!" << std::endl;
float x = 0, y = 0;
if ( args.Count() == 3)
{
if (args[2].IsNumber() && args[3].IsNumber())
{
x = args[2].GetFloat();
y = args[3].GetFloat();
}
}
Point* p = new Point(x,y);
LuaPlus::LuaObject lua_obj = state->CreateTable();
lua_obj.SetLightUserData("__object", p);
lua_obj.SetMetaTable( metatable );
return 1;
}
int my_gc_event( LuaPlus::LuaState* state)
{
std::cout << "Calling gc_event from lua" << std::endl;
return 0;
}
int main()
{
/* Creating the object that holds the Lua interpreter as well as
* the command line
*/
LuaShell::Shell shell(true);
LuaPlus::LuaObject globals = shell.get_state()->GetGlobals();
metatable = globals.CreateTable("PointMetaTable");
metatable.SetObject("__index", metatable);
metatable.Register("new", new_point);
metatable.Register("__gc",my_gc_event);
metatable.RegisterObjectDirect("x", (Point*)0 ,&Point::x);
metatable.RegisterObjectDirect("y", (Point*)0 ,&Point::y);
globals.SetObject("Point", metatable);
//Get into the read-command-line-until-quit loop.
shell.run();
return 0;
}
在lua方面,我运行此测试。
? k,b = collectgarbage("count") print (k*1024)
> 33761
? for it=1,1000000 do foo() end
? k,b = collectgarbage("count") print (k*1024)
> 75315
? collectgarbage()
? k,b = collectgarbage("count") print (k*1024)
> 32363
如你所见,根据lua运行时有“一些”垃圾收集,但当我看到我的进程的最高报告时,内存只会上升,而且永远不会下降。此外,我从来没有看到来自点析构函数的消息(预期因为我没有真正调用它)或来自“my_gc_event”内部的消息(意外,因为我认为它在collectgarbage工作期间的某个时刻被调用)。 / p>
再次感谢!
答案 0 :(得分:1)
function foo()
p = MyCppObject:new() //Create a cpp object and bind it to a table+metatable
p:do_something() //Correctly calls the cpp member function do_something
....
(exits scope) //No more references to p, so p is deleted.
end
p
是全局的,因此当foo
返回时 不会保留范围。如果您希望它以词法作用于该函数,则需要使用关键字local
。
lightuserdata参数[...]我希望能够调用metatable方法“__gc”
Lightuserdata are not garbage collected.这可能与你的问题无关(我不知道LuaBind是如何工作的),但我认为值得一提。
对评论的回应:
我编写了以下测试,它创建了一个单字节,无操作的userdata类型,其中只有__gc
的metatable:
#include "lauxlib.h"
static int foo_gc (lua_State* L) {
puts("__gc called");
return 0;
}
static int foo_new (lua_State* L) {
lua_newuserdata(L, 1);
luaL_getmetatable(L, "foo");
lua_setmetatable(L, -2);
return 1;
}
int __declspec(dllexport) __cdecl luaopen_luagc (lua_State* L) {
// create foo type
static const struct luaL_reg foo[] = {
{ "__gc", foo_gc },
NULL, NULL
};
luaL_newmetatable(L, "foo");
luaL_openlib(L, NULL, foo, 0);
// create constructor
lua_register(L, "foo", foo_new);
return 0;
}
如果我然后运行以下测试:
require 'luagc'
for i=1,5 do
foo()
end
collectgarbage("collect")
输出结果为:
__gc called
__gc called
__gc called
__gc called
__gc called
答案 1 :(得分:1)
考虑到正确的答案(对Mud的道具)被埋没在评论中,我认为最好在这里添加最终代码并回答。
正如Mud所说,我的问题是尝试使用表来暴露对象。虽然这有效,但lua 5.1运行时(由luaplus使用)不会为表调用垃圾收集器事件(__gc),所以我不得不使用luaplus的BoxPointer辅助函数。还添加了一个映射来跟踪暴露对象的类型,因为它们作为void *传递,之后您需要正确地转换它们以进行适当的清理。使用一些模板技巧,我结束了一个非常简单的系统来清理。这是代码(主函数保持不变,以及Point类定义)。
#include <iostream>
#include <map>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include "LuaPlus.h"
#include "Shell.h"
//Function used to delete an object created and exposes to lua
template<typename type_to_delete>
void delete_wrapper(void* ptr)
{
type_to_delete* typed_ptr = static_cast<type_to_delete*>(ptr);
delete typed_ptr;
}
//My object metatable
LuaPlus::LuaObject metatable;
//Table to save function pointers for the correct deletion of exposed objects
std::map<void*,boost::function<void(void*)> > g_object_type_deleter;
int new_point( LuaPlus::LuaState* state)
{
LuaPlus::LuaStack args(state);
float x = 0, y = 0;
if ( args.Count() == 3)
{
if (args[2].IsNumber() && args[3].IsNumber())
{
x = args[2].GetFloat();
y = args[3].GetFloat();
}
}
Point* p = new Point(x,y);
//This used to be a table, but given the shortcoming with the gc, now using this version
LuaPlus::LuaObject lua_obj = state->BoxPointer(p);
lua_obj.SetMetaTable( metatable );
//Save the callback to delete the object we just created.
g_object_type_deleter[static_cast<void*> (p)] = boost::bind( delete_wrapper<Point> , _1);
return 1;
}
int my_gc_event( LuaPlus::LuaState* state)
{
//get the pointer lua is trying to delete.
void *p = state->UnBoxPointer(1);
//look for the delete callback with the type information
std::map<void*,boost::function<void(void*)> >::iterator it = g_object_type_deleter.find(p);
if (it == g_object_type_deleter.end() )
{
std::cout << "receiving callback to destroy object that was not indexed !" << std::endl;
return 0;
}
//Actually call the delete function passing the pointer as parameter
it->second(it->first);
return 0;
}
谢谢你们!