Lua 5.2 – 对象中的C对象(使用lua_lightuserdata)
编辑:[解答2中的解决方案]
我是LUA的新手,我很难尝试做我想做的事.我有一个看起来像这样的C对象: C对象定义 struct TLimit { bool enabled; double value; TLimit() : enabled(false),value(0.0) {} ~TLimit() {} }; class TMeaurement { public: TMeasurement() : meas(0.0) {} ~TMeasurement() {} TLimit min; TLimit max; double meas; }; 我希望能够在LUA中以下列形式访问TMeasurement类型的对象: LUA希望使用 -- objmeas is an instance of TMeasurement objmeas.min.enabled = true print(objmeas.min.value); …等等 另一件事,我不希望LUA为TMeasurement类型的对象实例分配内存.这将在我的C代码中完成.我尝试过很多不同的东西,都不成功.我现在将发布最后一次尝试. 在我的C代码中,我定义了以下内容: TLimit – 获取将映射到__index的函数 #define LUA_MEAS_LIMIT "itse.measurement.limit" extern int llim_get(lua_State* L) { TLimit* lim = (TLimit*)lua_chekuserdata(L,1,LUA_MEAS_LIMIT); std::string key = std::string(luaL_checkstring(L,2)); //-- this is only to check what is going on std::cout << "lim.get: " << key << std::endl; if(key.find("enabled") == 0) lua_pushboolean(L,lim->enabled); else if(key.find("value") == 0) lua_pushnumber(L,lim->value); else return 0; //-- should return some sort of error,but let me get this working first return 1; } TLimit – 设置将映射到__newindex的函数 extern int llim_set(lua_State* L) { TLimit* lim = (TLimit*)lua_chekuserdata(L,2)); //-- this is only to check what is going on std::cout << "limit.set: " << key << " <-" << std::endl; if(key.find("enabled") == 0) lim->enabled = lua_toboolean(L,3); else if(key.find("value") == 0) lim->value = lua_tonumber(L,3); return 0; } 现在,TMeasurement类还有一个函数. (我不会在这个例子中提供成员“meas”的set函数). TMeasurement – 获取__index的功能 #define LUA_MEASUREMENT "itse.measurement" extern int lmeas_get(lua_State* L) { TMeasurement* test = (TMeasurement*)lua_checkuserdata(L,LUA_MEASUREMENT); std::string key = std::string(luaL_checkstring(L,2)); //-- this is only to check what is going on std::cout << "meas." << key << " ->" << std::endl; if(key.find("meas") == 0) lua_pushinteger(L,test->meas); else if(key.find("min") == 0) { lua_pushlightuserdata(L,&test->min); luaL_getmetatable(L,LUA_MEAS_LIMIT); lua_setmetatable(L,-2); } else if(key.find("max") == 0) { lua_pushlightuserdata(L,&test->max); luaL_getmetatable(L,-2); } else return 0; //-- should notify of some error... when I make it work return 1; } 现在,代码中为这两个对象创建元数据的部分: C – 发布元数据 (更别关注nsLUA :: safeFunction< ...>位,它只是一个模板函数,它将在“安全模式”下执行<>中的函数…它将弹出一个MessaegBox时遇到错误) static const luaL_Reg lmeas_limit_f[] = { { NULL,NULL} }; static const luaL_Reg lmeas_limit[] = { { "__index",nsLUA::safeFunction<llim_get> },{ "__newindex",nsLUA::safeFunction<lllim_set> },{ NULL,NULL } }; //----------------------------------------------------------------------------- static const luaL_Reg lmeas_f[] = { { NULL,NULL} }; static const luaL_Reg lmeas[] = { { "__index",nsLUA::safeFunction<lmeas_get> },NULL } }; //----------------------------------------------------------------------------- int luaopen_meas(lua_State* L) { //-- Create Measurement Limit Table luaL_newmetatable(L,LUA_MEAS_LIMIT); luaL_setfuncs(L,lmeas_limit,0); luaL_newlib(L,lmeas_limit_f); //-- Create Measurement Table luaL_newmetatable(L,LUA_MEASUREMENT); luaL_setfuncs(L,lmeas,lmeas_f); return 1; } 最后,我在C中的主要功能,初始化LUA,创建对象TMeasurement的实例,将其作为全局传递给LUA并执行lua脚本.大多数此功能都包含在另一个名为LEngine的类中: C – 主要功能 int main(int argc,char* argv[]) { if(argc < 2) return show_help(); nsLUA::LEngine eng; eng.runScript(std::string(argv[1])); return 0; } //----------------------------------------------------------------------------- int LEngine::runScript(std::string scrName) { //-- This initialices LUA engine,openlibs,etc if not already done. It also // registers whatever library I tell it so by calling appropriate "luaL_requiref" luaInit(); if(m_lua) //-- m_lua is the lua_State*,member of LEngine,and initialized in luaInit() { LMeasurement measurement; measurement.value = 4.5; //-- for testing purposes lua_pushlightuserdata(m_lua,&tst); luaL_getmetatable(m_lua,LUA_MEASUREMENT); lua_setmetatable(m_lua,-2); lua_setglobal(m_lua,"step"); if(luaL_loadfile(m_lua,scrName.c_str()) || lua_pcall(m_lua,0)) processLuaError(); //-- Pops-up a messagebox with the error } return 0; } 现在,最后问题.当我执行任何lua脚本时,我可以访问步骤没有问题,但我只能在第一次访问“min”或“max”内的memebr …任何后续访问都会出错. LUA – 示例一 print(step.meas); -- Ok print(step.min.enabled); -- Ok print(step.min.enabled); -- Error: attempt to index field 'min' (a nil value) 此脚本生成的输出是: first script line: print(step.meas); meas.meas -> this comes from lmeas_get function 4.5 this is the actual print from lua sentence second script line: print(step.min.enabled) meas.min -> accessing step.min,call to function lmeas_get limit.get: enabled -> accessing min.enabled,call to function llim_get false actual print from script sentence third script line: print(step.min.enabled) limit.get: min -> accessing min from limit object,call to llim_get ??????? 所以.在第一次访问字段’min'(或者就此而言’max’)之后,任何后续尝试访问它将返回“尝试访问索引…”错误.无论我是首先访问__index(本地e = step.min.enabled)函数还是__newindex函数(step.min.enabled = true)都没关系. 我第一次访问对象步骤的最小元素时似乎搞乱了LUA堆栈.它以某种方式“替换”从LUA_MEASUREMENT metatable到LUA_MEAS_LIMIT的“指向步骤的指针”……我根本不知道为什么. 请帮忙……我搞砸的是什么? 谢谢你,对不起这篇长篇文章…我只是不知道如何缩短它. 解决方法
正如评论中已经提到的,所有lightuserdata共享一个metatable(参见
here),因此所有lightuserdata值始终处理完全相同.如果更改了一个lightuserdata的metatable,那么它将针对所有这些更改.这就是你的代码中发生的事情:
>在LEngine :: runScript中,您使所有lightuserdata都像TMeasurement对象一样.这适用于全局变量步骤中的值. Lightuserdata根本不适合这项工作.参见例如this discussion适用于可以使用lightuserdata的情况.对于其他一切,您需要完整的用户数据.与lightuserdata相比,这将分配一些额外的内存(对不起,无法帮助),但你可以做一些缓存,以避免产生太多的临时数据. 因此,对于您的步长值,您使用包含指向TMeasurement对象的指针的完整用户数据.您还将新表设置为uservalue(参见 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |