Add scripting to your tool with Lua

It’s been a while since I last wrote a programming article! I’m working on a big visual fx tool for my company, and I recently started to think that it would be nice to be able to script it. For example, create a small script to convert all texture paths of the effect from absolute to relative, etc.

And I found out that it’s really simple. I decided to use Lua which I (re)discovered through the wonderfull Premake project. So first thing first, go to Lua’s website and download it. Then just create a simple console application, and add all the .c files found in lua/src to your project, except lua.c and luac.c !

For my purpose, I just need to be able to execute some Lua, and access my tool’s functionalities from it. The first step is really simple:

// init lua
lua_State* lua = luaL_newstate();
// load the default libs (io, etc.)
luaL_openlibs(lua);

// then you can load and execute an existing script:
luaL_dofile(lua, "test.lua");
// or directly execute something:
luaL_dostring(lua, "io.write(\"Hello World!\")");

// and close
lua_close(lua);

Yes, it’s that simple.

Then the second part is allowing a Lua script to call a C function. This is also pretty simple:

// our function. All functions that will be called from Lua
// need to have the same signature.
int test(lua_State* lua)
{
    // since lua allow calling a function with variable arguments,
    // get the number of arguments it was called with.
    int num_args = lua_gettop(lua);

    // do some tests (we could also just print a message and return)
    // note: arguments are retrieved from the lua state object. Since the
    // very first value is the number of arguments, the arguments start
    // at index 1.
    assert(num_args >= 1 && num_args <= 2);
    assert(lua_isnumber(lua, 1));

    // get the first parameter
    int a = lua_tointeger(lua, 1);

    // check if we need to print
    if(num_args == 2 && lua_isboolean(lua, 2) && lua_toboolean(lua, 2))
    {
        std::cout << "result: " << a * a << std::endl;
    }

    // push as many return values that you want, and return the number of
    // values that you pushed. In our case it's only 1, but it could be
    // really powerful: need to return a list of selected items ? Just
    // push all of them and return the number of items.
    lua_pushnumber(lua, a * a);
    return 1;
}

// somewhere else, start Lua
lua_State* lua = luaL_newstate();
luaL_openlibs(lua);

// push our function on top of the lua state object
lua_pushcfunction(lua, testFunction);
// then make this function globally accessible through the name "test"
lua_setglobal(lua, "test");

// call our function. That's all :)
luaL_dostring(lua, "test(10, true)");
luaL_dostring(lua, "io.write(test(10))");

// close lua
lua_close(lua);

Now if you want to go further, here are a few links:

  • Reference: The Lua 5.2 reference manual.
  • LuaJIT: From what I understood, this is a replacement of the default Lua compiler/VM that is a lot faster and more efficient. I didn’t have time to investigate but it seems really good. Only drawback is that it’s Lua 5.1 only.

Leave a Reply

Your email address will not be published. Required fields are marked *