lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

于 2011-8-27 22:46, Jeff Smith 写道:
I have done a little bit of work with Lua C API bindings, that all went pretty smoothly, but I am having great difficulty understanding userdata and light userdata. I have not found the examples online easy to follow or very helpful, so I am hoping someone on this list might be kind enough to help out. The example I want to implement is to expose to Lua a C structure so Lua can access the individual data variables
struct foo
   int x;
   int y;
   int z;
struct foo myFoo;
-- In Lua I want to do something like
local x = myFoo.x
local y = myFoo.y
As I dont want Lua to create myFoo objects, or manage their memory, I am thinking this example should be implemented with light userdata, but dont know how to go about this.
This example is a slightly simplified version of what I need to do,  in reality I have a C array, such as    struct foo myFoo[3];
so ultimately I would want to do something like this in Lua,    local x = myFoo[2].x.
I am not sure how much the array version complicates the solution for the non array version ?
Any example C API code and explanation would be most appreciated if anyone fancies brushing up their skills to try this out ?
Regards Geoff 

a light userdata is just a "pointer". lua code can only do assignment and test the equality of userdata, all other operations (e.g. index) must be defined in C code.

simply saying, you store a C pointer into lua using light userdata, and define various operations that can be access from lua as metamethods.

for instance, you know, the lua code `myFoo.x' is actually syntax suggar for 'myFoo["x"]', which is a *index* operation with a key of the "string" type.

so if you want to access myFoo.x and myFoo.y from lua like this
 local x = myFoo.x
 local y = myFoo.y

you should :

1.  put a light userdata, or pointer (which points to the C struct `myFoo'), into lua, with the (global) name `myFoo'
    lua_pushlightuserdata(L, (void*)&myFoo);
    lua_setglobal(L, "myFoo");
2.  define the *index* operation (e.g. as a lua_CFunction) that accept the string "x" and "y" as key and return the x and y member of the C struct respectively.
    int foo_indexer (lua_State *L) {
        struct foo * p = lua_touserdata(L, 1);
        const char *key = lua_tostring(L, 2);
        if (p != NULL & key != NULL) {
            if ( key[0] == 'x' && key[1] == '\0') {      // key is "x"
                lua_pushinteger(L, p->x);
                return 1;
            else if ( key[0] == 'y' && key[1] == '\0') {  // key is "y"
                lua_pushinteger(L, p->y);
                return 1;
        return 0;
3.  assiociate the index operation with the userdata. through metamethod __index
    lua_getglobal(L, "myFoo");
    lua_pushcfunction(L, &foo_indexer);
    lua_setfield(L, -2, "__index");
    lua_setmetatable(L, -2);
    lua_pop(L, -1);

then,  in lua, the global myFoo is now a lightuser data.
but lua code can only assign it to other variable, or compare it with other value for equality, or index it,
all other operations would give a error, since we have only defined the `__index' metamethod.
    local x = myFoo.x
       -- metacall foo_indexer(myFoo, "x")

    mt = getmetatable(myFoo)
        -- mt.__index == foo_indexer

    print(type(x), type(myFoo["y"]), type(, type(myFoo[1]))
       -- would print : Number Number Nil Nil

    myBar = myFoo
       -- assignment

    assert( myBar == myFoo)
       -- equality test

    myFoo.x = 100
        -- issue a error message something like "newindex operation is not defined"

one thing to note:
all light userdata values in a same lua (global) State share a same metatable.
in other words, metatable for light userdata is not "per object".

the code I show is demostrating and not tested.
the important thing is to understand the concepts.

Hopefully may help.