Embedding a scripting language inside your C/C++ code

Posted by Steve on Tue 4 Oct 2005 at 11:07

There are many scripting languages which may be embedded into applications. Some languages have evolved this ability over time, and others have been designed from the ground up as embedded languages. One of the languages specifically designed for embedded use is Lua.

Here we're going to examine using the Lua Scripting Language and embedding it inside a C application.

One of my recent hobbies has been the creation of simple games, reminiscent of those I used to play upon my first computer.

Whilst writing games isn't something that will appeal to all Debian users, or sysadmins, there are a lot of uses to which a program can put an embedded scripting language - so that is the motivation for posting this here.

To start with you'll need to install the libraries. That can easily be done with :

apt-get install liblua50-dev

This will give you a set of development libraries, header files, and other things required to build lua scripted applications.

Once installed you can compile and link your applications using the lua-config helper. This will allow you to generate the compiler flags necessary:

skx@vilya:~$ lua-config --include --libs
-I/usr/include/lua50 -L/usr/include -llualib50 -llua50

Once installed you will want your C, or C++, code to do three things:

Each of these jobs will be demonstrated with a small example.

Initialising Lua

The most simple example we'll look at involves initialising the lua scripting engine, after it has been linked into your application.

The code would look like this:

#include <stdio.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

/* the Lua interpreter */
lua_State* L;

int main ( int argc, char *argv[] )
{
	/* initialize Lua */
	L = lua_open();

	/* load various Lua libraries */
	lua_baselibopen(L);
        luaopen_table(L);
        luaopen_io(L);
        luaopen_string(L);
        luaopen_math(L);
    
	/* cleanup Lua */
	lua_close(L);

	return 0;
}

(You may download this file.)

Once you've saved this as init.c you can compile it and build it like so:

skx@vilya:~$ gcc -o init -Wall `lua-config --include --libs` init.c
skx@vilya:~$ 

Executing the code, predictably, does nothing:

skx@vilya:~$ ./init 
skx@vilya:~$ 

This is because we've written no real code. We merely initialise an interpreter instance and then immediately clean it up. The various luaopen_ functions are worthy of note, they perform a job equivilent to loading some standard libraries. If you don't include them you will be unable to use some of the standard functions - by ommitting them you can "sandbox" your scripts a little.

To do something useful we must move on.

Calling from C into a Lua script.

Loading and calling into Lua scripts from your C code is the whole point of the lua language. So there are several ways you can do this.

The most basic is to load a single source file and execute it in its entirety - much like running system( "perl someScript.pl" );.

The more general purpose way of calling Lua code from C is to push arguments to the function upon the stack and then make a call.

We will show an example of both kinds of code. The first is the simplest.

Imagine we have a lua script, called do-me.lua with the following contents:

print "Start"
for i=1,10 do
   print(i) 
end
print "End"

(This script merely executes a loop - printing the numbers 1 through 10.)

We can load and execute this script by the following code:

#include <stdio.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

/* the Lua interpreter */
lua_State* L;

int main ( int argc, char *argv[] )
{
	/* initialize Lua */
	L = lua_open();

	/* load Lua base libraries */
	lua_baselibopen(L);

	/* run the script */
	lua_dofile(L, "do-me.lua");

	/* cleanup Lua */
	lua_close(L);

	return 0;
}

(This file is available for download.)

Compiling this code is a simple process, similar to the last one:

skx@vilya:~$ gcc -o do-me -Wall `lua-config --include --libs` do-me.c

Once we have the compiled C program we can execute it to see the Lua script do-me.lua executed:

skx@vilya:~$ ./do-me 
Start
1
2
3
4
5
6
7
8
9
10
End

That was a useful enough example. But it isn't great. Instead if you're building your extensible application you'll want to call specific user defined functions - allowing users to override basic operations, etc.

For the next example we'll write a lua function which will calculate the sum of two numbers. Then call it from C.

The process will look like this:

This code, saved in the file add.c, looks like this:

#include <stdio.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"


/* the Lua interpreter */
lua_State* L;

int main ( int argc, char *argv[] )
{
	int sum;
	
	/* initialize Lua */
	L = lua_open();

	/* load Lua base libraries */
	lua_baselibopen(L);

	/* load the script */
	lua_dofile(L, "add.lua");
	
	/* the function name */
	lua_getglobal(L, "add");

	/* the first argument */
	lua_pushnumber(L, 41 );

	/* the second argument */
	lua_pushnumber(L, 22 );

	/* call the function with 2
	   arguments, return 1 result */
	lua_call(L, 2, 1);

	/* get the result */
	sum = (int)lua_tonumber(L, -1);
	lua_pop(L, 1);

	/* print the result */
	printf( "The result is %d\n", sum );

	/* cleanup Lua */
	lua_close(L);

	return 0;
}

(This file is available for download.)

Compiling is as before:

skx@vilya:~$ gcc -o add -Wall `lua-config --include --libs` add.c
skx@vilya:~$ 

With the proper lua script file, add.lua:

function add ( x, y )
	return x + y
end

We can now run the program:

skx@vilya:~$ ./add 
The result is 63
skx@vilya:~$ 
Call from Lua into your C application.

The most interesting aspect of writing scriptable applications is to allow the script to contain your logic, calling into your application for the "hard" work.

In my terms that involves writing a simple piece of C to display the sprites upon the screen - then having the lua application make calls such as "moveLeft", "moveRight", "isDead" etc.

To call from Lua to your C functions you must first register them. Then when they are called you have to do a couple of extra things from your C code:

In this next example we'll write a C function "average" which will average a list of numbers. We will call this from the lua script "average.lua".

The calling script will look like this:

avg, sum = average(10, 20, 30, 40,  50)

print("The average is ", avg)
print("The sum is ", sum)

The corresponding C code looks like this:

#include <stdio.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

/* the Lua interpreter */
lua_State* L;

/* The function we'll call from the lua script */
static int average(lua_State *L)
{
	/* get number of arguments */
	int n = lua_gettop(L);
	double sum = 0;
	int i;

	/* loop through each argument */
	for (i = 1; i <= n; i++)
	{
	        if (!lua_isnumber(L, i)) 
		{
		      lua_pushstring(L, "Incorrect argument to 'average'");
		      lua_error(L);
		}

		/* total the arguments */
		sum += lua_tonumber(L, i);
	}

	/* push the average */
	lua_pushnumber(L, sum / n);

	/* push the sum */
	lua_pushnumber(L, sum);

	/* return the number of results */
	return 2;
}


int main ( int argc, char *argv[] )
{
	/* initialize Lua */
	L = lua_open();

	/* load Lua base libraries */
	lua_baselibopen(L);

	/* register our function */
	lua_register(L, "average", average);

	/* run the script */
	lua_dofile(L, "average.lua");

	/* cleanup Lua */
	lua_close(L);

	return 0;
}

(You may download these files too.)

Compiling this and running it shows that we've:

Compilation and execution look like this:

skx@vilya:~$ gcc -o average -Wall `lua-config --include --libs` average.c
skx@vilya:~$ ./average 
The average is  30
The sum is      150
Other Tricks

It is possible that you will want to setup some global variables from your C code which you wish the script to be able to use. A good example of this is a version number.

You can do that with the following code:

#define VERSION "0.7"

lua_pushstring(L, "VERSION");
lua_pushstring(L, VERSION);
lua_settable(L, LUA_GLOBALSINDEX);

This allows your Lua code to do something like this:

print( "Version " .. VERSION );

If you've followed along you should be capable of embedding simple functions into external script. Or writing main loops entirely in Lua.

The online Programming Lua book contains a wealth of information about using the language, including:

This section in particular, (and the following ones building upon it), show in detail how you can manage calling from C to Lua, and back again.


This article can be found online at the Debian Administration website at the following bookmarkable URL (along with associated comments):

This article is copyright 2005 Steve - please ask for permission to republish or translate.