如何在 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
迟做总比不做好,这个解决方案将会帮助其他人。
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的魔力。
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
有一个简单直接的答案,它可能不是最有效率的答案。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
。已经对 Lua 列表 和 lua-users wiki 上的用户脚本实现沙盒环境进行了相当多的讨论。