保存对Lua用户数据的引用

#1 Lua:

local test = Test();

本地变量test赋值为Test()函数的返回值。

#2 C:

//creating "lua's test"
luaL_newmetatable(L, "someTable");
lua_userdata *userData = (lua_userdata *)lua_newuserdata(L, sizeof(lua_userdata));

luaL_getmetatable(L, "someTable");
lua_setmetatable(L, -2);

创建元表“someTable”,并创建名为lua_userdatauserdata。获得元表“someTable”,为userdata设置元表。

#3 Lua:

function test.newMethod()
end

添加test对象的newMethod()方法。

#4 C:

//this part is not executed from Lua
//what i have to have here from #2 to call "test.newMethod" and how to call it?
//if userdata would be on stack i guess i could:
luaL_getmetafield (L, 1, "newMethod");
lua_call(L, 0, 0);
//but because this part is not executed by Lua call its not on stack.

这部分代码没有被Lua执行,所以没有userdata在栈上。如果userdata在栈上,可以用luaL_getmetafield(L, 1, "newMethod")获得newMethod()方法,并用lua_call(L, 0, 0)调用它。

编辑:

试着用伪代码更简单地解释:

Lua:

local test = Object();

C:

int Object(){
    ...
    somePointer = luaL_newmetatable(...); //如何得到“somePointer”? 可以用luaL_ref()吗?
    push ...
}

Lua: 创建新的方法

function test.newMethod()
    ...
end

C:某个事件(比如计时器)触发C方法

void triggeredCMethod(){
    //这里我需要调用test.newMethod方法
    //如果我能得到 'somePointer' 并将其推到Lua栈上,我就能查找并调用newMethod()
}

所以问题是:如何在C中存储指向某个Lua对象的指针(希望我需要这个),如何通过该指针获得Lua对象并调用其中的方法。

原文链接 https://stackoverflow.com/questions/1646497

点赞
stackoverflow用户41661
stackoverflow用户41661

如果你只是想调用 Lua 函数 `test.newMethod()',我认为你需要大致使用这样的方式:

lua_getglobal(L, "test");
lua_getfield(L, -1, "newMethod");
lua_call(L, 0, 0);

我不认为你需要使用元表或用户数据来实现。

然而,你问题的意图并不是完全清楚的。

2009-10-30 01:29:27
stackoverflow用户197131
stackoverflow用户197131

我假设您想要能够调用动态添加的函数。这段代码应该相对简单地解释了它。请注意,我没有做太多的错误检查并做了一些假设,不要将它复制粘贴为解决方案。

typedef struct
{
    int number;
    int reference;
    lua_State *L;
} TestUserdata;

static int m_newindex( lua_State *L )
{
    /* 这个函数会传递三个值,第一个(-3)是对象,将其放到最前面*/
    lua_getfenv( L, -3 );
    /* 现在将第二个参数(即key)向前传输*/
    lua_pushvalue( L, -3 );
    /* 还有第三个参数(即value)*/
    lua_pushvalue( L, -3 );
    /* 然后我们就完成了*/
    lua_rawset( L, -3 );

    return 0;
}

static int m_tostring( lua_State *L )
{
    lua_pushstring( L, "TestUserdata" );
    return 1;
}

static int callobject( lua_State *L )
{
    /* 抓住传入的对象,检查它是否是正确的类型*/
    TestUserdata *data = luaL_checkudata( L, 1, "TestUserdata" );

    /* 获取我们在createobject中给它的函数环境,并在其中查找newmethod*/
    lua_getfenv( L, -1 );
    lua_pushstring( L, "newmethod" );
    lua_rawget( L, -2 );

    /* 调用函数*/
    lua_pushinteger( L, data->number );
    lua_call( L, 1, 0 );

    return 0;
}

static const struct luaL_reg userdata_m[] = {
    { "__newindex", m_newindex },
    { "__tostring", m_tostring },
    { NULL, NULL }
};

int main (int argc, char *argv[])
{
    lua_State *L = luaL_newstate();
    luaL_openlibs( L );

    /* 让我们创建一个用户数据元表,并将其填充*/
    luaL_newmetatable( L, "TestUserdata" );
    /* 使用luaL_register为我们填充元表*/
    luaL_register( L, NULL, userdata_m );
    lua_pop( L, 1 ); /* 清理堆栈,我们不需要在这里留下元表*/

    TestUserdata *data = lua_newuserdata( L, sizeof( TestUserdata ) );
    lua_pushvalue( L, -1 ); /* 复制用于luaL_ref*/
    int ref = luaL_ref( L, LUA_REGISTRYINDEX );
    data->reference = ref;
    data->number = 42;
    data->L = L;

    /*从之前的元表中加载并给这个用户数据*/
    luaL_getmetatable( L, "TestUserdata" );
    lua_setmetatable( L, -2 );

    /*为这个对象提供空的函数环境*/
    lua_newtable( L );
    lua_setfenv( L, -2 );

    lua_setglobal( L, "test" );

    luaL_dostring( L, "function test.newmethod( num ) print( num ) end" );

    /* 现在只要有对象,就可以调用任何在任何地方定义过的方法*/
    lua_rawgeti( data->L, LUA_REGISTRYINDEX, data->reference );
    lua_getfenv( data->L, -1 );
    lua_pushstring( data->L, "newmethod" );
    lua_rawget( data->L, -2 );
    lua_remove( data->L, -2 );

    if( lua_isfunction( data->L, -1 ) == 1 )
    {
        lua_pushinteger( data->L, data->number );

        lua_pcall( data->L, 1, 0, 0 );
    }

    lua_close( L );

    return 0;
}

请检查一下,我认为这就是您要的内容。

2009-10-30 06:25:02