3

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

  • We do some complex function dispatch by custom reflection logic in C++
  • obj (and all params) are fixed wrapper type (similar to boost::any)

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.

1 Answer 1

2

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.

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

5 Comments

Also note that this method creates a closure around k each time you call it. To prevent that in tight loops, you may cache inner functions (return function...) and return from cache for already-seen ks. (Added sample code for that)
The wrapper should probably return whatever call returns back to the callsite.
@greatwolf - Thanks! Fixed. A note for this fix: as it makes tail-call now, any level-related lua routines (e.g. error with level argument) should take level-1, because anonymous function(obj, ...) will be replaced by call.
@user3125367 Thanks! the lua version works! But, how to achieve it via C interface? Since the obj was created by C function, it's inconvenient to modify its metatable via lua
@ZSaberLv0 - Updated for C

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.