c++clualuabind

Lua return custom data from C function


Despite searching hard, i couldn't find a valid Lua C API example for calling a Lua function returning custom data from C function. For example, I have register function "GetMyVector" and then I'm calling that from lua to retrive informations from C, what I got is a table but what I want is something like access to access variable from struct like in C, for example:

local x = GetMyVector()
print(x[1]) -- i 
print(x[2]) -- j
print(x[3]) -- k
-- how to access it via like this:
print(x.i)
print(x.j)
print(x.k)

my C function pushing vector in 3 dimensional array by lua_pushnumber:

static int GetMyVector(lua_State *L)
{
    vec3_t vec;
    vec[0] = 1;
    vec[1] = 2;
    vec[3] = 3;
    lua_newtable(L);
    lua_pushnumber(L, vec[0]);
    lua_rawseti(L, -2, 1);
    lua_pushnumber(L, vec[1]);
    lua_rawseti(L, -2, 2);
    lua_pushnumber(L, vec[2]);
    lua_rawseti(L, -2, 3);
    return 1;
}

Solution

  • It's a little bit extended, but thanks to @geov I have found what I was looking for, it's kinda similar to properties style like in C# i think, here you go the solution:

    #define MYCLASSNAME "vec"
    
    typedef struct {
        int i, j, k;
    } MyVec_t;
    
    typedef int(*Xet_func) (lua_State *L, void *v);
    
    /* member info for get and set handlers */
    typedef const struct{
        const char *name;  /* member name */
        Xet_func func;     /* get or set function for type of member */
        size_t offset;     /* offset of member within MyVec_t */
    }  Xet_reg_pre;
    
    typedef Xet_reg_pre * Xet_reg;
    
    // properties
    static int Get_Int(lua_State *L, void *v) {
        lua_pushnumber(L, *(int*)v);
        return 1;
    }
    
    static int Set_Int(lua_State *L, void *v) {
        *(int*)v = luaL_checkinteger(L, 3);
        return 0;
    }
    
    static int Get_Number(lua_State *L, void *v) {
        lua_pushnumber(L, *(lua_Number*)v);
        return 1;
    }
    
    static int Set_Number(lua_State *L, void *v) {
        *(lua_Number*)v = luaL_checknumber(L, 3);
        return 0;
    }
    
    static int Get_String(lua_State *L, void *v) {
        lua_pushstring(L, (char*)v);
        return 1;
    }
    
    static void Property_Add(lua_State *L, Xet_reg l)
    {
        for (; l->name; l++) {
            lua_pushstring(L, l->name);
            lua_pushlightuserdata(L, (void*)l);
            lua_settable(L, -3);
        }
    }
    
    static int Property_Call(lua_State *L)
    {
        Xet_reg m = (Xet_reg)lua_touserdata(L, -1);  
        lua_pop(L, 1);                               
        luaL_checktype(L, 1, LUA_TUSERDATA);
        return m->func(L, (void *)((char *)lua_touserdata(L, 1) + m->offset));
    }
    
    static int index_handler(lua_State *L)
    {
        /* stack has userdata, index */
        lua_pushvalue(L, 2);                     /* dup index */
        lua_rawget(L, lua_upvalueindex(1));      /* lookup member by name */
        if (!lua_islightuserdata(L, -1)) {
            lua_pop(L, 1);                         /* drop value */
            lua_pushvalue(L, 2);                   /* dup index */
            lua_gettable(L, lua_upvalueindex(2));  /* else try methods */
            if (lua_isnil(L, -1))                  /* invalid member */
                luaL_error(L, "cannot get member '%s'", lua_tostring(L, 2));
            return 1;
        }
        return Property_Call(L);                      /* call get function */
    }
    
    static int newindex_handler(lua_State *L)
    {
        /* stack has userdata, index, value */
        lua_pushvalue(L, 2);                     /* dup index */
        lua_rawget(L, lua_upvalueindex(1));      /* lookup member by name */
        if (!lua_islightuserdata(L, -1))         /* invalid member */
            luaL_error(L, "cannot set member '%s'", lua_tostring(L, 2));
        return Property_Call(L);                      /* call set function */
    }
    
    static MyVec_t *CheckMyVec(lua_State *L, int index) // get data
    {
        MyVec_t *p;
        luaL_checktype(L, index, LUA_TUSERDATA);
        p = (MyVec_t *)luaL_checkudata(L, index, MYCLASSNAME);
        return p;
    }
    
    static MyVec_t *PushMyVec(lua_State *L) // push data
    {
        MyVec_t *p = (MyVec_t *)lua_newuserdata(L, sizeof(MyVec_t));
        luaL_getmetatable(L, MYCLASSNAME);
        lua_setmetatable(L, -2);
        return p;
    }
    
    static int MyVec_Create(lua_State *L)   // C function which will push data
    {
        MyVec_t *p;
        p = PushMyVec(L);
        p->i = luaL_checkinteger(L, 1);
        p->j = luaL_checkinteger(L, 2);;
        p->k = luaL_checkinteger(L, 3);;
        return 1;
    }
    
    static int MyVec_destroy(lua_State *L)
    {
        MyVec_t *p = (MyVec_t *)lua_touserdata(L, 1);
        return 0;
    }
    
    static int MyVec_Position(lua_State *L)
    {
        MyVec_t *p = CheckMyVec(L, 1);
        double   x = p->i;
        double   y = p->j;
        double   z = p->k;
        if (lua_gettop(L) > 1) {
            p->i = luaL_checknumber(L, 2);
            p->j = luaL_checknumber(L, 3);
            p->k = luaL_checknumber(L, 4);
        }
        lua_pushnumber(L, x);
        lua_pushnumber(L, y);
        lua_pushnumber(L, z);
        return 2;
    }
    
    
    static const luaL_Reg myvec_meta_methods[] = {
        { "__gc", MyVec_destroy },
        { 0, 0 }
    };
    
    static const luaL_Reg myvec_methods[] = {
        { "create",  MyVec_Create },
        { "position", MyVec_Position },
        { 0, 0 }
    };
    
    static const Xet_reg_pre MyVec_get[] = {
        { "i", Get_Int, offsetof(MyVec_t, i) },
        { "j", Get_Int, offsetof(MyVec_t, j) },
        { "k", Get_Int, offsetof(MyVec_t, k) },
        { 0, 0 }
    };
    
    static const Xet_reg_pre MyVec_set[] = {
        { "i", Set_Int, offsetof(MyVec_t, i) },
        { "j", Set_Int, offsetof(MyVec_t, j) },
        { "k", Set_Int, offsetof(MyVec_t, k) },
        { 0, 0 }
    };
    
    
    int MyVec_Register(lua_State *L)
    {
        int metatable, methods;
    
        /* create methods table, & add it to the table of globals */
        luaL_openlib(L, MYCLASSNAME, myvec_methods, 0);
        methods = lua_gettop(L);
    
        /* create metatable for MyVec_t, & add it to the registry */
        luaL_newmetatable(L, MYCLASSNAME);
        luaL_openlib(L, 0, myvec_meta_methods, 0);  /* fill metatable */
        metatable = lua_gettop(L);
    
        lua_pushliteral(L, "__metatable");
        lua_pushvalue(L, methods);              /* dup methods table*/
        lua_rawset(L, metatable);               /* hide metatable:
                                                metatable.__metatable = methods */
        lua_pushliteral(L, "__index");
        lua_pushvalue(L, metatable);            /* upvalue index 1 */
        Property_Add(L, MyVec_get);             /* fill metatable with getters */
        lua_pushvalue(L, methods);              /* upvalue index 2 */
        lua_pushcclosure(L, index_handler, 2);
        lua_rawset(L, metatable);               /* metatable.__index = index_handler */
    
        lua_pushliteral(L, "__newindex");
        lua_newtable(L);                        /* table for members you can set */
        Property_Add(L, MyVec_set);             /* fill with setters */
        lua_pushcclosure(L, newindex_handler, 1);
        lua_rawset(L, metatable);               /* metatable.__newindex = newindex_handler */
    
        lua_pop(L, 1);                          /* drop metatable */
        return 1;                               /* return methods on the stack */
    }