C/C++中实现__index元方法

我有一个 C++ 回调/函数对象系统的脚本,可以使用字符串和/或变量调用任何“已注册”的 C++ 函数。

//已删除此帖子的错误检查和错误代码
int LuaGameObject::LuaCallFunction( lua_State *luaState )
{
    if ( lua_isuserdata( luaState, 1 ) == 1 )
    {
        int nArgs = lua_gettop( luaState );

        //获取 GameObject
        OGameObject* pGameObject = static_cast<OGameObject*>(lua_touserdata( luaState, 1 ));
        if ( pGameObject )
        {
            //获取 FunctionName
            const char* functionNameString = lua_tostring( luaState, 2 );

            //获取 Arguments
            std::vector<OVariant> args;
            for ( int i = 3; i <= nArgs; ++i )
            {
                OVariant variant;
                variant.SetFromLua( luaState, i );
                args.push_back( variant );
            }

            //调用它!
            CallGameObjectFunction( luaState, pGameObject, functionNameString, args );

            return 1;
        }
    }

    return 0;
}

OVariant LuaGameObject::ExecuteLua()
{
    lua_State *lState = luaL_newstate();

    luaL_openlibs( lState );
    lua_register( lState, "Call", LuaCallFunction );

    luaL_loadstring( lState, m_pScript );

    //现在运行它
    lua_pcall( lState, 0, 1, 0 );

    //处理返回值
    OVariant result;
    result.SetFromLua( lState, -1 );

    lua_close( lState );

    return result;
}

在 Lua 中,我可以这样做...

local king = Call("EmpireManager","GetKing")
Call("MapCamera","ZoomToActor",king)

然而,我感觉可以使用 __index 元方法来简化 Lua...

local king = EmpireManager:GetKing()
MapCamera:ZoomToActor(king)

我希望通过使用以下 __index 元方法实现简化的 Lua

这是我如何注册 __index metafunction 的方式...(大部分从在线示例中复制)

void LuaGameObject::Register( lua_State * l )
{
    luaL_Reg sRegs[] =
    {
        { "__index", &LuaGameObject::LuaCallFunction },
        { NULL, NULL }
    };

    luaL_newmetatable( l, "luaL_EmpireManager" );

    // 把 C 函数注册到我们刚刚创建的 metatable 中
    luaL_setfuncs( l, sRegs, 0 );
    lua_pushvalue( l, -1 );

    // 使 metatable 的 "__index" 字段指向自身
    // 这将弹出堆栈
    lua_setfield( l, -1, "__index" );

    // 现在使用 setglobal 正式向 Lua 公开 luaL_EmpireManager metatable
    // 并使用名称“EmpireManager”
    lua_setglobal( l, "EmpireManager" );
}

不幸的是,我似乎无法正确设置回调。Lua 正确调用我的 LuaGameObject::LuaCallFunction,但堆栈不包含我想要的内容。在 LuaGameObject::LuaCallFunction 中,我可以找到函数名称和 EmpireManager 对象的堆栈。但是,我找不到堆栈中的参数。设置方式是什么?还是不可能的?

点赞
用户2510649
用户2510649

好的,经过更多的研究,我现在相信我不能使用 __index 元函数来调用带参数的 c 函数。它只传递表名和键到回调函数。

然而,对于任何有兴趣的人来说,它可以用于类似于表的对象,但不适用于函数(因为参数不会压入堆栈)。我将它用于我的 "property" 对象。它们没有参数,可以在 lua 中如下使用...

local king = EmpireManager:king
king:name = "Arthur"
local name = king:name

这些适当地链接和调用相应的 C++ 对象函数

Actor::SetName(std::string name)
std::string Actor::GetName()

2018-06-15 19:25:23
用户9822560
用户9822560

在 Lua 中,可以给“userdata”类型添加方法,如官方网站上Programming in Lua所述。

当你输入以下 Lua 代码时:

myUserdata:someMethod(arg1,arg2,arg3)

假设myUserdata是一个“userdata”对象,则解析器将执行以下操作。

  1. 调用getmetatable(myUserdata).__index(myUserdata,"someMethod")来获取someMethod的值。
  2. 调用someMethod(myUserdata,arg1,arg2,arg3),而someMethod可以是任何在 Lua 中可调用的函数,例如:Lua 函数、C 函数或具有__call元方法的表/用户数据。

你的__index元方法应该只返回一个实现该方法的函数(或另一个在 Lua 中可调用的对象)。像这样:

// 不太准确的 __index 元方法名称(有一个 __call 元方法)
int LuaGameObject::LuaCallFunction( lua_State *l)
{
    // 待完成:错误检查
    OGameObject* pGameObject = static_cast<OGameObject*>(lua_touserdata( luaState, 1 ));
    std::string memberName = lua_tostring( luaState, 2 );

    int result = 1;
    if (memberName == "method1") {
        lua_pushcfunction(l,LuaGameObject::luaMethod1);
    } else if (memberName == "method2") {
        lua_pushcfunction(l,LuaGameObject::luaMethod2);
    } else {
        result = 0;
    }

    return result;
}

返回的__index元方法函数的基本骨架:

int LuaGameObject::luaMethod1(lua_State* l) {
    // todo:错误检查。
    OGameObject* pGameObject = static_cast<OGameObject*>(lua_touserdata(l, 1));
    float arg1 = lua_tonumber(l, 2);
    // 获取其他参数
    pGameObject->method1(arg1 /*, 如果有其他参数则为这里补充*/);
    // 可选:将返回值压入堆栈。
    return 0; // <-- 返回值的数量。
}
2018-06-16 13:55:02