Lucas
Klassmann | Blog

Embedding Lua in C

Extending your tools and apps with Lua scripts

2019-02-02 ยท By Lucas Klassmann

Introduction

Allowing users to extend apps with scripts it is an amazing feature. There are many languages for this purpose, but Lua is the choice today, it is powerful and easy to embed.

Lua is an important tool for many industries, it has been used inside Game Engines, Databases like Redis, and HTTP servers like Nginx, powering users to extend their features.

I will show you some essential but complete examples in how to work with Lua, I will not cover some deep concepts behind the language, installation and other topics like LuaJIT.

Another thing that I will not cover is how to compile and link your app with Lua library, if you know a little about C it is really simple to do looking at the official manual, the goal here is talking about examples and how to use the API. But if you do not know how to do, check my repository link with examples at the end of this article.

Installation

How I said I do not cover the installation of Lua development libraries, because it varies and depends on your platform. You can look at the installation guide in the official website here.

Some Lua Concepts

It is important you understand at least one concept before, the virtual stack. Lua uses a virtual stack to pass values to and from C.

All communication between lua code and your application works using the virtual stack. You push data from C for defining global variables, tables, functions and function arguments. Lua VM will be made all this data available inside Lua script. When a Lua code calls a C function, for example, inside the function you have to recover the arguments and push the result again to Lua.

Another example is when you need to call from C a Lua function. You have to call an API function to let Lua knows which function should be put onto the stack to be available to C call it.

Now you are ready to start coding.

Starting Lua VM

The basic code that we need to call when we use Lua C API is as follow. We start declaring a lua_State pointer and initialize it with luaL_newstate() function.

Note that versions before 5.3 lua_open() is used instead.

It is our pointer to our Lua Virtual Machine and this lua_State store all data that we share between Lua and C. We can have many Lua states to run scripts with different contexts.

lua_State *L = luaL_newstate();

After opening a state, we are ready to call functions from Lua API.

One important function to be called is luaL_openlibs, it makes available the Lua Standard Library for the code that we will run afterwards. It is not a requirement, but without this you will not be able to call functions from libraries like math, io, string, utf8, inside Lua code.

luaL_openlibs(L);

You can also open only the libraries that you know it will be useful or safe to allow scripts to call, see the example bellow, more information here.

// Allows only math and string libraries to be used
luaopen_math(L);
luaopen_string(L);

After using the lua state, when you do not need to execute anything else, you have to close the state with:

lua_close(L);

Examples

I wrote some examples of basic operations like running Lua code from a string, loading a script file, exposing variables and C functions to Lua code, recovering the values from global variables inside the Lua code and calling Lua function in C.

Initializing Lua VM

Here is our first example, it is a starting point for using Lua with C.

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int main(int argc, char ** argv) {

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    // Work with lua API

    lua_close(L);
    return 0;
}

Let's dissect the code structure. We start adding the headers:

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

Start a new Lua state:

lua_State *L = luaL_newstate();

With L state your now able to call Lua C API.

Open the Lua standard libraries, like math, string, utf8, io, etc.:

luaL_openlibs(L);

After using Lua we need to close the state:

lua_close(L);

Running Lua code

This example shows how to load a Lua code from string and run it.

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int main(int argc, char ** argv) {

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    // Our Lua code, it simply prints a Hello, World message
    char * code = "print('Hello, World')";

    // Here we load the string and use lua_pcall for run the code
    if (luaL_loadstring(L, code) == LUA_OK) {
        if (lua_pcall(L, 0, 1, 0) == LUA_OK) {
            // If it was executed successfuly we 
            // remove the code from the stack
            lua_pop(L, lua_gettop(l));
        }
    }

    lua_close(L);
    return 0;
}

The new things here are the API functions: luaL_loadstring, that is in charge of loading the code chunk on the top of the stack, lua_pcall is in charge of running the code in the stack, and if the execution is successful we remove the code from the top(lua_gettop) of the stack.

Exposing a Simple Variable

It is really common to need to expose some variables for Lua code, and it is simple to do this:

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int main(int argc, char ** argv) {

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    lua_pushinteger(L, 42);
    lua_setglobal(L, "answer");

    char * code = "print(answer)";

    if (luaL_loadstring(L, code) == LUA_OK) {
        if (lua_pcall(L, 0, 1, 0) == LUA_OK) {
            lua_pop(L, lua_gettop(L));
        }
    }

    lua_close(L);
    return 0;
}

We use here lua_pushinteger to put an integer on the top of the stack and after it, we use lua_setglobal to get this value and set as a global variable with the name answer. After exposing the variable we can call inside the Lua code.

Note: For more complex types and structures, check Lua manual.

Exposing a Single Function to Lua

This example is a little bit more complex, I added some comments inside the code to explain the more important lines.

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

// Define our function, we have to follow the protocol of lua_CFunction that is 
// typedef int (*lua_CFunction) (lua_State *L);
int multiplication(lua_State *L) {

    // Check first argument is integer and return the value
    int a = luaL_checkinteger(L, 1);

    // Check second argument is integer and return the value
    int b = luaL_checkinteger(L, 2);

    // multiply and store the result inside a type lua_Integer
    lua_Integer c = a * b;

    // push the result to Lua
    lua_pushinteger(L, c);

    // exit code, successful = 1, otherwise error.
    return 1; // Successful
}

int main(int argc, char ** argv) {

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    // Push the pointer to function
    lua_pushcfunction(L, multiplication);

    // Get the value on top of the stack
    // and set as a global, in this case is the function
    lua_setglobal(L, "mul");

    // we can use the function `mul` inside the Lua code
    char * code = "print(mul(7, 8))";

    if (luaL_loadstring(L, code) == LUA_OK) {
        if (lua_pcall(L, 0, 1, 0) == LUA_OK) {
            lua_pop(L, lua_gettop(l));
        }
    }

    lua_close(L);
    return 0;
}

This example is similar to the exposing variables, but here we push a function pointer instead of an integer value.

Exposing Functions to Lua with Namespace

We use a table to create a namespace, we put all functions inside this table.

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

// Here is the same function from the previous example
int multiplication(lua_State *L) {
    int a = luaL_checkinteger(L, 1);
    int b = luaL_checkinteger(L, 2);
    lua_Integer c = a * b;
    lua_pushinteger(L, c);
    return 1;
}

int main(int argc, char ** argv) {

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    // First, we need to define an array with
    // all functions that will be available inside our namespace 
    static const struct luaL_Reg MyMathLib[] = {
        { "mul", multiplication }
    };

    // We create a new table
    lua_newtable(L);

    // Here we set all functions from MyMathLib array into
    // the table on the top of the stack
    luaL_setfuncs(L, MyMathLib, 0);

    // We get the table and set as global variable
    lua_setglobal(L, "MyMath");

    // Now we can call from Lua using the namespace MyMath
    char * code = "print(MyMath.mul(7, 8))";

    if (luaL_loadstring(L, code) == LUA_OK) {
        if (lua_pcall(L, 0, 1, 0) == LUA_OK) {
            lua_pop(L, lua_gettop(L));
        }
    }

    lua_close(L);
    return 0;
}

Running a Lua Script

The only difference between running code from string or file it is that we use luaL_loadfile instead of luaL_loadstring.

-- script.lua
print("Hello, World from File")
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int main(int argc, char ** argv) {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    if (luaL_loadfile(L, "script.lua") == LUA_OK) {
        if (lua_pcall(L, 0, 1, 0) == LUA_OK) {
            lua_pop(L, lua_gettop(L));
        }
    }

    lua_close(L);
    return 0;
}

Getting a Global Variable from Lua

Retrieving values from a Lua script it is a good way of configuring your app.

Note that we can only get the variable after running the Lua code.
-- script2.lua
message = 'This message is stored inside Lua code'
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int main(int argc, char ** argv) {

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    if (luaL_loadfile(L, "script2.lua") == LUA_OK) {
        if (lua_pcall(L, 0, 1, 0) == LUA_OK) {
            lua_pop(L, lua_gettop(L));
        }
    }

    lua_getglobal(L, "message");

    if (lua_isstring(L, -1)) {
        const char * message = lua_tostring(L, -1);
        lua_pop(L, 1);
        printf("Message from lua: %s\n", message);
    }

    lua_close(L);
    return 0;
}

Calling a Lua Function in C

Calling a function defined in a Lua code it is pretty similar of getting a variable, but to execute the function you must check if it is a function on the top of the stack with lua_isfunction and call it with lua_pcall. If it is successfully executed you have to remove from the stack with lua_pop(l, lua_gettop(l)).

-- script3.lua
function my_function()
    print("Hello from Function in Lua")
end
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int main(int argc, char ** argv) {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    if (luaL_loadfile(L, "script3.lua") == LUA_OK) {
        if (lua_pcall(L, 0, 1, 0) == LUA_OK) {
            lua_pop(L, lua_gettop(l));
        }
    }

    lua_getglobal(L, "my_function");
    if (lua_isfunction(L, -1)) {
        if (lua_pcall(L, 0, 1, 0) == LUA_OK) {
            lua_pop(L, lua_gettop(L));
        }
    }

    lua_close(L);
    return 0;
}

Calling a Lua Function in C with Arguments and Return Value

When you call a function defined in Lua, you can pass arguments values and get the return value. To do that you need put onto the stack the list of arguments after putting the function on the stack. After running the function you can check on the top of the stack a get the return value.

-- script4.lua
function my_function(a, b)
    return a * b
end
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int main(int argc, char ** argv) {

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    if (luaL_loadfile(L, "script4.lua") == LUA_OK) {
        if (lua_pcall(L, 0, 1, 0) == LUA_OK) {
            lua_pop(L, lua_gettop(L));
        }
    }

    // Put the function to be called onto the stack
    lua_getglobal(L, "my_function");
    lua_pushinteger(L, 3);  // first argument
    lua_pushinteger(L, 4);  // second argument

    // Execute my_function with 2 arguments and 1 return value
    if (lua_pcall(L, 2, 1, 0) == LUA_OK) {

        // Check if the return is an integer
        if (lua_isinteger(L, -1)) {

            // Convert the return value to integer
            int result = lua_tointeger(L, -1);

            // Pop the return value
            lua_pop(L, 1);
            printf("Result: %d\n", result);
        }
        // Remove the function from the stack
        lua_pop(L, lua_gettop(L));
    }

    lua_close(L);
    return 0;
}

The End

There is much more about this topic, you can learn more about Lua in the official manual, LuaJIT is another important topic, that it is a way to use Lua with a better performance. If you are interested in game development, check Love2D that is an amazing tool. Finally, check my repository with all examples. I hope it will be useful for you.

Thank you!

Extra Resources

Note: English is not my main language, I've been writing for improving my English. If you find something wrong you can open an issue or send me a message on Twitter.