New Lua C API Tutorial

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

New Lua C API Tutorial

Jeremy Jurksztowicz
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.
Reply | Threaded
Open this post in threaded view
|

Re: New Lua C API Tutorial

Patrick Rapin
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!

Reply | Threaded
Open this post in threaded view
|

Re: New Lua C API Tutorial

Tom N Harris
On 10/31/2011 06:48 AM, Patrick Rapin wrote:
> 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
>
... snip ...
>
> 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
[hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: New Lua C API Tutorial

Daurnimator
On 1 November 2011 11:30, Tom N Harris <[hidden email]> wrote:

> 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.
>

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;