Search code examples
c++lualua-tablelua-c++-connection

Read both key and values of a nested lua script in C/C++


I've the following lua script, in which I use a C++ function that needs to read both keys and values of a nested table:

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

my.sendfromscenariolist(scenariolist)

This is my C++ function that's executed when calling sendfromscenariolist:

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);
  }
}

This function read only values and it works, when I run it in the console I obtain:

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

The problem is that I'm not able to read also keys of nested table elements. I've changed my code with this:

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);
  }
}

But if I try to read the keys, the program will broke and I obtain an error: This is the output of my program:

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'

where invalid key to 'next' is the error string of lua.

What I'm doing wrong? How can I read both keys and values?


Solution

  • The problem is here:

    std::string key = lua_tostring(L, -2);
    

    The lua_tostring modifies its argument: it replaces number 1 with string "1" at the API stack index -2, so following lua_next is unable to continue traversing the table as it receives non-existing key "1" instead of existing 1.
    This behavior is described in the manual.

    Solution:
    Create additional temporary stack slot for the value to be modified by lua_tostring.
    Replace

    std::string key = lua_tostring(L, -2);
    

    with

    lua_pushvalue(L, -2);
    std::string key = lua_tostring(L, -1);
    lua_pop(L, 1);