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

2021-12-30 00:00:00 swig lua scripting c++

这段 Lua 代码,创建了一个表并动态添加了一个新成员.运行这个我可以按预期在屏幕上看到 "hello" :

This Lua code, creates a table and dynamically adds a new member. Running this I can get "hello" on the screen as expected:

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

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

But now I'm using SWIG to bind some C++ classes to Lua. For that purpose, in a test.i (SWIG module file) I created a simple class like this:

%module test

%inline
%{

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

%}

然后我写了一个这样的测试Lua代码:

Then I wrote a test Lua code like that:

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

按预期运行并获取 "Number 5".问题是,当我动态地向我的 SWIG 绑定对象添加一个新成员时,我尝试访问它,如下所示:

Running and getting "Number 5" as expected. The problem is that when I dynamically add a new member to my SWIG-binded object, and I try to access it, like so:

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

我收到此错误消息:

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

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

Is it possible to dynamically add new members on objects binded using SWIG? Is there some option without having to move to another Lua binding library?

推荐答案

SWIG 的对象不使用表;它使用用户数据.毕竟那些对象都是C++对象,需要存放Lua代码不能接触到的C++数据.

SWIG doesn't use tables for its objects; it uses userdata. After all, those objects are C++ objects, and need to store C++ data that Lua code shouldn't be able to touch.

而且我不会费心寻找另一个 Lua 绑定库";几乎所有都使用用户数据,Lua 代码明确无法修改这些数据(为了提供完全执行此操作的能力).

And I wouldn't bother looking for "another Lua binding library"; pretty much all of them use userdata, which Lua code explicitly cannot modify (in order to provide the ability to do exactly this).

然而,这并不意味着你不能作弊.

However, that doesn't mean you can't cheat.

你总是可以把你从 C++ 代码中得到的对象包装到你自己的 Lua 表中,它会有一个元表,将未知的调用转发到 C++ 对象.执行此操作的代码如下所示:

You can always wrap the object you get from C++ code into your own Lua table, which would have a metatable that forwards unknown calls to the C++ object. The code to do so would look something like this:

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 表,可以在其上设置键和值.当您获得一个值时,例如调用一个函数,它会查看该值是否已在表中设置.如果没有,它会尝试从您包装的 C++ 对象中获取它,该对象将通过它的元表.

The returned proxy object is a Lua table that can have keys and values set on it. When you get a value, such as to call a function, it will see if that value was set in the table. If not, it attempts to fetch it from the C++ object that you wrapped, which will go through its metatable.

如果您的 C++ 类使用其他元函数,如 __add__sub__tostring 等,则您需要扩展此元表.

You'll need to expand this metatable if your C++ class uses other metafunctions like __add, __sub, __tostring and so forth.

相关文章