即使表的字段已知,__index也总是会被调用的(Lua/C++)。
2013-12-5 9:10:25
收藏:0
阅读:145
评论:2
我想在 Lua 中声明全局元表,并由 C++ 应用程序注册。
我定义了元表和一些字段的 __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;
// 创建一个不会暴露给 Lua 脚本的元表。
// 名称必须唯一
// 调用后的堆栈:
// 1,-1 | 表
m_ptLua->NewMetaTable( m_tMetaName );
// 注册所有属性
PushProperties( false, false );
// 注册分配给 __index 的回调
ptStack->PushCFunction( TLuaStruct_Index_CallBack );
m_ptLua->SetField( -2, "__index" );
// 创建 UserData 以存储此地址。
// 调用后的堆栈:
// 2,-1 | userdata
// 1,-2 | 表
ptUserLuaStruct = ( TLuaStruct** )m_ptLua->NewUserData( sizeof( ptUserLuaStruct ) );
*ptUserLuaStruct = this;
m_pvLuaThisPtr = ( void* )ptUserLuaStruct;
// 切换 userdata 和表
// 调用后的堆栈:
// 2,-1 | 表
// 1,-2 | userdata
ptStack->Insert( -2 );
// 将元表与 userdata 关联
// 调用后的堆栈:
// 1,-1 | userdata
m_ptLua->SetMetaTable( -2 );
m_ptLua->SetGlobal( tName.c_str() );
return true;
}
如果我将元表本身分配给 __index 而不是一个 C 回调,那么脚本可以以只读方式访问 FirstName_、_LastName 和 _Age_(我想做的事情),但我无法知道脚本尝试访问未知字段的时间,因为我的 C++ 应用程序没有被调用。
也许 Lua 总是在元表是 userdata 时调用 __index,因此它确保值是最新的,但是如果定义只读变量(例如表中的函数),否则一直调用 __index 可以减慢应用程序的速度。
有没有人知道如何执行此操作? 谢谢。
点赞
用户1479549
解决方案:
Riv的解决方案几乎正确,但存在一些小差异,因此我发布了一个新答案。
解决方案是将主metatable的__index设置为自身,创建第二个metatable,其中__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;
// 创建一个不会暴露给Lua脚本的metatable。
// 名称必须唯一
// 调用后的堆栈:
// 1,-1 | table
m_ptLua->NewMetaTable(m_tMetaName);
// 注册所有常量成员
PushProperties(false, false);
// 复制metatable
// 调用后的堆栈:
// 2,-1 | table
// 1,-2 | table
ptStack->PushValue(-1);
// 将其__index设置为自身,以便访问其所有常量成员
// 调用后的堆栈:
// 1,-1 | table
m_ptLua->SetField(-2, "__index");
// 创建另一个metatable,将用于非常量成员
// 调用后的堆栈:
// 2,-1 | table
// 1,-2 | table
m_ptLua->NewMetaTable("9999");
// 推送在读取非常量成员时调用的C回调
// 调用后的堆栈:
// 3,-1 |函数
// 2,-2 | table
// 1,-3 | table
ptStack->PushCFunction(TLuaStruct_Index_CallBack);
// 设置metatable的__index
// 调用后的堆栈:
// 2,-1 | table
// 1,-2 | table
m_ptLua->SetField(-2, "__index");
// 设置主metatable的metatable
// 调用后的堆栈:
// 1,-1 | table
m_ptLua->SetMetaTable(-2);
// 创建UserData来存储此处的地址。
// 调用后的堆栈:
// 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;
// 切换userdata和table
// 调用后的堆栈:
// 2,-1 | table
// 1,-2 | userdata
ptStack->Insert(-2);
// 设置userdata的metatable
// 调用后的堆栈:
// 1,-1 | userdata
m_ptLua->SetMetaTable(-2);
// 使用模块名称发布userdata对象。
// 调用后的堆栈:
// --空--
m_ptLua->SetGlobal(tName.c_str());
return true;
}
2013-12-05 09:12:26
评论区的留言会收到邮件通知哦~
推荐文章
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
该手册很好地解释了元表的工作原理,也许您误读了 - 元表中的任意字段没有影响,只有
__index将“假”字段添加到对象中。最好的解决方案是将所有静态值放在
__index表中,然后为__index表创建_另一个_元表,并将_其___index字段设置为您的函数。更新: 正如其他人提到的那样,存在一个稍微更紧凑的解决方案:将第一个元表的
__index设置为它本身(并填充静态值),然后将_其_元表设置为另一个元表,该元表具有您的函数作为其__index。