4

I'm using Lua inside a C application, and I have two tables. I want to create a third table that, while empty, will index values from my first two tables. I wrote the following simple example in Lua -

a = { one="1", two="2" }
b = { three="3", four="4" }

meta = { __index = function(t,k)
  if a[k] == nil then return b[k]
  else return a[k] end
end }

c = {}
setmetatable(c, meta)

print(c.one) -- prints "1"
print(c.four) -- prints "4"

My question is, what is the most effective way to do this from the C API? I've been able to do this by creating a new table, pushing the above Lua code chunk to that table, then calling setmetatable() on it but this seems less than optimal. Is there a better way?

2
  • 3
    BTW, your __index function reduces to return a[k] or b[k]. Commented Aug 10, 2010 at 16:31
  • 6
    @Judge Maygarden: Not in the case a[k] can be false Commented Aug 11, 2010 at 6:13

2 Answers 2

11
#include <stdio.h>
#include "lua.h"

/* __index metamethod for the 'c' table (stack: 1 = table 'c', 2 = desired index) */
static int
cindex(lua_State *L)
{
    /* try the global 'a' table */
    lua_getglobal(L, "a");
    lua_pushvalue(L, 2);
    lua_gettable(L, -2);
    if (!lua_isnil(L, -1))
        return 1;

    /* try the global 'b' table */
    lua_getglobal(L, "b");
    lua_pushvalue(L, 2);
    lua_gettable(L, -2);
    if (!lua_isnil(L, -1))
        return 1;

    /* return nil */
    return 0;
}

int
main(int argc, char **argv)
{
    lua_State *L;

    L = (lua_State *) luaL_newstate();
    luaL_openlibs(L);

    /* create the global 'a' table */
    lua_createtable(L, 0, 2);
    lua_pushstring(L, "1");
    lua_setfield(L, -2, "one");
    lua_pushstring(L, "2");
    lua_setfield(L, -2, "two");
    lua_setglobal(L, "a");

    /* create the global 'b' table */
    lua_createtable(L, 0, 2);
    lua_pushstring(L, "3");
    lua_setfield(L, -2, "three");
    lua_pushstring(L, "4");
    lua_setfield(L, -2, "four");
    lua_setglobal(L, "b");

    /* create the global 'c' table and use a C function as the __index metamethod */
    lua_createtable(L, 0, 0);
    lua_createtable(L, 0, 1);
    lua_pushcfunction(L, cindex);
    lua_setfield(L, -2, "__index");
    lua_setmetatable(L, -2);
    lua_setglobal(L, "c");

    /* run the test script */
    luaL_loadstring(L, "print(c.one)\nprint(c.four)");
    if (0 != lua_pcall(L, 0, 0, 0)) {
        puts(lua_tostring(L, -1));
        return 1;
    }

    return 0;
}
Sign up to request clarification or add additional context in comments.

Comments

2

Are you able to modify b's metatable? If so, this is more efficient:

a = { one="1", two="2" }
b = { three="3", four="4" }

setmetatable(a, { __index = b })

-- setmetatable(x, m) returns x, so you can do this:
c = setmetatable({}, { __index = a }) -- meta is here, too

print(c.one) -- prints "1"
print(c.four) -- prints "4"

When __index points to a table, it is more efficient than when it points to a function; I read somewhere that it was equivalent to 3 indirections in C. So in the worst case (c.one) there's a total of 6 indirections.

2 Comments

As presented here b trumps a; not the other way around as presented in the question. Just minor nit-picking
@JosephMariadassou thanks for your comment, i have switched the order of the dependencies so that a is looked at first instead of last.

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.