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

但是我说我的c ++代码中有一个类的实例,调用了新的X,并且我有la lua_state L,其中包含一个我要调用的函数,它接受一个参数,一个X的实例...我该如何调用该功能。这是(一些)有问题的代码(我省略了错误处理的东西):

的main.cpp

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);
}

mainmenu.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函数来注册它。挖掘一个不应该是人类可读的文件是不合理的(所以它在文件的顶部说明)并且只是MESSY! (但确实有效!)

当然,这是一种更好的方式来完成我想要做的事情。

PS从高级pov我想要的是让lua不重新计算由GuiInst中的Object Factory创建的Gui组件,以防我出错了。这是我第一次将功能暴露给脚本语言,除了一些非常简单(和非swig)的python模块,所以我准备接受建议。

感谢您的任何建议!


回应RBerteig的评论

当swig运行以防止lua构造它的实例时,GuiInst的构造函数#defined为private,因此对我来说不起作用。我试图阻止的是以下(在lua中):

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

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

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

这将消除对私人构造函数,Gui Object Factory和令人讨厌的外部的需求。

我是否会这样做错?

感谢。

有帮助吗?

解决方案

有一个简单直接的答案,可能不是最有效的答案。 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调用,使引擎表在堆栈上全局化,从中提取 new_GuiInst 成员,调用它,然后调用全局 Init ,但稍微提高效率是以一定的清晰度为代价的。

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

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

您可能希望检查并查看SWIG返回的 engine 表的全部内容,并创建一个仅包含您希望用户使用的方法的代理对象。您还可以更改用户脚本看到的环境,以便它只有代理可用,并命名代理 engine 。在沙盒用户脚本进行了大量讨论://www.lua.org/lua-l.html"rel =“nofollow noreferrer”> Lua列表和 lua-users wiki

其他提示

迟到,从来没有,这个解决方案将帮助其他人。

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魔法。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top