You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++向Lua传递嵌套结构体时触发“调用nil值”错误排查

问题根源

错误attempt to call a nil value的核心原因是:调用lua_pcall时,Lua栈上没有可执行的函数。

具体代码流程问题:

  1. 执行lua_getglobal(L, func_name.c_str())后,WaterBall函数被压入栈顶。
  2. 验证是函数后,将lambda存入SkillMap,但lambda并未保存该函数的引用。
  3. 随后执行lua_pop(L, 1),把栈上的WaterBall函数弹出,此时栈中已无该函数。
  4. 调用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++代码专用的全局存储表,可避免栈操作导致的函数丢失,性能更优:

  1. 获取函数后存入注册表,记录引用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;
};
  1. 程序结束前释放注册表引用,避免内存泄漏:
// 在lua_close(L)之前添加
luaL_unref(L, LUA_REGISTRYINDEX, func_ref);

该方式仅需一次存储操作,后续直接通过引用获取函数,适合高频调用场景。

额外注意
  • 确认stack_struct执行后栈顶是结构体对应的table,保证lua_pcall的参数数量(1个)正确。
  • 若使用sol2库,可直接通过sol::state简化结构体绑定与函数调用,减少手动栈操作的出错概率。

内容的提问来源于stack exchange,提问作者richardkiwilee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:49:53