如何在Lua代码中扩展SWIG的userdata?
我正在使用 SWIG 将 C++ 代码绑定到 Lua。到目前为止,效果很好,但现在我需要“欺骗”,在 Lua 中扩展单个 userdata,添加自定义字段和方法等。
我找不到一种方法来在 SWIG 的指令中完成它。我知道包装器代码中的魔法发生在哪里,但我不完全理解 __index 和 __newindex 如何工作。此外,SWIG 使用 __setitem 和 __getitem,该行被注释为“/* NEW:查找 __setitem() 函数,这是用户提供的设置 fn */” - 我也不知道这是什么意思。最后,我的环境在每次构建之前自动调用脚本将 SWIG 指令绑定到 C++ 包装器,因此如果我选择重新编译或添加更多 Lua 绑定,则修改源代码后续非常繁琐。
到目前为止,我的唯一结论是使用 toLua++,它文档中有这个功能。请帮我避免很多转换工作!
编辑:我还发现了 5.2 中的“ lua_setuservalue” API 调用-它看起来很有用,但我不知道在所有 SWIG 绑定代码中哪里调用它。
编辑:为了澄清:
我通过具有 C 函数创建对象
SoundObj *loadSound(const char *name)
{
return g_audio->loadSound(name); // 返回 SoundObj 指针
}
SWIG 通过在 *.i swig 定义中包括其原型来绑定此函数。
在 Lua 中,我编写如下代码:
sound = audio.loadSound("beep.mp3")
sound.scale = 0.2
sound:play()
编辑:进步!我遵循了 Schollii 的指示,并编写了以下 Lua 代码。这里,“ground”是之前使用绑定的 C++ 代码获取的 userdata。该代码存储了 swig 版本的 __index 和 __newindex,然后我重新创建这些功能,首先查询另一个("_other")表。
我目前的问题在于,存储在“_other”表中的新值在所有该类型的 userdata 对象之间共享。换句话说,如果 ground.nameFoo =“ha!”,所有其他对象都有一个名为 nameFoo 的字段,其中存储“ha!”。我该如何解决这个问题?
mt = getmetatable(ground)
mt._other = {}
mt.__oldindex = mt.__index
mt.__oldnewindex = mt.__newindex
mt.__index = function(tbl, key)
if mt.__oldindex(tbl, key) == nil then
if mt._other[key] ~= nil then
return mt._other[key]
end
else
return mt.__oldindex(tbl, key)
end
end
mt.__newindex = function(tbl, key, val)
if mt.__oldnewindex(tbl, key, val) == nil then
mt._other[key] = val
end
end
编辑:我实现了此答案中的解决方案:在 Lua + SWIG 中动态将成员添加到类
问题在于现在我的对象类型不再是 userdata,而是表。这意味着我不能将其作为参数自然地传递给其他需要 userdata 作为参数的绑定 C++ 函数。有解决方法吗?而且,我必须对返回 userdata 对象的每个函数进行此操作。
我已经根据 Schollii 的例子制作了一个解决方案......但做出了更少的牺牲。 这并不会将您的用户数据转换为表,因此您仍然拥有用户数据类型 (我可以将其传递给其他需要用户数据的C++函数)。 Schollii 的解决方案实际上将用户数据转换为表包装器。
因此,第一行使用变量 "ground" - 这是由 SWIG 包装的一个用户数据实例。 在执行开始时这样做,会为 "ground" 用户数据类型的所有实例更改元表,但每个实例保留了一个由用户数据的内存位置索引的个人表。该表位于_G._udTableReg表中。
-- Store the metatable and move the index and newindex elsewhere
mt = getmetatable(ground)
mt.__oldindex = mt.__index
mt.__oldnewindex = mt.__newindex
-- Store the global registry of tables associated with userdata, make a pointer to it in the metatable
_G._udTableReg = {}
mt._udTableReg = _G._udTableReg
-- Rewrite the new index function that looks in the udTableReg using 'self' as index before proceeding to use the old index as backup
mt.__index = function(self, key)
local ret;
local privateTable = mt._udTableReg[self]
-- If the private table exists and has key, return key
if privateTable ~= nil and privateTable[key] ~= nil then
ret = privateTable[key]
-- Use the old index to retrieve the original metatable value
else ret = mt.__oldindex(self, key) end
if ret == nil then return 0
else return ret end
end
-- Try to assign value using the original newindex, and if that fails - store the value in
mt.__newindex = function(self, key, val)
-- If old newindex assignment didn't work
if mt.__oldnewindex(self, key, val) == nil then
-- Check to see if the custom table for this userdata exists, and if not - create it
if mt._udTableReg[self] == nil then
mt._udTableReg[self] = {}
end
-- Perform the assignment
mt._udTableReg[self][key] = val
end
end
我仍然不知道最佳的放置此 Lua 代码的位置,也不知道如何更优雅地获取用户数据的元表,而不是实际使用现有变量。
所以你有一个C++中的SoundObject类,你将其导出到Lua(无论是通过SWIG还是tolua++或手动方式)。你的应用程序运行一个Lua脚本,该脚本创建了一个SoundObject实例,并添加了一个属性(在Lua中)和一个方法(同样在Lua中),然后你希望能够从C++中使用该属性并调用该方法。类似于:
-- Lua脚本:
sound = audio.loadSound("beep.mp3")
sound.scale = 0.2 -- 这个属性由SWIG导出
sound.loop = true -- 这个是仅在Lua中使用的
sound.effect = function (self, a) print(a) end -- 这个是仅在Lua中使用的
sound:play() -- 这个方法由SWIG导出
// C++伪代码:
将SoundObject* load_sound(string)与“audio.loadSound”绑定
运行Lua脚本
而且你希望play()方法调用其他属性和方法(如loop和effect)并执行某些操作。我无法想象是什么操作,但希望这能为你更清楚地表达问题提供帮助。
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在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 中,一个表格通过使用另一个表格作为元表来“继承”另一个表格。因此,如果你将 C++ Foo 导出到 Lua 并想要一个从 Foo 派生的 Lua “class”,你可以创建一个表格并将其元表设置为 Foo。然后,当你使用该 new table 访问一个不存在于表格中的字段时,它将在元表中查找是否存在该字段。以下是伪代码示例:
baseTable = {a=123} assert( getmetatable(baseTable) == nil ) derived={b=456} assert(derived.a == nil) setmetatable(derived, baseTable ) assert(derived.a == 123)baseTable 是通过 SWIG 导出到 Lua 的 C++ 类,你无法更改它的元表,但你可以更改你在 Lua 中创建的表格的元表。因此,你不需要对 SWIG 代码进行任何修改或使用 SWIG 指令,只需在 Lua 中操作即可。例如:
-- define Foo class: Foo = {} Foo.__index = Foo -- make Foo a Lua "class" setmettable(Foo, YourCppBaseClass) -- derived from your C++ class -- instantiate it: foo = {} setmetatable(foo, Foo) -- foo is of "class" Foo