Understanding the lua stack (pt. 2): Viewing the stack

Preface

In pt. 1 I explained a bit about the inner workings of the stack.  Now, let’s take a look at the actual stack, from inside a program (ooooooooh!).

(NOTE: I’m using lua version 5.2.2 which has some syntactical differences from its predecessors.  Please be aware of that if you’re going to cut and paste code from here.)

Let’s create a lua_State

Here is the most basic version of creating a lua state.

#include <stdio.h>

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

#pragma comment(lib, "lua.lib")

int main()
{
lua_State *L;
L = luaL_newstate();

return 0;
}

So, now we have an active lua_State. Obviously our program doesn’t really do anything, but we’ve got all we need to run a few small experiments.

A simple way to view the stack.

Here, you’ll find a function called stackDump.  I’ve taken that function and changed it a bit, to represent the stack in a way that makes a bit more sense to me personally.  First, I iterate through the stack in the reverse order from the original function.  To me, having the most recent addition on the top makes more sense as the stack is LIFO.  Secondly, I’ve added a bit of code that keeps track of the negative representation as well as the positive one.  I discussed this briefly in my last post, but I’ll talk more about the importance of that later.  Last, I renamed it from stackDump to stackTrace.  I don’t know why really, I just liked it better.

Here is my code:


void stackTrace(lua_State *L)
{
int i;
int top = lua_gettop(L);
printf("---- Begin Stack ----\n");
printf("Stack size: %i\n\n", top);
for (i = top; i >= 1; i--)
{
int t = lua_type(L, i);
switch (t)
{
case LUA_TSTRING:
printf("%i -- (%i) ---- `%s'", i, i - (top + 1), lua_tostring(L, i));
break;

case LUA_TBOOLEAN:
printf("%i -- (%i) ---- %s", i, i - (top + 1), lua_toboolean(L, i) ? "true" : "false");
break;

case LUA_TNUMBER:
printf("%i -- (%i) ---- %g", i, i - (top + 1), lua_tonumber(L, i));
break;

default:
printf("%i -- (%i) ---- %s", i, i - (top + 1), lua_typename(L, t));
break;
}
printf("\n");
}
printf("---- End Stack ----\n");
printf("\n");
}

So now we have a function to visually display what is in the stack.  Let’s discuss for a second.  The first interesting thing that we do is use lua_gettop to find the stack element with the largest positive representation (the most recent addition).  If there are 5 elements in the stack, lua_gettop will return a value of 5.  Once we have the stack size, we now iterate through the stack using i, but we do so in the reverse order.  We’ll start at 5 and go down from there, rather than starting at 1 and going up.

Remember how I mentioned in the last blog that the lua stack doesn’t really care what type of value you put in it?  While it is true that it doesn’t care what type of data you put in it, it still remembers what type of data you put in it.  In our loop, we check to see what data type each element is, and then we call the proper lua_to function on it.  Each lua_to function converts the data to a C++ data type.

Let’s see what it does.

Insert these lines into the main function, after you create the lua_State, but before return 0.


stackTrace(L);

char c;
c = getchar();

Your result should be a stack with 0 elements in it (since we haven’t done anything to the stack yet).  We know that stackTrace is indeed working though, so let’s actually add something to the stack.

Under your first stackTrace call, add these lines:


lua_pushnumber(L, 17);
lua_pushstring(L, "hi there");
lua_pushnil(L);
lua_pushboolean(L, 1);

stackTrace(L);

What we have now is 2 stackTrace calls.  The first one shows us the empty stack, the second shows us a stack populated with 4 elements.  The 4 lines we added above the second stackTrace call are functions to “push” different data types onto the stack.  As you look at the output of the program compared to your code, notice that the very first item in the output is the value “true”.  This represents the boolean value pushed from lua_pushboolean(L,1), which is the last thing we pushed to the stack.  So again, the last thing we put onto the stack (the most recent thing) goes on top.

Nice.  What next?

As of now we can view the stack, which is incredibly useful for debugging.  Keep in mind that obviously you don’t have to print this to the screen.  You could potentially call stackTrace every single time you manipulate the stack and put the results into a log file.  From there, it would be relatively easy to retrace your steps through the stack, if certain pieces of data are not interchanging between the programming language and lua.

Next time, I’ll go more into manipulating the stack, including removing, replacing, and inserting.  For now you can clearly see how to push data onto the stack, which is obviously integral for utilizing lua in any practical manner.

2 thoughts on “Understanding the lua stack (pt. 2): Viewing the stack

Leave a comment