0

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?

14
  • self is not a global, self is an automatic variable defined by the table:function syntactic sugar. function table:class(args...) ... end is identical to function table.class(self, args...) ... end. Similarly a call to tab:class(args...) is identical to tab.class(tab, args...) (though tab is evaluated only once). Commented Nov 20, 2013 at 17:14
  • I understand that. self however is referring to a table I created somewhere in my lua program. How can I tell what variable name self is referring to? Commented Nov 20, 2013 at 17:15
  • You can't disassociate a function from its table "object" and have it work on that table "object" again later. You would need to create a closure as your callback function to do what you wanted to do here. Commented Nov 20, 2013 at 17:16
  • self is the variable name. It is the first argument to the function. It is the table on which the function is called. Commented Nov 20, 2013 at 17:16
  • but self is referring to a table I created somewhere in the lua program is it not? that table variable name must be in the global table is it not? Commented Nov 20, 2013 at 17:17

2 Answers 2

3

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)().

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

1 Comment

Thanks for an alternative method. Currently I got the method of sending self, then a string method name of the lua class and calling it back from C++. Very similar to what you did here. I was concerned with the details on how it's done but I figured it out.
2

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

12 Comments

My 2nd param to RegisterCallback() was actually a string. My original hope was to be able to get the variable name self was referring to and concat it like "obj:Callback" and then break it out in the C++ function. What would the second param need to be if I did this method?
RegisterCallback takes a string? It assumes that names a global function that can be looked up later?
It's a string because my thought process was to be able to lookup the table name that has the function, which would mean it's assuming it's global. I'm the only one working with this so I control that. However changing the param isn't a huge deal, but I'd just need to know what a closure returns and what type my C++ function would be expecting. Also, my C++ program would be passing 1 parameter of a lightuserobject to the closure. I assume the ... handles all the params automatically?
The closure doesn't return anything for this purpose. The closure is the return from create_closure and a closure is just a function. You can't store a lua function in C++ directly (unless your binding handles the necessary work for you). What you store is a reference to the function in a known table and then look it up again when you need it. See luaL_ref in the lua manual for a lua api example of this idea. Yes, ... is lua varargs.
So the point of this is to be able to call a lua function from C++, so I would have to store something in C++ to be able to call it at some point from C++. In the past without classes I would pass in the string name of a lua function so that I could get it and call it from C++. Now that this is a "class" function, I still need to store something to be able to call it later from C++. Since it's anonymous it doesn't have a name, but I assume it's in the global table somewhere (just no name?). Does lua behind the scenes give it some value that I can lookup in C++ to call it or something?
|

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.