1
\$\begingroup\$

Basically, I'm trying to register a callback into a Lua state like this:

class MyClass
{
    public:
        int myLuaFunction(lua_State* L)
        {
            //do something
        }

};

I want to be able to call MyClass::myLuaFunction of a given instance (which could register itself).

I've spent a couple hours trying to do tricks with templates and lambdas in an attempt to get it to work, but I haven't got anything successful (mainly the fact that I couldn't pass a lamba function that uses parent scope variables to Lua's API).

Without having to download any other libraries (just Lua's standard interpreter implementation), how could I do this?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

While a non-capturing lambda function can be converted into a free function pointer, a capturing one can not, which you've discovered.

The generic approach to using member functions as callbacks is a common problem and is typically resolved via thunking or trampolines.

In short, you make a static/free function which compatible with the mandated interface, and hide a pointer to the object you want to call it on in user-specified data. The function body would extract this object and make a call to the actual member function, forwarding the parameters and return values.

You can fiddle around with templates and member function pointers to get a single thunk function template that can dispatch to functions with a particular arity, but then you've reinvented most of luabind or similiar libraries.

An example in a fictional interface:

typedef int (*Fn)(State*);
void RegisterFn(Fn fn, void* userData);
void* GetUserData(State* state);

struct S {
    int f(State*);
};

int SfThunk(State* state) {
    void* userData = GetUserData(state);
    S* obj = (S*)userData;
    return obj->f(state);
}

S s;
RegisterFn(&SfThunk, &s);
\$\endgroup\$
1
  • \$\begingroup\$ The problem here is that I'd need one SfThunk-type function for each callback since Lua uses one callback per C function-call. \$\endgroup\$
    – Verody
    Commented May 11, 2015 at 14:15
0
\$\begingroup\$

Push this as a light uservalue when pushing the callback as a closure and make it an upvalue:

class MyClass
{
    public:
        int myLuaFunction(lua_State* L)
        {
            //do something
        }

        static int myLuaFunctionCallback(lua_State* L){
            lua_getupvalue(L, lua_upvalueindex(1)); //pushes the light userdata
            MyClass* obj = static_cast<MyClass*>(lua_touserdata(L, -1)); //gets the value of the user data
            lua_pop(L); //pop userdata
            obj->myLuaFunction(L); //call actual function
        }
};

and to register the callback you do:

lua_pushlightuserdata(L, obj);
lua_pushcclosure(L, MyClass::myLuaFunctionCallback, 1);
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .