3

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.

3
  • It's better to ask one question at a time. Commented Aug 26, 2014 at 5:09
  • and there's plenty of information on stackoverflow on "binding lua to c++", and google has plenty of answers too. It's important to know what you're trying to achieve. You can combine LuaState with LuaBridge to achieve quite a lot in binding to C++. I've made a project for trying that out here: lua_cpp_tryout. Once there's a clear Commented Aug 26, 2014 at 6:50
  • I'll try to keep it to one question next time :) Commented Aug 26, 2014 at 23:01

1 Answer 1

5

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.

Sign up to request clarification or add additional context in comments.

2 Comments

It wasn't exactly what I wanted, but have learned enough from your post. I feel I have an idea to solve it. Thanks for the help. Will mark as answer since I probably specified what I wanted a bit wrong. But sure someone will find use from it. TY :)
glad to hear. Feel free to add to your question or open a new one and I'll try to help clarify

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.