使用Lua+SWIG动态地将成员添加到类中。

这段Lua代码创建了一个table并动态地添加了一个新的成员。执行后,我们可以在屏幕上看到"hello":

foo = {}
foo.x = "hello"
print(foo.x)

我现在在使用SWIG将一些C++类绑定到Lua。为此,在test.i(SWIG模块文件)中创建了一个简单的类,如下所示:

%module test

%inline
%{

class Foo
{
public:
  Foo() { X = 0; }
  void SetX(int x) { X = x; }
  int GetX() { return X; }
private:
  int X;
};

%}

然后,我编写了一个测试Lua代码,如下所示:

obj = test.Foo()
obj:SetX(5)
print("Number: " .. obj:GetX())

执行后,我们如期获得“Number 5” 。问题出在当我想给绑定了SWIG的对象动态地添加新成员,并尝试访问它时,如下所示:

obj.Y = 7
print("Number: " .. obj.Y)

我会得到以下错误消息:

"attempt to concatenate field 'Y' (a nil value)"

是否有可能在使用SWIG绑定的对象上动态添加新成员?是否有其他选项,而不必转移到另一个Lua绑定库?

点赞
用户734069
用户734069

SWIG不使用表(table)来表示对象,而是使用_userdata_。毕竟,这些对象是C++对象,需要存储Lua代码不能触及的C++数据。

而且,我不会费心去寻找“另一个Lua绑定库”; 几乎所有库都使用_userdata ,这是Lua代码明确不能修改的(为了能够提供这种能力)。

然而,这并不意味着你不能“作弊”。

你总是可以将你从C++代码中获得的对象包装成自己的Lua表(table),该表具有一个metatable,以转发未知调用到C++对象。要执行此操作的代码如下所示:

local function WrapObject(cppObject)

    local proxy = {}

    local wrapper_metatable = {}

function wrapper_metatable.__index(self, key)
    local ret = rawget(self, key)
    if(not ret) then
        ret = cppObject[key]
        if(type(ret) == "function") then
            return function(self, ...)
                return ret(cppObject, ...)
            end
        else
            return ret
        end
    else
        return ret
    end

end

    setmetatable(proxy, wrapper_metatable)
    return proxy
end

返回的代理对象是一个Lua表(table),可以在其中设置键和值。当获取一个值,比如调用一个函数时,它会查看该值是否已在表中设置。如果没有,它将尝试从你封装的C++对象中获取它,这将经过_它_的metatable。

如果你的C++类使用其他元方法,比如__add、__sub、__tostring等,你需要扩展这个metatable。

2012-11-10 22:14:20