绑定C++函数并以类作为参数调用Lua函数

Aze*_*kia 3 c++ lua

我一直在阅读有关 Lua/C++ 的内容,并且有一个关于设计的问题,希望能得到一些帮助。

我想要的是:

我希望我的 C++ 类(由 C++ 创建和销毁)能够使用自身作为参数来调用 Lua 函数。

例如。

对象.h

class Object
{
public:
    Object(const std::string & onLoad, const std::string & onEvent);
    ~Object();

    void OnLoad();

    void RegisterEvent(const std::string & eventID);
    void OnEvent(const std::string & eventID);

    void SetValue(int value);

private:
    int m_value;
    std::string m_onLoad;
    std::string m_onEvent;
};
Run Code Online (Sandbox Code Playgroud)

对象.cpp

Object::Object(const std::string & onLoad, const std::string & onEvent)
    : m_value(0)
    , m_onLoad(onLoad)
    , m_onEvent(onEvent)
{
}

Object::~Object()
{
    GAME->GetEventManager()->UnregisterListener(this);
}

void Object::OnLoad()
{
    //
    // call lua function [name store in: m_onLoad]
    // eg. m_onLoad = Object_OnLoad
    // in lua ->
    // function Object_OnLoad(object)
    //
}

void Object::RegisterEvent(const std::string & eventID)
{
    GAME->GetEventManager()->RegisterEvent(this, eventID);
}

void Object::OnEvent()
{
    //
    // call lua function [name store in: m_onEvent]
    // eg. m_onEvent = Object_OnEvent
    // in lua ->
    // function Object_OnEvent(object, eventID)
    //
}

void Object::SetValue(int value)
{
    m_value = value;
}
Run Code Online (Sandbox Code Playgroud)

脚本.lua

function Object_OnLoad(object)
    object:RegisterEvent("EVENT_CURRENT_HEALTH_CHANGED")
end

function Object_OnEvent(object, eventID)
    if (eventID == "EVENT_CURRENT_HEALTH_CHANGED")
        object:SetValue(GetCurrentHealth());

end
Run Code Online (Sandbox Code Playgroud)

测试.cpp

Object *pTest = new Object("Object_OnLoad", "Object_OnEvent");

pTest->OnLoad();

GAME->GetEventManager()->TriggerEvent(CEvent("EVENT_CURRENT_HEALTH_CHANGED"));

delete pTest;
Run Code Online (Sandbox Code Playgroud)

经过一番阅读后:

据我所知,这不是分配 C++ 类实例函数的直接方法。需要非成员函数。表用于跟踪函数。

我的问题:

  • 调用 Lua 函数时我应该将什么作为参数(Object_OnEvent(object, eventID) 等...)它是指向对象的指针
  • Lua如何知道对象设计
  • 我需要每个对象或实例一个表吗
  • 我是否需要再次复制我打算在 Lua 中使用的所有函数,就像普通函数抓取 ptr 来调用它一样

作为最后一个可能的单一问题:

  • 有没有什么地方可以让我获得有关我想要实现上述目标的更多信息。

我可能会回到第一步并尝试再次吸收这些信息。我仍然不想发表我的帖子。如果我设置了,我会亲自回复。

Dmi*_*sov 5

有很多问题,但原则上,如果我理解正确的话,您希望将C++ 类绑定到 Lua,具有共享对象生存期和自动垃圾回收,并且能够在 C++ 端创建的对象上调用 Lua 函数。

这一切都可以通过低级粘合代码或专用绑定库(例如LuaBridgeLuaState)来实现。我的答案中使用 LuaState 是为了方便和快速原型设计。

目前尚不清楚的是为什么要在 Lua 中定义一个简单的函数,例如Object_OnLoad从 C++ 中调用它,这将调用您在 C++ 端相同作用域中创建的对象的方法。我猜想,您的代码中有更复杂的图片,因此这种 Lua 使用模式是合理的。在这种情况下,一一:

这些成分

将类绑定到 Lua

这是一个声明性绑定,您可以在调用任何其他 Lua 函数之前调用一次

void luabridge_bind(lua_State *L) {
 luabridge::getGlobalNamespace(L)
  .beginClass<MyObject>("MyObject")
  .addConstructor<void(*)(), RefCountedPtr<MyObject> /* creation policy */ >()
  .addFunction("RegisterEvent", &MyObject::RegisterEvent)
 .endClass()
 ;
}
Run Code Online (Sandbox Code Playgroud)

要执行绑定:

 lua::State state;
 luabridge_bind(state.getState());
Run Code Online (Sandbox Code Playgroud)

在 C++ 端对象上调用 lua 函数

不幸的是,LuaState 目前无法在调用参数中使用对象,而原语可以工作,即来自自述文件:

state.doString("function add(x, y) return x + y end");
int result = state["add"](1,2);
Run Code Online (Sandbox Code Playgroud)

但我们可以做的是临时创建一个全局变量实例(注意名称冲突)并在其上调用函数。

准备脚本:

static const char *script =
 "function Object_OnLoad(object)\n"
 " object:RegisterEvent('EVENT_CURRENT_HEALTH_CHANGED')\n"
 "end"
;
state.doString(script);
Run Code Online (Sandbox Code Playgroud)

创建一个自动生命周期管理的对象:

auto my_obj = RefCountedPtr<MyObject>(new MyObject);
Run Code Online (Sandbox Code Playgroud)

在对象上调用lua函数:

SetGlobal(state.getState(), "my_obj", my_obj);
state.doString("Object_OnLoad(my_obj); my_obj = nil");
Run Code Online (Sandbox Code Playgroud)

哪里SetGlobal可以看起来像这样:

template <typename T>
void SetGlobal(lua_State* L, const char *name, T value) {
 luabridge::push(L, value);
 lua_setglobal(L, name);
}
Run Code Online (Sandbox Code Playgroud)

完整的例子和注释

您可以在 Github 上找到完整的示例代码:try_luabridge.cpp

它已在Travis CI编译并运行。

可能性是无限的。这取决于您如何构建代码,因此,自然地,这个答案不会提供立即满足您需求的代码。不过,我鼓励您阅读《Lua 编程》、《LuaBridge》和《LuaState》手册,以便更好地了解您手头上的可能性。