Wednesday, 12 January 2011

Lua and C++

I'm not sure this is as complicated as I thought it was last night; I think I was too tired to think straight. Still, it does no harm to make a few notes on the subject.

So let's write a simple Lua script:

print "hello"

Put that in a file called "hello.lua" and run it with "lua hello.lua". It should print "hello" to the console.

So let's have a C++ program do that. First we need to include the Lua headers.

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


That supplies definitions for the Lua functions and types. The 'extern "C"' bit tells the compiler that the Lua libraries are in C, not C++.

Then some C++ includes so we can use strings and stream I/O:

#include <string>
#include <iostream>


All standard stuff. A main function, with our lua program in a string:

int main()
{
        string prog = "print \"hello\"";
}

Had to escape the quotes, otherwise unremarkable.

Next - start a Lua state. I had trouble getting my head around this bit for some reason.

        lua_State *L = lua_open();

Clearly, it's starting a Lua session - so why "state"? Well, if you think about it, any running program has two parts to it. There's the code, which is the binary stuff in the .exe file, and then there's the program state, which is all the variables that tell the program what line number it's up to and so forth. So the Lua guys cleverly kept the state separate the code, which isn't always as easy as it sounds. And since we already have the code part included in the lua libraries, all we need is to keep track of the state, and we can do anything the lua command interpreter can do.

Hence lua_State. That *L pointer is effectively the lua interpreter program, embedded in C++ and ready for  the next line of input.

The state also keeps track of any variables and functions you define. This means that if you run "x = 54321" in one call, and "print x" in another, you get the value 54321 printed to the console. And if you create another lua_State with another lua_open() call, you get a whole new dataspace with completely unrelated variables,

So, you can see why I had trouble getting my head around that. There's a lot more to that one line than initially meets the eye.

Next, load and run the string. The simplest way to do that is like this:


        int rc = luaL_dostring(L, prog.c_str());

luaL_dostring is a convenience method which loads and runs the string all in one. The documentation mildly discourages you from using it, and we'll see why in due course. But it's by far the fastest way to get this program running.

Just for the sake of form, we'll close down the lua_State before we exit. If probably doesn't matter in this case, but if we were creating and destroying lots of lua states, this would stop us leaking memory

        lua_close(L);

Put it all together:

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

using namespace std;

#include <string>
#include <iostream>

int main()
{
        string prog = "print \"hello\"";

        lua_State *L = lua_open();

        int rc = luaL_dostring(L, prog.c_str());

        lua_close(L);
        return 0;
}


And it does nothing. What went wrong? We can print out the value of prog to make sure that's the same as in our lua script

        cout    << "prog = [["
                << prog
                << "]]"
                << endl
        ;


And that's correct. How about checking the return code from luaL_dostring? The manual tells us that a return of 1 indicates an error:

        int rc = luaL_dostring(L, prog.c_str());
        if(rc == 1) {
                cout << "there was an error executing the script" << endl;
        }

And apparently we have an error. We need to find out what it was. Digging through the documentation a bit we find that if there is an error (either loading or executing) then the error message is pushed onto the stack.

Actually, talking about the Lua stack is probably worth a post on its own...

[edit]

Kudos.

No comments: