ZSaberLv0
ZSaberLv0

Reputation: 267

Simplify Lua function call to C function

I'm a newbie to Lua

I have a C function registered to Lua which looks like this:

call(obj, "func", param0, param1)

In the call():

Because of that, we can't do simple class member register using regular Lua bind tools (luabind, luabridge, etc)

Here's the question:

How can we simplify the Lua call to be like this:

obj.func(param0, param1)

or

obj:func(param0, param1)

?

Thanks.

Upvotes: 3

Views: 328

Answers (1)

user3125367
user3125367

Reputation: 3000

You have to set a metatable on each obj you create (assuming it is userdata under your control and has no metatable) and override __index metamethod in it:

local cache = { }

debug.setmetatable(obj, {
    __index = function (obj, k)
        return function (obj, ...) -- or (...) for [non-canonical] .-syntax
            return call(obj, k, ...)
        end

        -- malloc-optimized version --
        local f = cache[k] or function (obj, ...) return call(obj, k, ...) end
        cache[k] = f
        return f
    end,
    __metatable = "whatever",
})

f = obj.func
f(obj, param0, param1) --> call(obj, 'func', param0, param1)

obj:func(param0, param1) --> the same via syntactic sugar

If obj already has a metatable, then that metatable has to be modified in a similar way.

The same may be done via C interface, so you can combine obj creation with __index setup.


Update on C side:

If creation function is in external lib, then you have no options except catching all object appearance points (return values and values in tables passed as arguments) and wrap there as described above.

If creation function is under your control, you can see something like that there:

ud = lua_newuserdata(L, sizeof(object));
*ud = object;

// this part is missing if objects have no metatable at all
luaL_getmetatable(L, tname); // or 'if (luaL_newmetatable(L, tname)) { ... }'
lua_setmetatable(L, -2);

return 1;

You have to add the __index metamethod at where metatable is created:

...somewhere in the code, maybe right after *ud = object line...

if (luaL_newmetatable(L, tname)) {
    ... original metatable setup ...

    int res = luaL_loadstring(L,
        "return function (obj, k)\n"
        "    return function (obj, ...)\n"
        "        return call(obj, k, ...)\n"
        "    end\n"
        "end\n");
    assert(res == 0);

    lua_call(L, 0, 1);
    lua_setfield(L, -2, "__index");
}

If your userdata has no metatable at all, it has to be created and set.

If you want to get rid of global symbol call, then pass its C implementation as an argument to luaL_loadstring'ed chunk:

luaL_loadstring(L, "local call = ...\n return function (obj, k)\n" ...);
lua_pushcfunction(L, l_call);
lua_call(L, 1, 1); // instead of (0, 1)

Then call will be localized into the closure.

Upvotes: 2

Related Questions