Lua and C++

By | 2013-09-05

I’ve not yet had a project that’s needed an embedded scripting language. When I do, I think I’ll reach for Lua.

$ apt-get install lua5.2 liblua5.2-dev

Command line lua:

$ lua
Lua 5.2.2  Copyright (C) 1994-2013 Lua.org, PUC-Rio
> print("Hello, World") 
Hello, World

Embedded lua:

#include <lua5.2/lua.hpp>

int main()
{
    // Create a Lua virtual machine
    lua_State *l = luaL_newstate();

    // Make all the lua libs to the virtual machine
    luaL_openlibs(l);

    // Test it
    luaL_dostring(l, "print(\"Helua, World!\")");
    cout << lua_tostring(l, -1);

    lua_close(l);
}

I’m not sure it gets much easier.

Calling C from Lua

Obviously the main reason for embedding a scripting language is to make custom routines from your application available to the script.

int x = 0;

int customFunction(lua_State *L) {
    // Number of elements on the parameter stack
    int n = lua_gettop(L);
    if( n == 0 )
        return 0;

    for(int i = 0; i < n; i++) {
        if( !lua_isnumber(L, i+1) ) {
            lua_pushstring(L, "incorrect arguments");
            lua_error(L);
        }
        x += lua_tointeger(L, i+1);
    }

    // return x
    lua_pushinteger(L, x);
    return 1;
}

Then register it and call it.

int main()
{
    // Create a Lua virtual machine
    lua_State *l = luaL_newstate();

    lua_register(l, "customFunction", customFunction);

    // Test it
    luaL_dostring(l, "return customFunction(1)");
    std::cout << lua_tointeger(l, -1) << endl;

    lua_close(l);
}

It took me quite a while to realise that “return” was needed inside the luaL_dostring() parameter. It seems that the string being evaluated is treated as being inside some anonymous function and we are effectively calling that entire function. Now I understand that, the following from the Lua reference manual is clearer:

int luaL_dofile (lua_State *L, const char *filename);

Loads and runs the given file. It is defined as the following macro:

 (luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0))

It returns false if there are no errors or true in case of errors.

luaL_loadfile() uses lua_load() which says this:

Loads a Lua chunk (without running it). If there are no errors, lua_load pushes the compiled chunk as a Lua function on top of the stack.

So the string we give to luaL_dostring() becomes a function on the stack, which is what lua_pcall() calls … calls means it must return. That’s in addition to our customFunction() which itself returns to this anonymous function.

Calling Lua from C

The reverse will also be useful; when you want to let your script react to events generated by the C/C++ application.

int main()
{
    // Create a Lua virtual machine
    lua_State *l = luaL_newstate();

    // Define the lua callback in the state; note that this doesn't
    // call the function, it creates the reference to it in the
    // global table
    luaL_dostring(L, R"***(
        function onEvent(a)
            return a+10
        end
    )***"));

    // Random number
    int x = 0x7673f562;

    // What function do we want to call?  Find it and push it onto
    // the stack
    lua_getglobal(L, "onEvent");
    // Then push the arguments
    lua_pushnumber(L, x);
    // Call it, telling Lua it has one argument, one result
    lua_call(L, 1, 1);
    // Fetch the result
    int y = lua_tointeger(L, -1);
    // Restore the stack
    lua_pop(L, 1);

    cout << "LUA.onEvent()" = y << endl;
}

Leave a Reply