C++向Lua传递嵌套结构体时触发“调用nil值”错误排查
问题根源
错误attempt to call a nil value的核心原因是:调用lua_pcall时,Lua栈上没有可执行的函数。
具体代码流程问题:
- 执行
lua_getglobal(L, func_name.c_str())后,WaterBall函数被压入栈顶。 - 验证是函数后,将lambda存入
SkillMap,但lambda并未保存该函数的引用。 - 随后执行
lua_pop(L, 1),把栈上的WaterBall函数弹出,此时栈中已无该函数。 - 调用lambda时,仅将结构体table压入栈,直接执行
lua_pcall,此时栈顶是table而非函数,触发调用nil的错误。
修复方案
提供两种可行修复方式,按需选择:
方案1:每次调用前重新获取全局函数
在lambda内部,每次调用前通过lua_getglobal重新获取函数,确保栈上存在可调用对象:
SkillMap[id] = [L, func_name](ComplexData* info) -> double { lua_getglobal(L, func_name.c_str()); // 每次调用前重新获取函数 stack_struct(L, *info); if (lua_pcall(L, 1, 1, 0) != LUA_OK) { std::cerr << "Error calling Lua function: " << lua_tostring(L, -1) << std::endl; lua_pop(L, 1); return 0.0; } double result = 0.0; if (lua_isnumber(L, -1)) { result = lua_tonumber(L, -1); } else { std::cerr << "Lua function did not return a number!" << std::endl; } lua_pop(L, 1); return result; };
该方式实现简单,但每次调用都要查询全局表,适合调用频率较低的场景。
方案2:将函数存入Lua注册表保存
Lua注册表是C++代码专用的全局存储表,可避免栈操作导致的函数丢失,性能更优:
- 获取函数后存入注册表,记录引用ID:
// 在lua_isfunction判断之后 int func_ref = luaL_ref(L, LUA_REGISTRYINDEX); // 将函数存入注册表,返回引用ID SkillMap[id] = [L, func_ref](ComplexData* info) -> double { lua_rawgeti(L, LUA_REGISTRYINDEX, func_ref); // 从注册表取出函数压入栈 stack_struct(L, *info); if (lua_pcall(L, 1, 1, 0) != LUA_OK) { std::cerr << "Error calling Lua function: " << lua_tostring(L, -1) << std::endl; lua_pop(L, 1); return 0.0; } double result = 0.0; if (lua_isnumber(L, -1)) { result = lua_tonumber(L, -1); } else { std::cerr << "Lua function did not return a number!" << std::endl; } lua_pop(L, 1); return result; };
- 程序结束前释放注册表引用,避免内存泄漏:
// 在lua_close(L)之前添加 luaL_unref(L, LUA_REGISTRYINDEX, func_ref);
该方式仅需一次存储操作,后续直接通过引用获取函数,适合高频调用场景。
额外注意
- 确认
stack_struct执行后栈顶是结构体对应的table,保证lua_pcall的参数数量(1个)正确。 - 若使用sol2库,可直接通过
sol::state简化结构体绑定与函数调用,减少手动栈操作的出错概率。
内容的提问来源于stack exchange,提问作者richardkiwilee
相关产品推荐
相关产品推荐

