繁体   English   中英

如何在Lua代码中扩展SWIG的用户数据?

[英]How to extend SWIG's userdata in Lua code?

我正在使用SWIG将C ++代码绑定到Lua。 到目前为止,它看起来不错,但现在我需要“欺骗”并从Lua中扩展单个用户数据,添加自定义字段和方法等。

在SWIG的指令中工作时,我无法找到实现它的方法。 我知道魔术发生在包装器代码中的哪个位置,但我不完全理解__index和__newindex是如何工作的。 此外,SWIG使用__setitem和__getitem,其注释为“/ * NEW:查找__setitem()fn这是用户提供的set fn * /” - 不知道这意味着什么。 最后,我的环境会在每次构建之前自动调用脚本将SWIG指令绑定到C ++包装器,因此如果我选择重新编译或添加更多Lua绑定,那么之后修改源代码非常繁琐。

到目前为止,我唯一的结论是与Lua ++一起使用,其中记录了此功能。 请帮我避免很多过渡工作!

编辑:我还发现5.2中的“lua_setuservalue”API调用 - 它似乎很有用,但我不明白在所有SWIG绑定代码中我会在哪里调用它。

编辑:澄清事情:

我通过C函数创建对象

SoundObj *loadSound(const char *name)
{
    return g_audio->loadSound(name); // Returns SoundObj pointer
}

SWIG通过在* .i swig定义中包含它的原型来约束此函数。

在Lua我写这样的代码:

sound = audio.loadSound("beep.mp3")
sound.scale = 0.2
sound:play()

编辑:进步! 我按照Schollii的指示编写了以下Lua代码。 这里,“ground”是之前使用绑定C ++代码获取的用户数据。 代码存储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对象的每个函数执行此操作。

我将在这里马虎:在Lua中,一个表“继承”另一个表,将其用作metatable。 因此,如果您将C ++ Foo导出到Lua并想要从Foo派生的Lua“类”,您可以创建一个表并将其metatable设置为Foo。 然后,当您使用表中不存在的字段访问新表时,它将查找metatable以查看它是否存在。 示例伪代码:

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 ++类,你不能改变它的metatable,但你可以改变你在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

所以你有一个C ++的SoundObject类,你将它导出到Lua(无论是通过SWIG还是tolua ++或手动)。 您的应用程序运行一个lua脚本,它创建一个SoundObject实例并添加一个属性(在Lua中)和一个方法(再次在Lua中),然后您希望能够使用该属性并从C ++调用该方法。 就像是:

-- Lua script: 
sound = audio.loadSound("beep.mp3")
sound.scale = 0.2   -- this property exported by SWIG
sound.loop = true   -- this is Lua only
sound.effect = function (self, a) print(a) end  -- this is Lua only
sound:play()        -- this method exported by SWIG

// C++ pseudocode:
bind SoundObject* load_sound(string) to "audio.loadSound"
run Lua script 

并且您希望play()方法调查其他属性和方法(如循环和效果)并执行某些操作。 什么,我无法想象,希望这会让你知道如何更清楚地表达你的问题。

我按照Schollii的例子制定了一个解决方案......但牺牲更少。 这不会将您的userdata转换为表,因此您仍然具有userdata类型(我可以将其传递给其他带有userdata的C ++函数)。 Schollii的解决方案有效地将userdata变成了表格包装器。

所以第一行使用变量“ground” - 这是由SWIG包装的userdata实例。 在执行开始时执行此操作会改变该类型的所有实例的“地面”用户数据类型的元表,但每个实例保留由userdata的内存位置索引的个人表。 该表位于_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代码的最佳位置,或者如何获得更优雅的方法来获取userdata的metatable,而不实际使用现有变量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM