Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding C++ functions & calling Lua functions with class as parameter

Tags:

c++

lua

I've been reading about Lua/C++ and I had a question about design I was hoping I could get some help on.

What I want:

I want so my class in C++ (Created and Destroyed by C++) to be able to call a Lua function using itself as a parameter.

Eg.

object.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;
};

object.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;
}

script.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

test.cpp

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

pTest->OnLoad();

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

delete pTest;

After Some reading:

From what I've read this is no direct way to assign C++ class instance functions. Non-member functions are needed. Tables are used to track functions.

My Questions:

  • What do I push as an argument when calling the Lua functions (Object_OnEvent(object, eventID) etc...) Is it a pointer to the object
  • How does Lua know the object design
  • Do I need a table per object or instance
  • Do I need to duplicate all the functions I intend to use in Lua again as normal functions grabbing a the ptr to call it from

As a final and possible single question:

  • Is there any place I could get more information on what I'm trying to achieve described above.

I'm probably just going to go back to step one and try and absorb this information again. I still wan't to make my post tho. I'll post back myself if I set it up.

like image 822
Azelekia Avatar asked Dec 02 '25 17:12

Azelekia


1 Answers

There are many questions, but in principle, if I understand you correctly, you want to bind your C++ classes to Lua, have a shared object lifetime and automatic garbage collection, and be able to call Lua functions on objects created on the C++ side.

This is all possible with either low-level glue code, or dedicated binding libraries, such as LuaBridge and LuaState. LuaState is used in my answer for convenience and fast prototyping.

What's not yet clear is why you want to define a trivial function in Lua, such as Object_OnLoad to call it from C++, which would call a method of an object that you have created in the same scope on the C++ side. I'd guess, you have a more complicated picture in your code, so that such Lua usage pattern would be justified. In that case, one by one:

The ingredients

Binding a class to Lua

Here's a declarative binding that you can call once before calling any other Lua functions

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

To perform the binding:

 lua::State state;
 luabridge_bind(state.getState());

Calling a lua function on a C++ side object

LuaState unfortunately cannot use objects in call parameters at the moment, while primitives work, i.e. from the readme:

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

But what one could do is to temporary create a global variable instance (watch out for name collisions) and call the function on it.

Preparing the script:

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

Creating an automatically lifetime-managed object:

auto my_obj = RefCountedPtr<MyObject>(new MyObject);

Calling the lua function on the object:

SetGlobal(state.getState(), "my_obj", my_obj);
state.doString("Object_OnLoad(my_obj); my_obj = nil");

Where SetGlobal can look like that:

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

A complete example and comments

You can find the whole example code at Github: try_luabridge.cpp

which has been compiled and run at Travis CI.

The possibilities are limitless. It's up to you how you structure your code, so, naturally, this answer won't provide code that would immediately fit your needs. However, I'd encourage you to read Programming in Lua, and LuaBridge and LuaState manuals to get a better overview of the possiblities that are at your hand.

like image 136
Dmitry Ledentsov Avatar answered Dec 05 '25 06:12

Dmitry Ledentsov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!