Lua回调函数从C++中

我有一个 C++ 宿主程序,使用 tolua++ 将一些类暴露给 Lua 使用。其中一个类有一个函数应该从 Lua 中注册回调。这样当 C++ 代码需要调用一个已注册的 Lua 函数时,就可以通过这个回调函数实现。但是该函数是一个表/类函数。我以前使用字符串函数名(不是 Lua “类”的一部分)成功的注册过,但我想知道是否能以某种方式存储 Lua 函数,而不是函数名。

我定义我的 Lua 类如下:

MyClass = {}

function MyClass:Create()
    local obj = {}

    -- 复制 MyClass 表中的所有函数到此本地 obj 并返回它们
    local k, v
    for k, v in pairs(obj) do
        obj[k] = v
    end

    return obj
end

function MyClass:Enter()
    self.CPlusClass = CPlusClass:Create()  -- 这是 C++ 类创建,我定义了一个静态函数 Create()
    self.CPlusClass:RegisterCallback(15, self.Callback)  -- 这是我想要传递这个实例函数,以便从 C++ 类回调时调用
end

function MyClass:Callback(reader)
    -- 我想要能够在这里使用 self,因此需要是同一个实例
end

我想在 MyClass:Enter() 内注册 Lua“类”函数 MyClass::Callback,以便能够从 C++ 对象调用它。我该如何将此传递给 C++ 函数?类型将是什么,以便从 C++ 中调用 MyClass:Callback(),同时将“self”传递,以便它是同一个类的“实例”?

我知道“self”是指我创建的实际变量,我假设它会通过变量名在全局表中,但当你在 Lua “类”内部时,我该如何确定“self”指向的变量名是什么?如果我能够得到它,我就可以将其作为字符串传递给我的 C++ 函数,并将其存储为字符串,以便我可以调用它来获取该特定表。然后我也可以将表函数名作为字符串传递,并在 C++ 中获取它并调用它。但问题是如何将“self”转换为它指向的实际变量名,以便我可以在 C++ 中调用 getglobal 来获取该表?

点赞
用户258523
用户258523

如果您想保留RegisterCallback的签名和操作(而不是以某种方式增强其理解对象方法回调),则需要创建一个闭包并将其作为回调传递。像这样:

function create_closure(obj, fun)
    return function(...)
        return obj[fun](...)
    end
end

function MyClass:Enter()
    self.CPlusClass = CPlusClass:Create()   -- 这是 C++ 类的创建,我定义了一个静态函数 Create()
    self.CPlusClass:RegisterCallback(15, create_closure(self, "Callback"))
end
2013-11-20 17:39:08
用户869951
用户869951

你将 C++ 的 CPlusClass "class" 导出到了 Lua:这个 C++ 类一定有一个 RegisterCallback 方法,它接受一个函数指针或一个 YourCallbackClass 实例的指针,该实例使用虚函数来分派到正确的对象。然后你只需要将 YourCallbackClass 导出到 Lua:在幕后,Lua 版本将创建一个实例并将其传递给你的 C++ RegisterCallback。

例如:

class CPlusClass {
public:
    RegisterCallback(int val, CPlusCallback* cb) { ... store cb for later... }
};

class SomeObj; // type of Objects that you want to call back into

class CPlusCallback {
public:
    typedef void (SomeObj::*Method)();
    CPlusCallback(SomeObj* obj, Method method) { callee=obj; method=method; }
    call() { callee->*Method(); }
private:
    SomeObj* callee;
    Method method;
};

你的 CPlusCallback 可能不同,这只是一个例子。例如,如果你只想要函数而不是方法,则不需要存储被调用方。如果你两者都需要,则 CPlusCallback 必须是一个基类,而 call() 必须是虚的,派生类根据需要实现细节。根据你的情况进行调整。

然后将 CPlusCallback 和 SomeObj 导出到 Lua,然后你可以在 Lua 中执行以下操作:

somObj = SomeObj:Create()

function MyClass:Enter()
    -- 在创建 Lua 的 CPlusClass 实例时,使用一个 C++ 的用户数据作为成员
    self.CPlusClass = CPlusClass:Create()
    self.CPlusCallback = CPlusCallback:Create(someObj, someObj.method)
    self.CPlusClass:RegisterCallback(15, self.CPlusCallback)
end

上述示例假定 someObj.method 是你从 C++ 类 SomeObj 导出的一个方法,其具有适当的签名 void (SomeObj::*Method)()。

2013-11-20 22:56:34