如何在 Lua 堆栈中推送由 Swig 封装的 C++ 类的实例?

我有一个被 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);
}

在此刻,我发现可以工作的唯一方法是从 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);

这得到了指向模块的指针,然后是类型的指针,然后调用 swig 的函数进行注册。这是在一个不该人类阅读的文件中挖掘的不合理事情(所以文件的顶部说),而且非常混乱!(但是它确实可以工作!)

肯定有更好的方法来实现我想做的事情。

PS:我想要的高级使用方法是让 Lua 不引用 Gui 组件,这些组件是在 GuiInst 的 Object Factory 中创建的,以防我错了。这是我第一次针对脚本语言公开功能,除了一些非 SWIG Python 模块之外,都非常简单,所以我准备接受建议。

感谢任何建议!

原文链接 https://stackoverflow.com/questions/613282

点赞
stackoverflow用户68204
stackoverflow用户68204

有一个简单直接的答案,它可能不是最有效率的答案。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_TYPETYPE。两者都可以在模块的表中看到,您需要将这两个名称都设置为 nil。我对 SWIG 封装 C++ 类的经验少得多,结果可能会有所不同...

您可能希望检查和审查 SWIG 返回的整个 engine 表的内容,并创建一个代理对象,其中只包含您希望用户可用的方法。您还可以更改用户脚本看到的环境,以便仅具有代理,并将代理命名为 engine。已经对 Lua 列表lua-users wiki 上的用户脚本实现沙盒环境进行了相当多的讨论。

2009-03-05 02:56:17
stackoverflow用户634979
stackoverflow用户634979
迟做总比不做好,这个解决方案将会帮助其他人。

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-02 00:50:05