5

I am having a slight confusion of how lua_next really works. User defines a table:

a={["a1"]=20,["a2"]=30}

I want to print this table with a C++ code:

inline int lua_print(lua_State* L)
{
wxString wxReturnStr=wxEmptyString;
wxString tempString=wxEmptyString;
int nargs = lua_gettop(L);

for (int i=1; i <= nargs; i++)
{
    int type = lua_type(L, i);
    switch (type)
    {

    case LUA_TNIL:
        break;

    case LUA_TBOOLEAN:
        tempString<<(lua_toboolean(L, i) ? "true" : "false");
        break;

    case LUA_TNUMBER:
        tempString<<lua_tonumber(L, i);
        break;

      case LUA_TSTRING:
          tempString<<lua_tostring(L, i);
        break;
      case LUA_TTABLE:
      {
          lua_pushnil(L);
          while(lua_next(L,-2))
          {
                const char* key=lua_tostring(L,-2);
                double val=lua_tonumber(L,-1);
                lua_pop(L,1);
                tempString<<key<<"="<<val<<"\t";
          }

          break;
      }

      default:
          tempString<<lua_typename(L, type);
        break;
    }

    wxReturnStr=wxReturnStr+tempString+"\n";
    tempString=wxEmptyString;

}

lua_pop(L,nargs);

This code works very well when I call from Lua:

print(a) -- Works well

However, imagine I have a table in Lua as:

b={["b1"]=10, ["b2"]=15}

if I call the code as:

print(a,b) -- Twice prints only contents of b 

My understanding with how lua_next work is in the following figure:enter image description here [Edition #1]

Where is the bug?

macroland
  • 973
  • 9
  • 27

2 Answers2

3

The bug is in lua_next(L, -2) line, because -2 refers to stack top minus one, which happens here to be the last argument to print.

Use lua_next(L, i) instead.

Upd: Lua stack indexes are subject to float when moving code around at development stage, so the general advice is to pin indexes with sml int t = lua_gettop(L) just after getting/pushing/considering values and to use that t instead of -n (though this specific case appears to be sort of a keystroke bug.)

user3125367
  • 2,920
  • 1
  • 17
  • 17
  • Your suggestion `lua_next(L,i)` works. However, I am now confused... When I use `lua_next(L,1)` for i=1, this refers to the bottom of the stack as I understand. However, when I say `lua_pop(L,1)` this pops from the top of the stack. I am not sure how it is working... – macroland Jun 19 '15 at 21:04
  • @macroland Some functions take *stack indexes* (1..top / -top..-1) as arguments, others take *count*. lua_next(), lua_tostring(), lua_getfield() take stack indexes. lua_pop(), lua_concat() take *the number of values* to remove/concatenate at top of the stack. You just can't pop from the middle of the stack, that's why lua_remove() exists. If in doubt, consult specific function's documentation. – user3125367 Jun 19 '15 at 22:19
  • Thanks! When we say `lua_next(L,i)` for i=1, then this refers to the bottom of the stack, the element with absolute index 1. When we pop from the stack, however, the only way we can pop is from the top for sure. Now if I have two tables on the stack (table a at index 1, table b at index 2), when we run `lua_next(L,1)`, the code is referring to table a. This "takes" values from table a, and pushes on the stack. Now using idx=-1 for value and idx=-2 for key makes sense. My understanding is then lua_next internally does something to remove table a (with index 1) from the stack. – macroland Jun 19 '15 at 22:48
  • My understanding is: https://drive.google.com/file/d/0B6oCKmT1bPAyYkRveFZGZnZnT2M/view – macroland Jun 19 '15 at 23:55
  • lua_next() does not modify or remove the table. It pops the key and pushes key-value pair from hash table bucket next to that key (or nothing if key was last). Nil is considered as "smallest" key, thus you start with nil to traverse all table kv-pairs. Popping value on each iteration leaves last key on top for next lua_next() call, effectively and naturally looping through all keys. Please read the documentation, there is nothing more or less behavior than described. Your code moves to `b` because of incrementing of `i`, not that of removing value at index 1. – user3125367 Jun 20 '15 at 06:27
  • Image at last link is all right, btw. (The only note is that stack is usually drawn top-down, i.e. growing down.) – user3125367 Jun 20 '15 at 06:54
1

You forgot the lua_pop after processing the table.

 lua_pushnil(L);
      while(lua_next(L,-2))
      {
            const char* key=lua_tostring(L,-2);
            double val=lua_tonumber(L,-1);
            lua_pop(L,1);
            tempString<<key<<"="<<val<<"\t";
      }
 lua_pop(L, 1); // THE FIX, pops the nil on the stack used to process the table

This means, that extra nil is left on the stack, so in the second iteration, the

case LUA_TNIL:
   break;

just prints nothing.

About your graphics representation of the stack. The command under each image represents the state after the command was called. So the very last image is missing the [Key = a2] item on the stack.

kovarex
  • 1,766
  • 18
  • 34
  • OPs while loop doesn't leave nil on the stack, because lua_next consumes initial nil and pushes nothing when returns 0. – user3125367 Jun 19 '15 at 14:22
  • Your suggestion to use `lua_pop(L,1)` at the end of the block, which I actually have tried before posting here, does not work. For example, for `print(a,b)` it only prints the first argument and the second argument is popped before it can be printed. – macroland Jun 19 '15 at 21:06