Discussion:
New Lua C API Tutorial
Jeremy Jurksztowicz
2011-10-31 00:12:53 UTC
Permalink
Hi,

A year ago I had a hard time finding a Lua C API tutorial that showed a
good way to export C++ classes. I refused to use a heavy binding library
and learned how to do it myself, with the Lua reference manual and a lot of
trial and error.

I've written a Lua C API tutorial aimed at indie game developers. The main
goal is to show you how you can export C++ classes for use in a Lua script.
It's pragmatic. The tutorial is unedited and sure to contain a bug or two,
I haven't even run the example code (I know, so irresponsible), but am just
simplifying real code from my own game engine. If someone is bored, perhaps
they can take a quick gander at the tutorial to make sure I am not making
some gross error and perverting innocent young'uns minds with bad practices.

http://forums.tigsource.com/index.php?topic=22524.0

For the record, the techniques I am using are working great in my iOS game
and I have good performance, stable memory usage, and no crashes (so far),
so I don't think I'm totally off base, but I've only been using Lua on this
one project for about a year, so maybe I'm missing something.

Thanks.
Patrick Rapin
2011-10-31 10:48:41 UTC
Permalink
In the "Functions" chapter, you give the following example :

int move_player (lua_Stack * ls)
{
const int xoffset = lua_tointeger(ls, 1);
const int yoffset = lua_tointeger(ls, 2);
lua_settop(ls, 0); // <-- I am talking about this line

const Point p = player->move(xoffset, yoffset);

lua_pushinteger(ls, p.x);
lua_pushinteger(ls, p.y);
return 2;
}

You say it is good practice to maintain a nice clean stack and
recommend to use lua_settop(0). Well certainly not in such a
situation.
Lua API is designed so that it is useless to pop arguments from the
stack: the return value already tells how many results there are
inside the stack.
The interpreter will automatically perform the equivalent of
lua_settop(0) after the call.
Worse: in some situations, it can yield to weird bugs.
If an argument is retrieved with lua_tostring, flushing the stack
could make the garbage collector to delete the content of the string,
yielding the const char* argument pointing to invalid memory!
Tom N Harris
2011-11-01 00:30:22 UTC
Permalink
Post by Patrick Rapin
int move_player (lua_Stack * ls)
{
const int xoffset = lua_tointeger(ls, 1);
const int yoffset = lua_tointeger(ls, 2);
lua_settop(ls, 0); //<-- I am talking about this line
... snip ...
Post by Patrick Rapin
You say it is good practice to maintain a nice clean stack and
recommend to use lua_settop(0). Well certainly not in such a
situation.
Not to mention that the function is assuming the stack has 2 valid
values on it. I think the properly defensive function would begin:

lua_settop(ls, 2); // expect two arguments
const int xoffset = lua_tointeger(ls, 1);
const int yoffset = lua_tointeger(ls, 2);

As you say, Lua will clean up the stack afterwards, so the function need
not pop the consumed values. And of course, if the arguments are
mandatory then you should also be checking for nil.
--
- tom
***@whoopdedo.org
Daurnimator
2011-11-01 00:35:50 UTC
Permalink
Not to mention that the function is assuming the stack has 2 valid values on
   lua_settop(ls, 2);       // expect two arguments
   const int xoffset = lua_tointeger(ls, 1);
   const int yoffset = lua_tointeger(ls, 2);
As you say, Lua will clean up the stack afterwards, so the function need not
pop the consumed values. And of course, if the arguments are mandatory then
you should also be checking for nil.
That is also rather incorrect usage
You ensure the arguments are there by using the luaL_check* functions.
the code above should be:

const int xoffset = luaL_checkint ( L , 1 );
const int yoffset = luaL_checkint ( L , 2 );

const Point p = player->move(xoffset, yoffset);

lua_pushinteger ( L , p.x );
lua_pushinteger ( L , p.y );
return 2;

Loading...