0x11901
0x11901

Reputation: 389

How to modify a metatable which was created by C API?

I want to add some methods or properties to a lua object witch metadata was created by C API. I can't add property in normal way, for example:

local foo = libc.new()
foo.bar = "hello"

it say:

Failed to run script: attempt to index a libc_meta value (local 'foo')

So I think maybe need to modify metatable, so I change my code:

local foo = libc.new()
local mt = getmetatable(foo)
foo[bar] = "hello"
setmetable(foo, mt)

Unfortunately, it still doesn't work.

Failed to run script: bad argument #1 to 'setmetatable' (table expected, got libc_meta)

So how can I add methods or properties to this 'foo'?

BTW, c code is here:

static int libc_new(lua_State *L) {
    ...
    lua_lib_space *libc = lua_newuserdata(L, sizeof(*libc));
    libc->L = L;
    libc->cb_enter = cb_enter;
    libc->cb_leave = cb_leave;
    luaL_getmetatable(L, "libc_meta");
    lua_setmetatable(L, -2);
    lib_space *lib = lib_new(enter, leave, libc);
    libc->space = lib;
    return 1;
}

Upvotes: 1

Views: 857

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 474436

Userdata is meant to be created by C, manipulated by C code, and for only the purposes that C code intends. Lua can talk to it, but only in the ways that C allows it to. As such, while userdata can have a metatable (and those metamethods are the only way Lua can "directly" interact with the userdata), only C functions (and the debug library, but that's cheating) can directly manipulate that metatable. Lua is an embedded language, and C has primacy; if some C library wants to shut you out, it can.

What you can do is take the userdata and stick it in a table of your own, then give that table a metatable. In your __index metamethod, if you have an override or new method for a particular key, then you forward the accesses to that. Otherwise, it will just access the stored userdata.

However, if what you get in the userdata is a function, then you may have a problem. See, most userdata functions take the userdata as a parameter; indeed, most are meant to be called via "ud:func_name(params). But if thatud` is actually the table wrapper, then the first parameter passed to the function will be the wrapper, not the userdata itself.

That poses a problem. When the thing you get from the userdata is a function, you would need to return a wrapper for that function which goes through the parameters and converts any references to the wrapper table into the actual userdata.

Upvotes: 2

Related Questions