【问题标题】:Lua/C++ : __index is always invoked even the table's field is knownLua/C++:__index 总是被调用,即使表的字段是已知的
【发布时间】:2013-12-18 09:50:50
【问题描述】:

我想在 Lua 中声明由 C++ 应用注册的全局元表。

我定义了元表和一些字段的 __index 元方法,但是当 lua 脚本访问已知字段时,__index 总是在我的 C++ 应用程序中调用。

例如,我想注册一个名为 User 的全局表,其中包含三个字段:FirstName、LastName 和 Age em>。

这是我注册表格的方法。该表被封装在一个名为 TLuaStruct 的类中,这使得在 Lua 中注册表更容易。

bool TLuaStruct::InternalRegister( std::string tName, bool AddInGC )
{
    TLuaStack *ptStack;
    TLuaStruct **ptUserLuaStruct;

    ptStack = m_ptLua->GetStack();
    if( ptStack==NULL )
        return false;

    //  Create a metatable that won't be exposed to Lua scripts.
    //  The name must be unique
    //  Stack after the call:
    //  1,-1 | table
    m_ptLua->NewMetaTable( m_tMetaName );

    //  Register all of the properties
    PushProperties( false, false );

    //  Register the callback assigned to __index
    ptStack->PushCFunction( TLuaStruct_Index_CallBack );

    m_ptLua->SetField( -2, "__index" );

    // Create a UserData to store the address of this.
    //  Stack after the call:
    //  2,-1 | userdata
    //  1,-2 | table
    ptUserLuaStruct = ( TLuaStruct** )m_ptLua->NewUserData( sizeof( ptUserLuaStruct ) );
    *ptUserLuaStruct = this;
    m_pvLuaThisPtr = ( void* )ptUserLuaStruct;

    //  Switch the userdata and the table
    //  Stack after the call:
    //  2,-1 | table
    //  1,-2 | userdata
    ptStack->Insert( -2 );

    //  Associate the metatable to the userdata
    //  Stack after the call:
    //  1,-1 | userdata
    m_ptLua->SetMetaTable( -2 );

m_ptLua->SetGlobal( tName.c_str() );

    return true;
}

如果我将元表本身分配给 __index 而不是 C 回调,则脚本可以只读方式访问 FirstName、LastName 和 Age (我想做什么)但我不知道脚本何时尝试访问未知字段,因为我的 C++ 应用程序没有被调用。

当元表是用户数据时,也许 Lua 总是调用 __index 以确保值是最新的,但是在表中定义只读变量(例如函数)可能会很好,否则一直调用 __index 会变慢关闭应用程序。

有人知道如何执行此操作吗? 谢谢。

【问题讨论】:

  • 不要将解决方案添加到问题中,让问题保持原样。相反,请发布您自己问题的答案。
  • 是的,但是 riv 的回答也几乎​​是对的。
  • @A.G.我同意于浩,你应该发布答案。您不必将其标记为已接受的答案,您可以将 Riv 保留为已接受。您的答案可能会被投票,从而让其他新加入该主题的人知道也应该考虑它。由于您的答案与您的问题一致,因此无法区分您的答案与问题的价值。
  • 请注意,使用“静态”与“动态”成员这两个词时要小心:在 C++(您正在使用)和许多其他 OO 语言中,静态表示非堆栈或类范围。从表面上看,您的意思是只读或 const,即它们的值不能改变,或者不能从对象/表中删除成员(在 Lua 中意味着设置为 nil)。
  • @Scholli:你说得对,我会改变我的帖子。

标签: c++ lua


【解决方案1】:

手册很好地解释了元表的工作原理,也许你误读了 - 元表中的任意字段都没有效果,只有 __index 向对象添加了“假”字段。

最好的解决方案是将所有静态值放在__index 表中,然后为__index 表创建另一个 元表,并设置其 @987654325 @ 字段到你的函数。

更新: 正如其他人所提到的,存在一个稍微更紧凑的解决方案:将第一个元表的 __index 设置为自身(并用静态值填充它),并将 its 元表设置为另一个元表,其功能为它的__index。

【讨论】:

  • 我已经尝试创建一个元表 #1,将我的静态值放入其中,创建另一个元表 #2 将我的函数放入它的 __index 然后将元表 #2 放入元表 #1 的 __index 中,最后设置具有元表#1 的用户数据的元表。但这根本不起作用,这是最糟糕的。你能提供一个样品吗?我尝试在 Lua 脚本中执行此操作,它工作正常,但在 C 中不行,我想我做错了什么,但我仍然不知道是什么。 I used the tutorial there
【解决方案2】:

解决方案:

Riv 的解决方案几乎是正确的,但有一点不同,所以我发布了一个新答案。

解决方案是将主元表的 __index 设置为自身,创建第二个元表,其中将其 __index 设置为 C 回调函数,并将这个新元表 设置为主元表的元表而不是它的 __index。

bool TLuaStruct::InternalRegister( std::string tName, bool AddInGC )
{
    TLuaStack *ptStack;
    TLuaStruct **ptUserLuaStruct;

    ptStack = m_ptLua->GetStack();
    if( ptStack==NULL )
        return false;

    //  Create a metatable that won't be exposed to Lua scripts.
    //  The name must be unique
    //  Stack after the call:
    //  1,-1 | table
    m_ptLua->NewMetaTable( m_tMetaName );

    //  Register all of the const members 
    PushProperties( false, false );

    //  Duplicate the metatable
    //  Stack after the call:
    //  2,-1 | table
    //  1,-2 | table
    ptStack->PushValue( -1 );

    //  Set its __index to itself so it can access all of its const members
    //  Stack after the call:
    //  1,-1 | table
    m_ptLua->SetField( -2, "__index" );


    //  Create another metable that will be used for non-const members
    //  Stack after the call:
    //  2,-1 | table
    //  1,-2 | table
    m_ptLua->NewMetaTable( "9999" );

    //  Push the C call back called when a non-const member is read
    //  Stack after the call:
    //  3,-1 | function
    //  2,-2 | table
    //  1,-3 | table
    ptStack->PushCFunction( TLuaStruct_Index_CallBack );

    //  Set the __index of the metable
    //  Stack after the call:
    //  2,-1 | table
    //  1,-2 | table
    m_ptLua->SetField( -2, "__index" );


    //  Set the metatable of the main metatable
    //  Stack after the call:
    //  1,-1 | table
    m_ptLua->SetMetaTable( -2 );

    // Create a UserData to store the address of this.
    //  Stack after the call:
    //  2,-1 | userdata
    //  1,-2 | table
#warning check whether we must create a new pointer each time...
    ptUserLuaStruct = ( TLuaStruct** )m_ptLua->NewUserData( sizeof( ptUserLuaStruct ) );
    *ptUserLuaStruct = this;
    m_pvLuaThisPtr = ( void* )ptUserLuaStruct;

    //  Switch the userdata and the table
    //  Stack after the call:
    //  2,-1 | table
    //  1,-2 | userdata
    ptStack->Insert( -2 );

    //  Set the metatable of the userdata
    //  Stack after the call:
    //  1,-1 | userdata
    m_ptLua->SetMetaTable( -2 );

    //  Publish the userdata object with the name of the module.
    //  Stack after the call:
    //  -- empty --
    m_ptLua->SetGlobal( tName.c_str() );

    return true;
}

【讨论】:

    猜你喜欢
    • 2015-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-04
    • 1970-01-01
    • 2017-02-01
    • 1970-01-01
    相关资源
    最近更新 更多