我想在Lua中声明由C ++应用程序注册的全局元表。
我定义了metatable和某些字段的__index元方法,但是当lua脚本访问已知字段时,总是在我的C ++应用程序中调用__index。
例如,我想注册一个名为 User 的全局表,其中包含三个字段: FirstName , LastName 和 Age
以下是我如何注册表格。该表封装在一个名为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;
}
如果我将metatable本身分配给__index而不是C回调,脚本可以在readonly中访问 FirstName , LastName 和 Age (我想做什么)但我不知道脚本何时尝试访问未知字段导致我的C ++应用程序未被调用。
当metatable是userdata时,也许Lua总是调用__index,所以它确保值是最新的,但它可以很好地定义只读变量,如表中的函数,否则一直调用__index会慢在应用程序中。
有人知道如何执行此操作吗? 感谢。
答案 0 :(得分:4)
本手册很好地解释了元表的工作原理,也许你误解了它 - 元表中的任意字段都没有效果,只有__index
向对象添加“假”字段。
最好的解决方案是将所有静态值放在__index
表中,然后为__index
表创建另一个元表,并设置 it __index
字段到您的函数。
<强>更新强>
正如其他人所提到的,存在一个稍微紧凑的解决方案:将第一个metatable的__index
设置为自身(并用静态值填充它),并将其 metatable设置为另一个metatable,它有你的作为__index
。
答案 1 :(得分:3)
<强> SOLUTION:强>
Riv的解决方案几乎是正确的但是有一点不同,所以我发布了一个新的答案。
解决方法是将主要元表的__index设置为自身,创建第二个元表,其中__index设置为C回调函数,并将此新的metatable 设置为主元表的metatable 而不是它的__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;
}