4

我有一个用 swig 包装的类,并用 lua 注册。我可以在 lua 脚本中创建此类的实例,并且一切正常。

但是假设我有一个在我的 C++ 代码中创建的类的实例,并调用了 new X,并且我有 la lua_state L,其中有一个我想调用的函数,它接受一个参数,一个 X 的实例......我如何调用该函数。这是(一些)有问题的代码(我省略了错误处理的东西):

主文件

class GuiInst;
extern "C"
{
    int luaopen_engine (lua_State *L);
}

int main()
{
    GuiInst gui=new GuiInst;
    lua_State *L=luaL_newstate();
    luaopen_engine(L); //this is swigs module
    int error=luaL_loadfile(L,"mainmenu.lua")||
    lua_pcall(L, 0, 0, 0);
    lua_getglobal(L,"Init");
    //Somehow push gui onto lua stack...
    lua_pcall(L, 1, 0, 0));
    lua_close(L);
}

主菜单.lua

function Init(gui)
    vregion=gui:CreateComponent("GuiRegionVertical");
end

目前,我发现可以工作的只是从 swig 生成的 cpp 文件中公开一些功能,然后调用它。由于几个原因,这很糟糕......如果我有多个模块并且我不得不更改 swig 文件中的默认链接规范(使用 -DSWIGRUNTIME=),它将无法工作。

我将以下内容添加到 main.cpp

extern "C"
{
    struct swig_module_info;
    struct swig_type_info;
    int luaopen_engine (lua_State *L);
    swig_module_info *SWIG_Lua_GetModule(lua_State* L);
    void SWIG_Lua_NewPointerObj(lua_State* L,void* ptr,swig_type_info *type, int own);
    swig_type_info *SWIG_TypeQueryModule(swig_module_info *start,swig_module_info *end,const char *name);
}
//and then to push the value...
SWIG_Lua_NewPointerObj(L,gui,SWIG_TypeQueryModule(SWIG_Lua_GetModule(L),SWIG_Lua_GetModule(L),"GuiInst *"),0);

它得到一个指向模块的指针,然后是指向类型的指针,然后调用 swigs 函数来注册它。不得不挖掘一个不应该是人类可读的文件(所以它在文件的顶部说)而且只是混乱,这是一件不合理的事情!(但它确实有效!)

当然,有更好的方法来完成我正在尝试做的事情。

PS 从高级 pov 我想要的是让 lua 不引用由 GuiInst 中的对象工厂创建的 Gui 组件,以防我遇到这个错误。这是我第一次向脚本语言公开功能,除了一些非常简单(和非 swig)的 python 模块,所以我准备接受建议。

感谢您的任何建议!


RBerteig 对评论的回应

当 swig 运行以防止 lua 构造它的实例时,GuiInst 的构造函数是 #defined 私有的,所以这对我不起作用。我试图阻止的是以下(在lua中):

r=engine.GuiRegionVertical()
r:Add(engine.GuiButton())

它将调用“g=new GuiButton”,然后将其注册到 GuiRegionVertical(由于各种原因需要存储一个指针),然后调用“delete g”,并且 GuiRegionVertical 留下了一个指向 g 的悬空指针。

我怀疑真正需要发生的是 GuiRegionVertical::Add(GuiButton*) 应该增加 GuiButton* 的引用计数,然后 GuiRegionVertical 的析构函数应该减少其所有内容的引用计数,尽管我不确定这应该如何痛饮就完事了。

这将消除对私有构造函数、Gui 对象工厂和讨厌的外部对象的需要。

我要解决这个错误吗?

谢谢。

4

2 回答 2

3

迟到总比不好,这个解决方案将帮助其他人。

void handle_web_request(WebRequest *request, WebResponse *response)
{
  lua_getfield(rackam->lua_state, LUA_GLOBALSINDEX, "handle_web_request");
  SWIG_Lua_NewPointerObj(rackam->lua_state, request, SWIGTYPE_p_WebRequest, 0);
  SWIG_Lua_NewPointerObj(rackam->lua_state, response, SWIGTYPE_p_WebResponse, 0);
  lua_call(rackam->lua_state, 2, 0);
}

此代码必须在 .i 文件的 %{}% 块内,因为 SWIGTYPE_p_WebRequest 是

#define SWIGTYPE_p_WebResponse swig_types[6]

和 swig_types[6] 是

static swig_type_info *swig_types[12];

这意味着 swig_types 只能从定义它的 C++ 文件中访问。

这个特定的片段发送了我的两个包装指针,因此从 C++ 端调用 handle_web_request(request, response) 将运行全局 lua 函数“handle_web_request”并将我的两个指针传递给它,应用 SWIG 魔法。

于 2012-03-02T00:50:05.627 回答
1

有一个简单直接的答案,这可能不是最有效的答案。SWIG 生成用于从脚本语言端操作对象的包装器。对于对象,它还合成一个包装的构造函数。因此,直接的解决方案是让 Lua 解释器调用 SWIG 的构造函数来创建新对象。

对于包装engine.GuiInst类,您几乎可以肯定可以执行以下操作:

int main()
{
    lua_State *L=lua_open();
    luaopen_engine(L); //this is swigs module
    int error=luaL_loadfile(L,"mainmenu.lua")||
    lua_pcall(L, 0, 0, 0);

    luaL_dostring(L, "Init(engine.new_GuiInst())");

    lua_close(L);
}

对于像脚本启动这样的一次性情况,通过 luaL_dostring() 运行字符串常量的惩罚一点也不差。但是,在事件回调或内部循环中,我会更加努力地避免它。

似乎应该有一种方法可以将指针直接转换为包装对象,我没有在我自己的少数 SWIG 生成的包装器中发现它。

编辑:当然,Lua 片段可以分解为 API 调用,这些调用在堆栈上获取引擎表 global,从中提取new_GuiInst成员,调用它,然后调用 global Init,但是一点点效率是以牺牲一些为代价的明晰。

至于处理不应在用户代码中意外构建的对象,正如已澄清的问题所表明的那样,我的第一个冲动是让 SWIG 生成构造函数,如果以后需要保留一个私有引用,并将其从表中删除. 即使是 C 模块(通常)也只是一个表,其成员包含函数值。除非付出额外的努力,否则在 C 中实现不会使它们成为只读的。

因此,您始终可以检索 的值engine.new_GuiInst并将其存放在注册表中(有关详细信息,请参阅伪索引的 Lua 参考手册的第 3.5 节中luaL_ref()的讨论)以供以后使用。然后,在让任何用户代码运行之前,只需执行. 我应该注意,对于我最近使用的 C 数据类型,SWIG 为每种类型创建了两个构造函数,分别命名为和. 两者都在模块的表中可见,您希望将两个名称都设置为. 如果对 SWIG 包装 C++ 类的经验要少得多,结果可能会有所不同......LUA_REGISTRYINDEXengine.new_GuiInst = nilnew_TYPETYPEnil

您可能想要检查和查看engineSWIG 返回的表的全部内容,并创建一个仅包含您希望用户可用的方法的代理对象。您还可以更改用户脚本看到的环境,以便它只有代理可用,并命名代理engine在Lua 列表lua-users wiki上已经有大量关于沙盒用户脚本的讨论。

于 2009-03-05T02:56:17.823 回答