user441521
user441521

Reputation: 6998

Lua callback from C++

I have a c++ host in which I use tolua++ to expose some classes to Lua. One of the classes has a function that should register a callback from lua. So when the C++ code needs to call a Lua registered function it can. The function is however a table/class function. I've done this before with the string function name (not part of a lua "class") with success but I'm curious if I'm able to somehow store the Lua function and not function name.

I define my lua classes like:

MyClass = {}

function MyClass:Create()
    local obj = {}

    -- copy all functions from MyClass table to this local obj and return it
    local k,v
    for k,v in pairs(obj) do
        obj[k] = v
    end

    return obj
end

function MyClass:Enter()
    self.CPlusClass = CPlusClass:Create()   -- this is the C++ class creation, I defined a static function Create()
    self.CPlusClass:RegisterCallback(15, self.Callback) -- this is where I want to pass this instances function to be called back from the C++ class
end

function MyClass:Callback(reader)
    -- I want to be able to use self here as well so it needs to be the same instance
end

Inside the MyClass:Enter() is where I want to register the lua "class" function MyClass::Callback to be able to be called from the C++ object. How would I pass this into a C++ function? What would the type be so that it will call MyClass:Callback() from C++ also passing in "self" so that it's the same "instance" of the class?

So I know 'self' is referring to an actual variable that I created and I assume will be in the global table by variable name, but when you are inside the lua "class" how can I tell what variable name 'self' is referring too? If I could get that I could pass that to my C++ function and store it as a string so that I can call getglobal on it to get that specific table, and then I could also pass the table function name as string and in C++ I could get that also, and call it. But the question would be, how can I convert 'self' to the actual variable name it's pointing too so I can call getglobal in C++ to get that table?

Upvotes: 0

Views: 2501

Answers (2)

Oliver
Oliver

Reputation: 29591

You exported CPlusClass "class" from C++ to Lua: surely this C++ class has a RegisterCallback which accepts a pointer to a function or to a YourCallbackClass instance which uses virtual methods to dispatch to the correct object. Then all you need is to export YourCallbackClass to Lua: behind the scenes, the Lua version will create an instance and give it to your C++ RegisterCallback. For instance:

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

Your CPlusCallback is likely different, this is just an example. For example if you just want functions not methods then you don't need to store the callee. If you want both, then CPlusCallback must be a base class and the call() is virtual, with the derived classes implementing the details as appropriate. Adapt to your situation.

Then export CPlusCallback and SomeObj to Lua, then you can do in Lua:

somObj = SomeObj:Create()

function MyClass:Enter()
    -- creates Lua instance of CPlusClass, with a user data for the C++
    -- counterpart as member
    self.CPlusClass = CPlusClass:Create()  
    self.CPlusCallback = CPlusCallback:Create(someObj, someObj.method)
    self.CPlusClass:RegisterCallback(15, self.CPlusCallback)
end

where the above assumes that someObj.method is a method you exported, from C++ class SomeObj, with the proper signature void (SomeObj::*Method)().

Upvotes: 3

Etan Reisner
Etan Reisner

Reputation: 81032

If you want to keep the RegisterCallback signature and operation (as opposed to enhancing it to understand object method callbacks in some fashion) then you need to create a closure and pass that as the callback. Something like this:

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

and

function MyClass:Enter()
    self.CPlusClass = CPlusClass:Create()   -- this is the C++ class creation, I defined a static function Create()
    self.CPlusClass:RegisterCallback(15, create_closure(self, "Callback"))
end

Upvotes: 2

Related Questions