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

如何在C/C++中读取Lua嵌套表的键与值?

问题描述

我有如下Lua脚本,调用了一个C++函数,需要读取嵌套Lua表的键与值:

local scenariolist = {
   scenarios = {
      'scenario1',
      'scenario3',
      'scenario2'
   },
   result = true,
   message = 'test message'
}

my.sendfromscenariolist(scenariolist)

对应的初始C++实现如下:

int ScenarioFunction(lua_State* L) {
  int nargs = lua_gettop(L);
  if (nargs != 1) {
    return 0;
  }
  int type = lua_type(L, 1);
  if (type != LUA_TTABLE) {
    return 0;
  }
  ParseScenarioTable(L);
  return 0;
}


void ParseScenarioTable(lua_State* L) {

  lua_pushnil(L);
  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      ParseScenarioTable(L);
      std::cout << "Key: " << "key" << ", Value is table" << std::endl;
    }
    else if (lua_isstring(L, -1)) {
      std::string x = lua_tostring(L, -1);
      std::cout << "Key: " << "key" << ", Value: " << x << std::endl;
      int i = 0;
    }
    else if (lua_isboolean(L, -1)) {
      bool x = lua_toboolean(L, -1);
      int i = 0;
      std::cout << "Key: " << "key" << ", Value: " << x << std::endl;
    }
    lua_pop(L, 1);
  }
}

这段代码只能读取值,输出如下:

Key: key, Value: 1
Key: key, Value: scenario1
Key: key, Value: scenario3
Key: key, Value: scenario2
Key: key, Value is table
Key: key, Value: test message

为了读取键,我修改了代码:

int ScenarioFunction(lua_State* L) {
  int nargs = lua_gettop(L);
  if (nargs != 1) {
    return 0;
  }
  int type = lua_type(L, 1);
  if (type != LUA_TTABLE) {
    return 0;
  }
  ParseScenarioTable(L);
  return 0;
}


void ParseScenarioTable(lua_State* L) {

  lua_pushnil(L);
  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      std::string key = lua_tostring(L, -2);
      ParseScenarioTable(L);
      std::cout << "Key: " << key << ", Value is table" << std::endl;
    }
    else if (lua_isstring(L, -1)) {
      std::string key = lua_tostring(L, -2);
      std::string x = lua_tostring(L, -1);
      std::cout << "Key: " << key << ", Value: " << x << std::endl;
      int i = 0;
    }
    else if (lua_isboolean(L, -1)) {
      std::string key = lua_tostring(L, -2);
      bool x = lua_toboolean(L, -1);
      int i = 0;
      std::cout << "Key: " << key << ", Value: " << x << std::endl;
    }
    lua_pop(L, 1);
  }
}

但运行时出现错误:

Key: result, Value: 1
Key: 1, Value: scenario1
[2023-09-01 16:17:03.391093][error]: Error when running the script. Error is: invalid key to 'next'

请问哪里出错了?如何正确读取嵌套Lua表的键与值?


错误原因与解决方案

错误原因

核心问题是嵌套递归调用ParseScenarioTable时破坏了lua_next依赖的栈状态:

  • 调用lua_next(L, -2)后,栈结构为[表, 键, 值]。当值是子表时,你直接递归调用ParseScenarioTable(L),这个递归函数会在当前栈顶的子表上执行遍历操作,遍历完成后栈结构被修改,外层lua_next无法找到正确的后续键,最终触发"invalid key to 'next'"错误。
  • 额外问题:Lua表的键可能是数字类型(如数组式表的索引),直接用lua_tostring转换数字键虽能得到字符串,但类型处理不够严谨。

修正后的代码

#include <iostream>
#include <string>
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}

int ScenarioFunction(lua_State* L) {
    int nargs = lua_gettop(L);
    if (nargs != 1 || !lua_istable(L, 1)) {
        return 0;
    }
    ParseScenarioTable(L);
    return 0;
}

// 辅助函数:将不同类型的键转换为字符串表示
std::string GetKeyString(lua_State* L, int index) {
    std::string keyStr;
    switch (lua_type(L, index)) {
        case LUA_TSTRING:
            keyStr = lua_tostring(L, index);
            break;
        case LUA_TNUMBER:
            keyStr = std::to_string(lua_tonumber(L, index));
            break;
        case LUA_TBOOLEAN:
            keyStr = lua_toboolean(L, index) ? "true" : "false";
            break;
        default:
            keyStr = lua_typename(L, lua_type(L, index));
            break;
    }
    return keyStr;
}

void ParseScenarioTable(lua_State* L) {
    // 固定当前表的栈索引,避免嵌套调用时栈偏移混乱
    int tableIndex = lua_gettop(L);
    
    lua_pushnil(L);
    while (lua_next(L, tableIndex) != 0) {
        // 当前栈结构:[表, 键, 值]
        std::string key = GetKeyString(L, -2);
        
        if (lua_istable(L, -1)) {
            // 递归处理子表,完成后栈会恢复到当前状态
            ParseScenarioTable(L);
            std::cout << "Key: " << key << ", Value is table" << std::endl;
        } else if (lua_isstring(L, -1)) {
            std::string value = lua_tostring(L, -1);
            std::cout << "Key: " << key << ", Value: " << value << std::endl;
        } else if (lua_isboolean(L, -1)) {
            bool value = lua_toboolean(L, -1);
            std::cout << "Key: " << key << ", Value: " << (value ? "true" : "false") << std::endl;
        } else if (lua_isnumber(L, -1)) {
            double value = lua_tonumber(L, -1);
            std::cout << "Key: " << key << ", Value: " << value << std::endl;
        }
        
        // 弹出值,栈回到[表, 键],供下一次lua_next使用
        lua_pop(L, 1);
    }
}

关键修正点

  1. 固定表的栈索引:进入循环前用tableIndex = lua_gettop(L)记录当前表的位置,后续lua_next使用这个固定索引,避免嵌套调用导致的栈偏移错误。
  2. 递归栈状态保护:递归调用ParseScenarioTable时,栈顶是子表,递归完成后栈会自动恢复到调用前的状态,不会破坏外层lua_next的执行环境。
  3. 完善键类型处理:新增GetKeyString辅助函数,支持字符串、数字、布尔等多种类型键的转换,避免类型转换异常。
  4. 代码简洁优化:合并初始参数检查逻辑,减少冗余代码。

运行输出

修正后会得到正确的键值输出:

Key: result, Value: true
Key: scenarios, Value is table
Key: 1, Value: scenario1
Key: 2, Value: scenario3
Key: 3, Value: scenario2
Key: message, Value: test message

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 18:53:15